cool_core/middleware/
log.rs

1//! 日志中间件
2//!
3//! 对应 TypeScript 版本的 `middleware/log.ts`
4
5use super::DepotExt;
6use salvo::prelude::*;
7use std::time::Instant;
8use tracing::{info, warn};
9
10/// 请求日志中间件
11pub struct RequestLogMiddleware {
12    /// 是否记录请求体
13    log_body: bool,
14    /// 忽略的路径
15    ignore_paths: Vec<String>,
16}
17
18impl RequestLogMiddleware {
19    pub fn new() -> Self {
20        Self {
21            log_body: false,
22            ignore_paths: vec![],
23        }
24    }
25
26    /// 设置是否记录请求体
27    pub fn log_body(mut self, enable: bool) -> Self {
28        self.log_body = enable;
29        self
30    }
31
32    /// 添加忽略的路径
33    pub fn ignore_path(mut self, path: impl Into<String>) -> Self {
34        self.ignore_paths.push(path.into());
35        self
36    }
37}
38
39impl Default for RequestLogMiddleware {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44
45#[async_trait]
46impl Handler for RequestLogMiddleware {
47    async fn handle(
48        &self,
49        req: &mut Request,
50        depot: &mut Depot,
51        res: &mut Response,
52        ctrl: &mut FlowCtrl,
53    ) {
54        let path = req.uri().path().to_string();
55
56        // 检查是否在忽略列表中
57        for ignore_path in &self.ignore_paths {
58            if path.starts_with(ignore_path) {
59                ctrl.call_next(req, depot, res).await;
60                return;
61            }
62        }
63
64        let method = req.method().to_string();
65        let start = Instant::now();
66
67        // 获取客户端 IP
68        let client_ip = req
69            .remote_addr()
70            .as_ipv4()
71            .map(|addr| addr.to_string())
72            .or_else(|| req.remote_addr().as_ipv6().map(|addr| addr.to_string()))
73            .unwrap_or_else(|| "unknown".to_string());
74
75        // 获取用户信息
76        let user_id = depot
77            .admin()
78            .map(|admin| admin.user_id.to_string())
79            .unwrap_or_else(|| "anonymous".to_string());
80
81        // 执行请求
82        ctrl.call_next(req, depot, res).await;
83
84        // 计算耗时
85        let duration = start.elapsed();
86        let status = res.status_code.unwrap_or(StatusCode::OK);
87
88        // 记录日志
89        if status.is_success() {
90            info!(
91                method = %method,
92                path = %path,
93                status = %status.as_u16(),
94                duration_ms = %duration.as_millis(),
95                ip = %client_ip,
96                user_id = %user_id,
97                "request completed"
98            );
99        } else {
100            warn!(
101                method = %method,
102                path = %path,
103                status = %status.as_u16(),
104                duration_ms = %duration.as_millis(),
105                ip = %client_ip,
106                user_id = %user_id,
107                "request failed"
108            );
109        }
110    }
111}
112
113/// 创建请求日志中间件
114pub fn request_log() -> RequestLogMiddleware {
115    RequestLogMiddleware::new()
116}