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_responses_err: bool,
39 pub log_sensitive_data: bool,
41 pub log_performance: bool,
43}
44
45impl Default for LogConfig {
46 fn default() -> Self {
47 Self {
48 level: LogLevel::Info,
49 log_requests: true,
50 log_responses: false, log_responses_err: true,
52 log_sensitive_data: false, log_performance: true,
54 }
55 }
56}
57
58#[derive(Debug)]
60pub struct Logger {
61 config: LogConfig,
62}
63
64impl Logger {
65 pub fn new(config: LogConfig) -> Self {
67 Self { config }
68 }
69
70 pub fn should_log(&self, level: &LogLevel) -> bool {
72 use LogLevel::*;
73 match (&self.config.level, level) {
74 (Off, _) => false,
75 (Error, Error) => true,
76 (Warn, Error | Warn) => true,
77 (Info, Error | Warn | Info) => true,
78 (Debug, Error | Warn | Info | Debug) => true,
79 (Trace, _) => true,
80 _ => false,
81 }
82 }
83
84 pub fn log_safe<F>(&self, level: LogLevel, message_fn: F, sensitive_data: Option<&str>)
86 where
87 F: FnOnce() -> String,
88 {
89 if !self.should_log(&level) {
90 return;
91 }
92
93 let message = message_fn();
94 let full_message = if self.config.log_sensitive_data {
95 if let Some(sensitive) = sensitive_data {
96 format!("{} [Sensitive data: {}]", message, sensitive)
97 } else {
98 message
99 }
100 } else {
101 message
102 };
103
104 #[cfg(feature = "tracing")]
106 match level {
107 LogLevel::Error => tracing_error!("{}", full_message),
108 LogLevel::Warn => tracing_warn!("{}", full_message),
109 LogLevel::Info => tracing_info!("{}", full_message),
110 LogLevel::Debug => tracing_debug!("{}", full_message),
111 LogLevel::Trace => tracing_trace!("{}", full_message),
112 LogLevel::Off => {}
113 }
114
115 #[cfg(not(feature = "tracing"))]
116 match level {
117 LogLevel::Error => error!("{}", full_message),
118 LogLevel::Warn => warn!("{}", full_message),
119 LogLevel::Info => info!("{}", full_message),
120 LogLevel::Debug => debug!("{}", full_message),
121 LogLevel::Trace => trace!("{}", full_message),
122 LogLevel::Off => {}
123 }
124 }
125
126 pub fn log_api_start(&self, request_id: &str, api_name: &str, params_count: usize, fields_count: usize) {
128 let request_id = request_id.to_string();
129 let api_name = api_name.to_string();
130 self.log_safe(
131 LogLevel::Info,
132 move || format!(
133 "[{}] Starting Tushare API call: {}, params count: {}, fields count: {}",
134 request_id, api_name, params_count, fields_count
135 ),
136 None,
137 );
138 }
139
140 pub fn log_request_details(&self, request_id: &str, api_name: &str, params: &str, fields: &str, token_preview: Option<&str>) {
142 if !self.config.log_requests {
143 return;
144 }
145
146 let request_id = request_id.to_string();
147 let api_name = api_name.to_string();
148 let params = params.to_string();
149 let fields = fields.to_string();
150 self.log_safe(
151 LogLevel::Debug,
152 move || format!(
153 "[{}] API request details - API: {}, params: {}, fields: {}",
154 request_id, api_name, params, fields
155 ),
156 token_preview,
157 );
158 }
159
160 pub fn log_http_request(&self, request_id: &str) {
162 let request_id = request_id.to_string();
163 self.log_safe(
164 LogLevel::Debug,
165 move || format!("[{}] Sending HTTP request to Tushare API", request_id),
166 None,
167 );
168 }
169
170 pub fn log_http_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str) {
172 let request_id = request_id.to_string();
173 let error = error.to_string();
174 self.log_safe(
175 LogLevel::Error,
176 move || format!(
177 "[{}] HTTP request failed, duration: {:?}, error: {}",
178 request_id, elapsed, error
179 ),
180 None,
181 );
182 }
183
184 pub fn log_http_response(&self, request_id: &str, status_code: u16) {
186 let request_id = request_id.to_string();
187 self.log_safe(
188 LogLevel::Debug,
189 move || format!("[{}] Received HTTP response, status code: {}", request_id, status_code),
190 None,
191 );
192 }
193
194 pub fn log_response_read_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str) {
196 let request_id = request_id.to_string();
197 let error = error.to_string();
198 self.log_safe(
199 LogLevel::Error,
200 move || format!(
201 "[{}] Failed to read response content, duration: {:?}, error: {}",
202 request_id, elapsed, error
203 ),
204 None,
205 );
206 }
207
208 pub fn log_raw_response(&self, request_id: &str, response_text: &str) {
210 if !self.config.log_responses {
211 return;
212 }
213
214 let request_id = request_id.to_string();
215 let response_text = response_text.to_string();
216 self.log_safe(
217 LogLevel::Trace,
218 move || format!("[{}] Raw response content length = {}, body = {}", request_id, response_text.len(), response_text),
219 None,
220 );
221 }
222
223 pub fn log_json_parse_error(&self, request_id: &str, elapsed: std::time::Duration, error: &str, response_text: &str) {
225 let request_id = request_id.to_string();
226 let error = error.to_string();
227 let response_preview = if self.config.log_responses_err {
228 response_text.to_string()
229 } else {
230 "[Hidden]".to_string()
231 };
232
233 self.log_safe(
234 LogLevel::Error,
235 move || format!(
236 "[{}] JSON parsing failed, duration: {:?}, error: {}, response content length: {}, response content: {}",
237 request_id, elapsed, error, response_preview.len(), response_preview
238 ),
239 None,
240 );
241 }
242
243 pub fn log_api_error(&self, request_id: &str, elapsed: std::time::Duration, code: i32, message: &str) {
245 let request_id = request_id.to_string();
246 let message = message.to_string();
247 self.log_safe(
248 LogLevel::Error,
249 move || format!(
250 "[{}] API returned error, duration: {:?}, error code: {}, error message: {}",
251 request_id, elapsed, code, message
252 ),
253 None,
254 );
255 }
256
257 pub fn log_api_success(&self, request_id: &str, elapsed: std::time::Duration, data_count: usize) {
259 let request_id = request_id.to_string();
260 if self.config.log_performance {
261 self.log_safe(
262 LogLevel::Info,
263 move || format!(
264 "[{}] API call successful, duration: {:?}, data rows returned: {}",
265 request_id, elapsed, data_count
266 ),
267 None,
268 );
269 } else {
270 self.log_safe(
271 LogLevel::Info,
272 move || format!("[{}] API call successful", request_id),
273 None,
274 );
275 }
276 }
277
278 pub fn log_response_details(&self, request_id: &str, response_request_id: &str, fields: &str) {
280 if !self.config.log_responses {
281 return;
282 }
283
284 let request_id = request_id.to_string();
285 let response_request_id = response_request_id.to_string();
286 let fields = fields.to_string();
287 self.log_safe(
288 LogLevel::Debug,
289 move || format!(
290 "[{}] Response details - Request ID: {}, fields: {}",
291 request_id, response_request_id, fields
292 ),
293 None,
294 );
295 }
296
297 pub fn config(&self) -> &LogConfig {
299 &self.config
300 }
301}