#tide #tracing #middleware #logging #request #request-response #tracing-subscriber

tide-tracing-middleware

使用tracing crate进行日志记录的tide中间件

2个版本

0.1.1 2021年9月17日
0.1.0 2021年9月17日

#489 in 调试

Apache-2.0

27KB
449

tide-tracing-middleware

使用tracing crate进行日志记录的tide中间件。

迁移了actix-web自带的log中间件,以应用于tide框架。

开始使用

代码在examples/basic.rs中。

use tide::{Request, Response, StatusCode};
use tide_tracing_middleware::TracingMiddleware;
use tracing::Level;
use tracing_subscriber::FmtSubscriber;

#[async_std::main]
async fn main() -> tide::Result<()> {
    FmtSubscriber::builder().with_max_level(Level::DEBUG).init();

    let mut app = tide::new();
    app.with(TracingMiddleware::default());
    app.at("/index").get(index);
    app.listen("127.0.0.1:8080").await?;
    Ok(())
}

async fn index(_req: Request<()>) -> tide::Result {
    let res = Response::builder(StatusCode::Ok)
        .body("hello world!")
        .build();
    Ok(res)
}

输出示例:

Sep 16 21:12:39.988  INFO tide_tracing_middleware: 127.0.0.1:56205 "GET /index?a=1&b=2 HTTP/1.1" 200 12 "-" "curl/7.64.1" 0.000278

自定义输出格式

下面的示例将输出大部分的信息,包括所有的请求头和响应头。

let tracing_middleware = TracingMiddleware::new(
	"%t  %a(%{r}a)  %r(%M %U %Q %V) %s %b(bytes) %T(seconds) %D(milliseconds) REQ_HEADERS:%{ALL_REQ_HEADERS}xi RES_HEADERS:%{ALL_RES_HEADERS}xo",
).custom_request_replace("ALL_REQ_HEADERS", |req| {
	let pairs = req.iter().map(|(k, v)| format!("{}:{}", k, v)).collect::<Vec<String>>();
	"{".to_owned() + &pairs.join(",") + "}"
}).custom_response_replace("ALL_RES_HEADERS", |res| {
	let pairs = res.iter().map(|(k, v)| format!("{}:{}", k, v)).collect::<Vec<String>>();
	"{".to_owned() + &pairs.join(",") + "}"
});

输出示例:

Sep 16 21:18:15.174  INFO tide_tracing_middleware: 2021-09-16T13:18:15  127.0.0.1:56234(127.0.0.1:56234)  GET /index?a=1&b=2 HTTP/1.1(GET /index a=1&b=2 HTTP/1.1) 200 12(bytes) 0.000437(seconds) 0.461000(milliseconds) REQ_HEADERS:{accept:["*/*"],user-agent:["curl/7.64.1"],host:["127.0.0.1:8080"]} RES_HEADERS:{content-type:["text/plain;charset=utf-8"]}

支持的标签和actix-web的log中间件一样,但额外增加了几个标签:

  • %%: 百分号
  • %a: 远程IP地址(使用反向代理时的代理IP地址)
  • %t: 开始处理请求的时间(rfc3339格式)
  • %r: 请求的第一行
  • %s: 响应状态码
  • %b: 响应体的大小(字节),不包括HTTP头部
  • %T: 处理请求所需的时间,以秒为单位,带有小数部分(.06f格式)
  • %D: 处理请求所需的时间,以毫秒为单位
  • %U: 请求URL
  • %M: 请求方法
  • %V: 请求HTTP版本
  • %Q: 请求URL的查询字符串
  • %{r}a: 实际IP远程地址
  • %{FOO}i: request.headers['FOO']
  • %{FOO}o: response.headers['FOO']
  • %{FOO}e: os.environ['FOO']
  • %{FOO}xi:标签为“FOO”的自定义请求替换
  • %{FOO}xo:标签为“FOO”的自定义响应替换

生成跟踪跨度

以下示例中使用 uuid 为每个请求生成一个 id,以便将该请求相关的日志关联起来。

#[async_std::main]
async fn main() -> tide::Result<()> {
    FmtSubscriber::builder().with_max_level(Level::DEBUG).init();

    let tracing_middleware = TracingMiddleware::new(
        "%t  %a(%{r}a)  %r(%M %U %Q %V) %s %b(bytes) %T(seconds) %D(milliseconds) REQ_HEADERS:%{ALL_REQ_HEADERS}xi RES_HEADERS:%{ALL_RES_HEADERS}xo",
    ).custom_request_replace("ALL_REQ_HEADERS", |req| {
        let pairs = req.iter().map(|(k, v)| format!("{}:{}", k, v)).collect::<Vec<String>>();
        "{".to_owned() + &pairs.join(",") + "}"
    }).custom_response_replace("ALL_RES_HEADERS", |res| {
        let pairs = res.iter().map(|(k, v)| format!("{}:{}", k, v)).collect::<Vec<String>>();
        "{".to_owned() + &pairs.join(",") + "}"
    }).gen_tracing_span(|_req| {
        tracing::info_span!("R", "{}", uuid::Uuid::new_v4().to_simple().to_string())
    });

    let mut app = tide::new();
    app.with(tracing_middleware);
    app.at("/index").get(index);
    app.listen("127.0.0.1:8080").await?;
    Ok(())
}

async fn index(_req: Request<()>) -> tide::Result {
    info!(a = "123", "index");
    let res = Response::builder(StatusCode::Ok)
        .body("hello world!")
        .build();
    Ok(res)
}

输出示例:

Sep 16 21:22:29.564  INFO R{c7abce9aba3c4a2c9161c3df20a4141b}: trace_span: index a="123"
Sep 16 21:22:29.564  INFO R{c7abce9aba3c4a2c9161c3df20a4141b}: tide_tracing_middleware: 2021-09-16T13:22:29  127.0.0.1:56260(127.0.0.1:56260)  GET /index?a=1&b=2 HTTP/1.1(GET /index a=1&b=2 HTTP/1.1) 200 12(bytes) 0.000613(seconds) 0.626000(milliseconds) REQ_HEADERS:{user-agent:["curl/7.64.1"],accept:["*/*"],host:["127.0.0.1:8080"]} RES_HEADERS:{content-type:["text/plain;charset=utf-8"]}

依赖项

~12–25MB
~376K SLoC