如何在tracing中条件使用non_blocking writer替代stdout并解决类型错误?
解决tracing Subscriber类型不匹配问题
我尝试用以下Rust代码初始化tracing subscriber,需求是仅在特定场景下使用non_blocking writer,其余场景用stdout,但出现了类型不匹配的编译错误,求解决方法:
use tracing_appender::non_blocking; use tracing_log::LogTracer; use tracing_subscriber::{fmt::Layer, prelude::*, EnvFilter, Registry}; pub fn init(tracing: &Tracing) -> non_blocking::WorkerGuard { LogTracer::init().expect("init LogTracer"); let (non_blocking, non_blocking_guard) = non_blocking(std::io::stdout()); let base_layer; if tracing.blocking { base_layer = Layer::default().with_writer(std::io::stdout); } else { base_layer = Layer::default().with_writer(non_blocking); } let mut layers = Vec::new(); layers.push(if tracing.pretty { base_layer.pretty().with_file(false).boxed() } else { base_layer.json().boxed() }); let registry = Registry::default() .with(EnvFilter::from(&tracing.level)) .with(layers); tracing::subscriber::set_global_default(registry).unwrap(); non_blocking_guard }
编译错误信息:
error[E0308]: mismatched types | 18 | base_layer = Layer::default().with_writer(non_blocking); | ----------- ^^^^^^^^^^^^ expected fn item, found struct `tracing_appender::non_blocking::NonBlocking` | | | arguments to this function are incorrect | = note: expected fn item `fn() -> std::io::Stdout {std::io::stdout}` found struct `tracing_appender::non_blocking::NonBlocking` note: associated function defined here | 177 | pub fn with_writer<W2>(self, make_writer: W2) -> Layer<S, N, E, W2> | ^^^^^^^^^^^
问题根源
两个分支中base_layer的类型不一致:
- 当
tracing.blocking为true时,with_writer接收的是std::io::stdout(返回Stdout的函数),此时Layer的writer类型为fn() -> Stdout - 当
tracing.blocking为false时,with_writer接收的是NonBlocking结构体,此时Layer的writer类型为NonBlocking
Rust要求同一变量的类型必须统一,因此触发类型不匹配错误。
解决方案
利用tracing_subscriber::fmt::MakeWriter trait,将两种writer统一为Box<dyn MakeWriter> trait对象,让两个分支的Layer类型保持一致。
修正后的代码:
use tracing_appender::non_blocking; use tracing_log::LogTracer; use tracing_subscriber::{fmt::{Layer, MakeWriter}, prelude::*, EnvFilter, Registry}; pub fn init(tracing: &Tracing) -> non_blocking::WorkerGuard { LogTracer::init().expect("init LogTracer"); let (non_blocking, non_blocking_guard) = non_blocking(std::io::stdout()); // 统一writer类型为Box<dyn MakeWriter> let writer: Box<dyn MakeWriter> = if tracing.blocking { Box::new(std::io::stdout) } else { Box::new(non_blocking) }; let base_layer = Layer::default().with_writer(writer); let mut layers = Vec::new(); layers.push(if tracing.pretty { base_layer.pretty().with_file(false).boxed() } else { base_layer.json().boxed() }); let registry = Registry::default() .with(EnvFilter::from(&tracing.level)) .with(layers); tracing::subscriber::set_global_default(registry).unwrap(); non_blocking_guard }
说明
MakeWriter是tracing_subscriber提供的标准trait,所有可作为日志输出目标的类型都实现了该trait,包括std::io::stdout函数和NonBlocking结构体- 使用
Box<dyn MakeWriter>将不同类型的writer封装为 trait 对象,确保Layer的writer类型统一,消除类型不匹配问题 - 即使在blocking模式下,我们依然创建了non_blocking writer,但此时不会被使用;WorkerGuard仍需返回,因为它负责管理后台线程的生命周期
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

