tushare_api/
logging.rs

1use log::{info, debug, error, trace, warn};
2
3#[cfg(feature = "tracing")]
4use tracing::{info as tracing_info, debug as tracing_debug, error as tracing_error, trace as tracing_trace, warn as tracing_warn};
5
6/// Log level configuration
7#[derive(Debug, Clone, PartialEq)]
8pub enum LogLevel {
9    /// Disable logging
10    Off,
11    /// Log errors only
12    Error,
13    /// Log errors and warnings
14    Warn,
15    /// Log basic information (default)
16    Info,
17    /// Log detailed debug information
18    Debug,
19    /// Log all information including raw data
20    Trace,
21}
22
23impl Default for LogLevel {
24    fn default() -> Self {
25        LogLevel::Info
26    }
27}
28
29/// Log configuration
30#[derive(Debug, Clone)]
31pub struct LogConfig {
32    /// Log level
33    pub level: LogLevel,
34    /// Whether to log request parameters
35    pub log_requests: bool,
36    /// Whether to log response content
37    pub log_responses: bool,
38    /// Whether to log sensitive data (such as token)
39    pub log_sensitive_data: bool,
40    /// Whether to log performance metrics
41    pub log_performance: bool,
42}
43
44impl Default for LogConfig {
45    fn default() -> Self {
46        Self {
47            level: LogLevel::Info,
48            log_requests: true,
49            log_responses: false, // Responses can be large, not logged by default
50            log_sensitive_data: false, // Sensitive data not logged by default
51            log_performance: true,
52        }
53    }
54}
55
56/// Logger
57#[derive(Debug)]
58pub struct Logger {
59    config: LogConfig,
60}
61
62impl Logger {
63    /// Create a new logger
64    pub fn new(config: LogConfig) -> Self {
65        Self { config }
66    }
67
68    /// Check if logging should be performed for the specified level
69    pub fn should_log(&self, level: &LogLevel) -> bool {
70        use LogLevel::*;
71        match (&self.config.level, level) {
72            (Off, _) => false,
73            (Error, Error) => true,
74            (Warn, Error | Warn) => true,
75            (Info, Error | Warn | Info) => true,
76            (Debug, Error | Warn | Info | Debug) => true,
77            (Trace, _) => true,
78            _ => false,
79        }
80    }
81
82    /// Safely log messages with lazy evaluation (decides whether to record sensitive information based on configuration)
83    pub fn log_safe<F>(&self, level: LogLevel, message_fn: F, sensitive_data: Option<&str>)
84    where
85        F: FnOnce() -> String,
86    {
87        if !self.should_log(&level) {
88            return;
89        }
90
91        let message = message_fn();
92        let full_message = if self.config.log_sensitive_data {
93            if let Some(sensitive) = sensitive_data {
94                format!("{} [Sensitive data: {}]", message, sensitive)
95            } else {
96                message
97            }
98        } else {
99            message
100        };
101
102        // Choose logging backend based on compile features
103        #[cfg(feature = "tracing")]
104        match level {
105            LogLevel::Error => tracing_error!("{}", full_message),
106            LogLevel::Warn => tracing_warn!("{}", full_message),
107            LogLevel::Info => tracing_info!("{}", full_message),
108            LogLevel::Debug => tracing_debug!("{}", full_message),
109            LogLevel::Trace => tracing_trace!("{}", full_message),
110            LogLevel::Off => {}
111        }
112        
113        #[cfg(not(feature = "tracing"))]
114        match level {
115            LogLevel::Error => error!("{}", full_message),
116            LogLevel::Warn => warn!("{}", full_message),
117            LogLevel::Info => info!("{}", full_message),
118            LogLevel::Debug => debug!("{}", full_message),
119            LogLevel::Trace => trace!("{}", full_message),
120            LogLevel::Off => {}
121        }
122    }
123
124    /// Log API call start
125    pub fn log_api_start(&self, request_id: &str, api_name: &str, params_count: usize, fields_count: usize) {
126        let request_id = request_id.to_string();
127        let api_name = api_name.to_string();
128        self.log_safe(
129            LogLevel::Info,
130            move || format!(
131                "[{}] Starting Tushare API call: {}, params count: {}, fields count: {}",
132                request_id, api_name, params_count, fields_count
133            ),
134            None,
135        );
136    }
137
138    /// Log request details
139    pub fn log_request_details(&self, request_id: &str, api_name: &str, params: &str, fields: &str, token_preview: Option<&str>) {
140        if !self.config.log_requests {
141            return;
142        }
143
144        let request_id = request_id.to_string();
145        let api_name = api_name.to_string();
146        let params = params.to_string();
147        let fields = fields.to_string();
148        self.log_safe(
149            LogLevel::Debug,
150            move || format!(
151                "[{}] API request details - API: {}, params: {}, fields: {}",
152                request_id, api_name, params, fields
153            ),
154            token_preview,
155        );
156    }
157
158    /// Log HTTP request sending
159    pub fn log_http_request(&self, request_id: &str) {
160        let request_id = request_id.to_string();
161        self.log_safe(
162            LogLevel::Debug,
163            move || format!("[{}] Sending HTTP request to Tushare API", request_id),
164            None,
165        );
166    }
167
168    /// Log HTTP request failure
169    pub fn log_http_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str) {
170        let request_id = request_id.to_string();
171        let error = error.to_string();
172        self.log_safe(
173            LogLevel::Error,
174            move || format!(
175                "[{}] HTTP request failed, duration: {:?}, error: {}",
176                request_id, elapsed, error
177            ),
178            None,
179        );
180    }
181
182    /// Log HTTP response reception
183    pub fn log_http_response(&self, request_id: &str, status_code: u16) {
184        let request_id = request_id.to_string();
185        self.log_safe(
186            LogLevel::Debug,
187            move || format!("[{}] Received HTTP response, status code: {}", request_id, status_code),
188            None,
189        );
190    }
191
192    /// Log response reading failure
193    pub fn log_response_read_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str) {
194        let request_id = request_id.to_string();
195        let error = error.to_string();
196        self.log_safe(
197            LogLevel::Error,
198            move || format!(
199                "[{}] Failed to read response content, duration: {:?}, error: {}",
200                request_id, elapsed, error
201            ),
202            None,
203        );
204    }
205
206    /// Log raw response content
207    pub fn log_raw_response(&self, request_id: &str, response_text: &str) {
208        if !self.config.log_responses {
209            return;
210        }
211
212        let request_id = request_id.to_string();
213        let response_text = response_text.to_string();
214        self.log_safe(
215            LogLevel::Trace,
216            move || format!("[{}] Raw response content: {}", request_id, response_text),
217            None,
218        );
219    }
220
221    /// Log JSON parsing failure
222    pub fn log_json_parse_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str, response_text: &str) {
223        let request_id = request_id.to_string();
224        let error = error.to_string();
225        let response_preview = if self.config.log_responses {
226            response_text.to_string()
227        } else {
228            "[Hidden]".to_string()
229        };
230
231        self.log_safe(
232            LogLevel::Error,
233            move || format!(
234                "[{}] JSON parsing failed, duration: {:?}, error: {}, response content: {}",
235                request_id, elapsed, error, response_preview
236            ),
237            None,
238        );
239    }
240
241    /// Log API error
242    pub fn log_api_error(&self, request_id: &str, elapsed: std::time::Duration, code: i32, message: &str) {
243        let request_id = request_id.to_string();
244        let message = message.to_string();
245        self.log_safe(
246            LogLevel::Error,
247            move || format!(
248                "[{}] API returned error, duration: {:?}, error code: {}, error message: {}",
249                request_id, elapsed, code, message
250            ),
251            None,
252        );
253    }
254
255    /// Log API call success
256    pub fn log_api_success(&self, request_id: &str, elapsed: std::time::Duration, data_count: usize) {
257        let request_id = request_id.to_string();
258        if self.config.log_performance {
259            self.log_safe(
260                LogLevel::Info,
261                move || format!(
262                    "[{}] API call successful, duration: {:?}, data rows returned: {}",
263                    request_id, elapsed, data_count
264                ),
265                None,
266            );
267        } else {
268            self.log_safe(
269                LogLevel::Info,
270                move || format!("[{}] API call successful", request_id),
271                None,
272            );
273        }
274    }
275
276    /// Log response details
277    pub fn log_response_details(&self, request_id: &str, response_request_id: &str, fields: &str) {
278        if !self.config.log_responses {
279            return;
280        }
281
282        let request_id = request_id.to_string();
283        let response_request_id = response_request_id.to_string();
284        let fields = fields.to_string();
285        self.log_safe(
286            LogLevel::Debug,
287            move || format!(
288                "[{}] Response details - Request ID: {}, fields: {}",
289                request_id, response_request_id, fields
290            ),
291            None,
292        );
293    }
294
295    /// Get reference to log configuration
296    pub fn config(&self) -> &LogConfig {
297        &self.config
298    }
299}