Skip to main content

hinge_rs/
logging.rs

1/// Library crates should not install a process-global logger.
2///
3/// Applications can install `tracing_subscriber`, `env_logger`, or any other
4/// logger before constructing the client.
5pub fn init_logger() {}
6
7/// Format HTTP headers for logging (hiding sensitive data)
8pub fn format_headers(headers: &reqwest::header::HeaderMap) -> String {
9    let mut output = String::new();
10    for (name, value) in headers.iter() {
11        let value_str = if name == "authorization" {
12            "Bearer ***REDACTED***".to_string()
13        } else if name == "x-session-id" || name == "x-device-id" || name == "x-install-id" {
14            format!(
15                "***{}",
16                &value
17                    .to_str()
18                    .unwrap_or("")
19                    .chars()
20                    .rev()
21                    .take(4)
22                    .collect::<String>()
23                    .chars()
24                    .rev()
25                    .collect::<String>()
26            )
27        } else {
28            value.to_str().unwrap_or("").to_string()
29        };
30        output.push_str(&format!("  {}: {}\n", name.as_str(), value_str));
31    }
32    output
33}
34
35/// Format WebSocket headers for logging (hiding sensitive data)
36pub fn format_ws_headers(headers: &[(impl AsRef<str>, impl AsRef<str>)]) -> String {
37    let mut output = String::new();
38    for (name, value) in headers.iter() {
39        let n = name.as_ref();
40        let v = value.as_ref();
41        let value_str = if n.eq_ignore_ascii_case("SENDBIRD-WS-AUTH")
42            || n.eq_ignore_ascii_case("SENDBIRD-WS-TOKEN")
43        {
44            "***REDACTED***".to_string()
45        } else if n.eq_ignore_ascii_case("Cookie") {
46            "***".to_string()
47        } else {
48            v.to_string()
49        };
50        output.push_str(&format!("  {}: {}\n", n, value_str));
51    }
52    output
53}
54
55/// Format JSON for pretty logging
56pub fn format_json(json: &serde_json::Value) -> String {
57    serde_json::to_string_pretty(json).unwrap_or_else(|_| "Invalid JSON".to_string())
58}
59
60/// Log HTTP request details
61pub fn log_request(
62    method: &str,
63    url: &str,
64    headers: &reqwest::header::HeaderMap,
65    body: Option<&serde_json::Value>,
66) {
67    log::info!("━━━━━━━━━━ HTTP REQUEST ━━━━━━━━━━");
68    log::info!("{} {}", method, url);
69    log::debug!("Headers:");
70    log::debug!("{}", format_headers(headers));
71
72    if let Some(body) = body {
73        log::debug!("Body:");
74        log::debug!("{}", format_json(body));
75    }
76    log::info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
77}
78
79/// Log HTTP response details
80pub fn log_response(
81    status: reqwest::StatusCode,
82    headers: &reqwest::header::HeaderMap,
83    body: Option<&serde_json::Value>,
84) {
85    log::info!("━━━━━━━━━━ HTTP RESPONSE ━━━━━━━━━━");
86    log::info!("Status: {}", status);
87    log::debug!("Headers:");
88    // Log each header on its own line so the logger prefix is consistent
89    for (name, value) in headers.iter() {
90        let printable = value.to_str().unwrap_or("");
91        let value_str = if name == "authorization" {
92            "Bearer ***REDACTED***".to_string()
93        } else if name == "x-session-id" || name == "x-device-id" || name == "x-install-id" {
94            format!(
95                "***{}",
96                printable
97                    .chars()
98                    .rev()
99                    .take(4)
100                    .collect::<String>()
101                    .chars()
102                    .rev()
103                    .collect::<String>()
104            )
105        } else {
106            printable.to_string()
107        };
108        log::debug!("  {}: {}", name.as_str(), value_str);
109    }
110
111    if let Some(body) = body {
112        log::debug!("Body:");
113        let formatted = format_json(body);
114        // Print full body (no truncation) for clarity during debugging
115        log::debug!("{}", formatted);
116    }
117    log::info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
118}