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#[derive(Debug, Clone, PartialEq)]
8pub enum LogLevel {
9 Off,
11 Error,
13 Warn,
15 Info,
17 Debug,
19 Trace,
21}
22
23impl Default for LogLevel {
24 fn default() -> Self {
25 LogLevel::Info
26 }
27}
28
29#[derive(Debug, Clone)]
31pub struct LogConfig {
32 pub level: LogLevel,
34 pub log_requests: bool,
36 pub log_responses: bool,
38 pub log_sensitive_data: bool,
40 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, log_sensitive_data: false, log_performance: true,
52 }
53 }
54}
55
56#[derive(Debug)]
58pub struct Logger {
59 config: LogConfig,
60}
61
62impl Logger {
63 pub fn new(config: LogConfig) -> Self {
65 Self { config }
66 }
67
68 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 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 #[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 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 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 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 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 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 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 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 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 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 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 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 pub fn config(&self) -> &LogConfig {
297 &self.config
298 }
299}