Rust in Everything
基于 tracing 0.1.44

tracing 门面

上一部分的 tracing-core 只定义类型:Metadata、DefaultCallsite、Dispatch、Subscriber、ValueSet。用户写的 info!、info_span!、span.enter()、.instrument(span) 都来自 tracing 这个 crate。它做的事是把这些类型拼起来:宏在每个调用点放好静态数据和判断,Span 记住自己属于哪个 dispatcher,Instrumented 在 poll 前后进出 span,log feature 在没有 subscriber 时把事件转给 log。

五章:

  1. 宏展开:用 cargo expand 看 info!("x = {}", x) 和 info_span!("req", id = 1) 展开成什么,每种字段写法变成什么;macros.rs 3 342 行里,五个级别宏是逐字相同的五份拷贝,event! 里同一段主体写了六遍。
  2. 级别过滤的几道门:事件主线上的五道门依次是 STATIC_MAX_LEVEL、LevelFilter::current()、调用点的 Interest、Subscriber::enabled、event_enabled;前四道挡住的事件不对字段表达式求值;用 criterion 测每道门的开销。
  3. Span 与 Entered:Span 里的 inner 是 Option,被过滤掉的 span 所有操作都是空操作;enter 返回借用 span 的守卫,entered 返回拥有 span 的守卫;Clone、Drop 对应 subscriber 的引用计数。
  4. Instrument 与异步:Instrumented<F> 每次 poll 前进入 span、返回后退出,Drop 时也进入一次;为什么不能跨 .await 持有 Entered;WithDispatch 把 dispatcher 带进别的任务。
  5. 和 log 互通:两个方向,tracing 的事件经 log feature 变成 log 记录,log 的记录经 tracing-log 的 LogTracer 变成 tracing 事件;两边都开时会不会重复。
flowchart TB
  user["info! 与 info_span!"] --> exp["宏展开:static 元数据"]
  exp --> gates["五道门"]
  gates --> ev["Event::dispatch"]
  gates --> sp["Span::new"]
  sp --> ent["enter 与 Entered"]
  ent --> inst["Instrumented:每次 poll"]
  ev --> logfeat["log feature:转给 log"]

前两章在事件主线的开头,后两章在 span 主线上,最后一章两条线都碰到。宏展开后调用的类型在 tracing-core 一部分讲过,这里只链过去;#[instrument] 生成的代码在 展开成什么。

💬 关联讨论

发起讨论
加载中...