1use log::{debug, error, info, trace, warn};
2
3#[cfg(feature = "tracing")]
4use tracing::{
5 debug as tracing_debug, error as tracing_error, info as tracing_info, trace as tracing_trace,
6 warn as tracing_warn,
7};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum LogLevel {
12 Off,
14 Error,
16 Warn,
18 Info,
20 Debug,
22 Trace,
24}
25
26impl Default for LogLevel {
27 fn default() -> Self {
28 LogLevel::Info
29 }
30}
31
32#[derive(Debug, Clone)]
34pub struct LogConfig {
35 pub level: LogLevel,
37 pub log_requests: bool,
39 pub log_responses: bool,
41 pub log_responses_err: bool,
42 pub log_sensitive_data: bool,
44 pub log_performance: bool,
46}
47
48impl Default for LogConfig {
49 fn default() -> Self {
50 Self {
51 level: LogLevel::Info,
52 log_requests: true,
53 log_responses: false, log_responses_err: true,
55 log_sensitive_data: false, log_performance: true,
57 }
58 }
59}
60
61#[derive(Debug)]
63pub struct Logger {
64 config: LogConfig,
65}
66
67impl Logger {
68 pub fn new(config: LogConfig) -> Self {
70 Self { config }
71 }
72
73 pub fn should_log(&self, level: &LogLevel) -> bool {
75 use LogLevel::*;
76 match (&self.config.level, level) {
77 (Off, _) => false,
78 (Error, Error) => true,
79 (Warn, Error | Warn) => true,
80 (Info, Error | Warn | Info) => true,
81 (Debug, Error | Warn | Info | Debug) => true,
82 (Trace, _) => true,
83 _ => false,
84 }
85 }
86
87 pub fn log_safe<F>(&self, level: LogLevel, message_fn: F, sensitive_data: Option<&str>)
89 where
90 F: FnOnce() -> String,
91 {
92 if !self.should_log(&level) {
93 return;
94 }
95
96 let message = message_fn();
97 let full_message = if self.config.log_sensitive_data {
98 if let Some(sensitive) = sensitive_data {
99 format!("{} [Sensitive data: {}]", message, sensitive)
100 } else {
101 message
102 }
103 } else {
104 message
105 };
106
107 #[cfg(feature = "tracing")]
109 match level {
110 LogLevel::Error => tracing_error!("{}", full_message),
111 LogLevel::Warn => tracing_warn!("{}", full_message),
112 LogLevel::Info => tracing_info!("{}", full_message),
113 LogLevel::Debug => tracing_debug!("{}", full_message),
114 LogLevel::Trace => tracing_trace!("{}", full_message),
115 LogLevel::Off => {}
116 }
117
118 #[cfg(not(feature = "tracing"))]
119 match level {
120 LogLevel::Error => error!("{}", full_message),
121 LogLevel::Warn => warn!("{}", full_message),
122 LogLevel::Info => info!("{}", full_message),
123 LogLevel::Debug => debug!("{}", full_message),
124 LogLevel::Trace => trace!("{}", full_message),
125 LogLevel::Off => {}
126 }
127 }
128
129 pub fn log_api_start(
131 &self,
132 request_id: &str,
133 api_name: &str,
134 params_count: usize,
135 fields_count: usize,
136 ) {
137 let request_id = request_id.to_string();
138 let api_name = api_name.to_string();
139 self.log_safe(
140 LogLevel::Info,
141 move || {
142 format!(
143 "[{}] Starting Tushare API call: {}, params count: {}, fields count: {}",
144 request_id, api_name, params_count, fields_count
145 )
146 },
147 None,
148 );
149 }
150
151 pub fn log_request_details(
153 &self,
154 request_id: &str,
155 api_name: &str,
156 params: &str,
157 fields: &str,
158 token_preview: Option<&str>,
159 ) {
160 if !self.config.log_requests {
161 return;
162 }
163
164 let request_id = request_id.to_string();
165 let api_name = api_name.to_string();
166 let params = params.to_string();
167 let fields = fields.to_string();
168 self.log_safe(
169 LogLevel::Debug,
170 move || {
171 format!(
172 "[{}] API request details - API: {}, params: {}, fields: {}",
173 request_id, api_name, params, fields
174 )
175 },
176 token_preview,
177 );
178 }
179
180 pub fn log_http_request(&self, request_id: &str) {
182 let request_id = request_id.to_string();
183 self.log_safe(
184 LogLevel::Debug,
185 move || format!("[{}] Sending HTTP request to Tushare API", request_id),
186 None,
187 );
188 }
189
190 pub fn log_http_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str) {
192 let request_id = request_id.to_string();
193 let error = error.to_string();
194 self.log_safe(
195 LogLevel::Error,
196 move || {
197 format!(
198 "[{}] HTTP request failed, duration: {:?}, error: {}",
199 request_id, elapsed, error
200 )
201 },
202 None,
203 );
204 }
205
206 pub fn log_http_response(&self, request_id: &str, status_code: u16) {
208 let request_id = request_id.to_string();
209 self.log_safe(
210 LogLevel::Debug,
211 move || {
212 format!(
213 "[{}] Received HTTP response, status code: {}",
214 request_id, status_code
215 )
216 },
217 None,
218 );
219 }
220
221 pub fn log_response_read_error(
223 &self,
224 request_id: &str,
225 elapsed: std::time::Duration,
226 error: &str,
227 ) {
228 let request_id = request_id.to_string();
229 let error = error.to_string();
230 self.log_safe(
231 LogLevel::Error,
232 move || {
233 format!(
234 "[{}] Failed to read response content, duration: {:?}, error: {}",
235 request_id, elapsed, error
236 )
237 },
238 None,
239 );
240 }
241
242 pub fn log_raw_response(&self, request_id: &str, response_text: &str) {
244 if !self.config.log_responses {
245 return;
246 }
247
248 let request_id = request_id.to_string();
249 let response_text = response_text.to_string();
250 self.log_safe(
251 LogLevel::Trace,
252 move || {
253 format!(
254 "[{}] Raw response content length = {}, body = {}",
255 request_id,
256 response_text.len(),
257 response_text
258 )
259 },
260 None,
261 );
262 }
263
264 pub fn log_json_parse_error(
266 &self,
267 request_id: &str,
268 elapsed: std::time::Duration,
269 error: &str,
270 response_text: &str,
271 ) {
272 let request_id = request_id.to_string();
273 let error = error.to_string();
274 let response_preview = if self.config.log_responses_err {
275 response_text.to_string()
276 } else {
277 "[Hidden]".to_string()
278 };
279
280 self.log_safe(
281 LogLevel::Error,
282 move || format!(
283 "[{}] JSON parsing failed, duration: {:?}, error: {}, response content length: {}, response content: {}",
284 request_id, elapsed, error, response_preview.len(), response_preview
285 ),
286 None,
287 );
288 }
289
290 pub fn log_api_error(
292 &self,
293 request_id: &str,
294 elapsed: std::time::Duration,
295 code: i32,
296 message: &str,
297 ) {
298 let request_id = request_id.to_string();
299 let message = message.to_string();
300 self.log_safe(
301 LogLevel::Error,
302 move || {
303 format!(
304 "[{}] API returned error, duration: {:?}, error code: {}, error message: {}",
305 request_id, elapsed, code, message
306 )
307 },
308 None,
309 );
310 }
311
312 pub fn log_api_success(
314 &self,
315 request_id: &str,
316 elapsed: std::time::Duration,
317 data_count: usize,
318 ) {
319 let request_id = request_id.to_string();
320 if self.config.log_performance {
321 self.log_safe(
322 LogLevel::Info,
323 move || {
324 format!(
325 "[{}] API call successful, duration: {:?}, data rows returned: {}",
326 request_id, elapsed, data_count
327 )
328 },
329 None,
330 );
331 } else {
332 self.log_safe(
333 LogLevel::Info,
334 move || format!("[{}] API call successful", request_id),
335 None,
336 );
337 }
338 }
339
340 pub fn log_response_details(&self, request_id: &str, response_request_id: &str, fields: &str) {
342 if !self.config.log_responses {
343 return;
344 }
345
346 let request_id = request_id.to_string();
347 let response_request_id = response_request_id.to_string();
348 let fields = fields.to_string();
349 self.log_safe(
350 LogLevel::Debug,
351 move || {
352 format!(
353 "[{}] Response details - Request ID: {}, fields: {}",
354 request_id, response_request_id, fields
355 )
356 },
357 None,
358 );
359 }
360
361 pub fn config(&self) -> &LogConfig {
363 &self.config
364 }
365}