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

Rust中tracing库与daemonize结合后日志丢失问题求助

Rust中tracing库与daemonize结合后日志丢失问题求助

嗨,这个问题我之前也踩过坑!核心原因和daemonize的工作机制、tracing_appender的non_blocking实现脱不了干系,咱们一步步说清楚:

为什么"Bar"日志会丢失?

当你调用daemonize::Daemonize::new().start()时,它会执行标准的守护进程化流程:fork出子进程,父进程直接退出,子进程脱离终端成为独立的会话首进程。

而你在daemon化前创建的non_blocking writer,是基于**跨线程通道(mpsc)**实现的:父进程里有通道的发送端,另一个线程拿着接收端负责把日志写入文件。当父进程退出时,发送端会被自动销毁,通道直接关闭——子进程里的tracing再发日志,只能往一个已经关闭的通道里塞,自然就没人处理这些日志了,"Bar"也就丢了。

而log库之所以能正常工作,是因为它默认的实现大多是同步直接写文件,不需要依赖跨线程通道,daemon化后只要文件描述符还保留着,就能继续写入。

两种解决办法,看你需求选:

方案1:先daemon化,再初始化tracing(更推荐)

把tracing的初始化逻辑放到daemon化完成之后,这样所有日志组件都是在子进程里创建的,完全不受父进程退出的影响:

use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::{fmt, Registry};

fn main() {
    // 先完成守护进程化
    daemonize::Daemonize::new().start().unwrap();
    
    // 在子进程里初始化tracing
    let file_appender = tracing_appender::rolling::never("/tmp", "test.log");
    let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);

    let subscriber = Registry::default().with(fmt::Layer::default().with_writer(non_blocking));
    tracing::subscriber::set_global_default(subscriber).unwrap();
    
    info!("Bar"); // 现在这个日志能正常写入了
    // 要是需要保留"Foo"的日志,可以在daemon化前临时输出到终端,或者也写入文件(注意不要覆盖)
}

方案2:用同步文件写入器代替non_blocking

如果你不想调整初始化顺序,可以试试直接用同步的file appender,这样不需要依赖跨线程通道,daemon化后只要文件没被关闭就能继续写:

use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::{fmt, Registry};

fn main() {
    let file_appender = tracing_appender::rolling::never("/tmp", "test.log");
    // 直接用同步writer,跳过non_blocking
    let subscriber = Registry::default().with(fmt::Layer::default().with_writer(file_appender));
    tracing::subscriber::set_global_default(subscriber).unwrap();
    
    info!("Foo");
    daemonize::Daemonize::new().start().unwrap();
    info!("Bar"); // 现在这个日志也能正常写入了
}

不过同步写入会影响高频日志场景的性能,所以更推荐第一种方案。

关于tracing_log的适配

如果你要结合tracing_log把原有log库的日志转到tracing里,只要在初始化tracing subscriber之后加上LogTracer的初始化就行,比如:

use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::{fmt, Registry};

fn main() {
    daemonize::Daemonize::new().start().unwrap();
    
    // 初始化tracing_log,把log的日志转发到tracing系统
    tracing_log::LogTracer::init().unwrap();
    
    let file_appender = tracing_appender::rolling::never("/tmp", "test.log");
    let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);

    let subscriber = Registry::default().with(fmt::Layer::default().with_writer(non_blocking));
    tracing::subscriber::set_global_default(subscriber).unwrap();
    
    info!("Bar");
    // 原来用log::info!的代码也会自动写到这个日志文件里
}

最后提醒一句:daemon化后进程的工作目录会变,一定要用绝对路径指定日志文件(你已经用了/tmp,这点没问题),不然可能会找不到文件哦~

备注:内容来源于stack exchange,提问作者mfld

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 10:49:33