You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Warp框架非匹配路由重复执行返回404 日志指标统计异常

Warp框架路由遍历导致日志、指标统计异常问题

问题现象

开发搭载Prometheus指标端点的Web服务器时发现指标统计失真:Warp框架会对每个进入的请求遍历执行所有已注册路由的逻辑,和当前请求不匹配的路由会返回404响应,导致挂载在路由上的日志打印、404计数逻辑被无效触发。

复现代码

最简复现代码如下:

extern crate warp;
extern crate tokio;
extern crate fern;

use warp::Filter;

#[tokio::main]
async fn main() {
    fern::Dispatch::new()
    .format(|out, message, record| {
    out.finish(format_args!(
        "{}[{}][{}] {}",
        chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
        record.target(),
        record.level(),
        message
    ))
    })
    .level(log::LevelFilter::Debug)
    .chain(std::io::stdout())
    .apply();

    let hlog = warp::log("hlog");
    let glog = warp::log("glog");
    // GET /hello/warp => 200 OK with body "Hello, warp!"
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name))
        .with(hlog);
        
    let goodbye = warp::path!("goodbye")
        .map(|| format!("goodbye"))
        .with(glog);

    let routes = hello.or(goodbye);
    
    warp::serve(routes)
        .run(([127, 0, 0, 1], 8080))
        .await;
}

访问localhost:8080/goodbye路径时,绑定在/hello路由上的hlog日志组件也会输出404响应的日志,日志输出如下:

[2022-06-27][18:57:59][warp::server][INFO] Server::run; addr=127.0.0.1:8080
[2022-06-27][18:57:59][warp::server][INFO] listening on http://127.0.0.1:8080
[2022-06-27][18:58:04][hyper::proto::h1::io][DEBUG] parsed 12 headers
[2022-06-27][18:58:04][hyper::proto::h1::conn][DEBUG] incoming body is empty
[2022-06-27][18:58:04][hlog][INFO] 127.0.0.1:62698 "GET /goodbye HTTP/1.1" 404 "-" "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0" 68µs
[2022-06-27][18:58:04][glog][INFO] 127.0.0.1:62698 "GET /goodbye HTTP/1.1" 200 "-" "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0" 25.3µs
[2022-06-27][18:58:04][hyper::proto::h1::io][DEBUG] flushed 123 bytes
[2022-06-27][18:58:04][hyper::proto::h1::io][DEBUG] parsed 11 headers
[2022-06-27][18:58:04][hyper::proto::h1::conn][DEBUG] incoming body is empty
[2022-06-27][18:58:04][hyper::server::server::new_svc][DEBUG] connection error: connection closed before message completed

如果在.with()方法中挂载404响应计数的Prometheus逻辑,每个被路由正常匹配的合法请求,都会同时触发所有其他未匹配路由的404计数,导致统计结果完全失真。

根因说明

Warp的Filter::or组合逻辑是顺序尝试匹配路由:前一个路由返回rejection(路径/方法不匹配)时,会自动把请求传给下一个路由继续尝试。如果把日志、指标中间件挂载在单个子路由内部,只要请求进入过该路由的处理流程,哪怕最后因为不匹配返回rejection,中间件也会被执行,这就是无效日志、错误计数的来源。

解决方案

  • 通用日志、全局指标中间件统一挂载到所有路由组合完成后的最外层,不要绑定在单个子路由上。这种写法下中间件只会在最终路由匹配完成、确定实际响应结果的时候执行,不会捕获路由匹配过程中产生的临时404 rejection。示例写法:
    // 先定义业务路由,不要在单个路由上挂日志/指标
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name));
    let goodbye = warp::path!("goodbye")
        .map(|| format!("goodbye"));
    // 组合所有业务路由
    let routes = hello.or(goodbye);
    // 最后统一在最外层挂载日志、Prometheus指标中间件
    let routes = routes.with(warp::log("access_log"));
    // 自定义Prometheus指标中间件也挂在这里
    
  • 如果需要按路由分维度做独立的日志、指标统计,不要直接用with把中间件挂在未做匹配兜底的路由上,要在路由确定匹配成功后的逻辑(map/and_then处理块)里做埋点,此时路由已经匹配完成,不会统计到匹配阶段的临时拒绝。
  • Prometheus指标采集场景可以直接使用成熟的warp指标中间件实现,这类实现默认挂载在路由最外层,会自动识别最终匹配的路由路径、响应状态码,不会把路由遍历过程中的临时rejection计入统计。

内容的提问来源于stack exchange,提问作者myaple

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:36:16