tracing-subscriber
tracing-core 只定义了 Subscriber 这个 trait,真正收下事件、记住 span 的代码几乎都来自 tracing-subscriber。它不写一个大而全的 Subscriber,而是拆成两类东西:一个 Registry 负责发 span 的 Id、存 span 的数据、记每个线程的当前 span;若干个 Layer 各管一件事(过滤、格式化输出、统计耗时),用 .with(...) 叠到 Registry 外面,叠出来的 Layered<..> 本身又是一个 Subscriber。fmt().with_env_filter(..).init() 这一行,展开后就是 Registry 外面先套一个 fmt::Layer、再套一个 EnvFilter。
六章按「先组合、再存储、再过滤、最后输出」的顺序:
- Layer 与 Layered:
Layer是Subscriber去掉发Id的那部分;Layered的判断方法从外往里问、遇到「不要」就短路,通知方法先交给内层再调本层;register_callsite由pick_interest合并;Option<L>、Vec<L>、Box<dyn Layer>也是Layer。 - Registry:span 存在 sharded-slab 的
Pool<DataInner>里,Id是打包了槽地址、线程号、代数的键加一;每个线程一个 span 栈;CloseGuard保证所有层的on_close跑完才释放槽;LookupSpan、SpanRef、scope()让 layer 查 span 数据。 - 过滤:全局与每层:
.with(filter)挡住所有层,layer.with_filter(filter)只挡这一层;每层过滤靠FilterId和线程局部的位图实现。 - EnvFilter:
RUST_LOG指令语法,静态指令和动态指令的区别,错误指令的处理。 - fmt:
fmt::Layer的四个类型参数、字段预先格式化存进Extensions、四种输出格式、MakeWriter、init做了什么。 - reload 与运行时改级别:
reload::Layer换掉 filter 后重建Interest缓存。
flowchart TB initcall["fmt().init()"] --> outer["Layered:EnvFilter"] outer --> mid["Layered:fmt::Layer"] mid --> reg["Registry"] reg --> pool["span 池:sharded-slab"] reg --> stackn["每线程 span 栈"] mid -.->|"ctx.span(id)"| reg
前两章是后四章的基础:过滤、fmt、reload 都是某种 Layer,都靠 Context 向 Registry 查 span。
💬 关联讨论
发起讨论加载中...