#logging-tracing #metrics #tracing #logging #observability

emit_file

向滚动文件中发出诊断事件

9个版本

0.11.0-alpha.9 2024年8月21日
0.11.0-alpha.82024年8月15日
0.11.0-alpha.72024年7月31日
0.11.0-alpha.62024年6月28日

#496 in 调试

Download history 639/week @ 2024-06-07 71/week @ 2024-06-14 2/week @ 2024-06-21 178/week @ 2024-06-28 28/week @ 2024-07-05 106/week @ 2024-07-26 22/week @ 2024-08-02 83/week @ 2024-08-09 155/week @ 2024-08-16

366 每月下载量

MIT/Apache

250KB
5K SLoC

emit_file

file

当前文档

向滚动文件中发出诊断事件。


lib.rs:

向滚动文件中发出诊断事件。

所有文件I/O都在一个专用的后台线程中按批执行。

此库默认以换行符分隔的JSON写入,例如

{"ts_start":"2024-05-29T03:35:13.922768000Z","ts":"2024-05-29T03:35:13.943506000Z","module":"my_app","msg":"in_ctxt failed with `a` is odd","tpl":"in_ctxt failed with `err`","a":1,"err":"`a` is odd","lvl":"warn","span_id":"0a3686d1b788b277","span_parent":"1a50b58f2ef93f3b","trace_id":"8dd5d1f11af6ba1db4124072024933cb"}

入门

emitemit_file 添加到您的 Cargo.toml

[dependencies.emit]
version = "0.11.0-alpha.9"

[dependencies.emit_file]
version = "0.11.0-alpha.9"

使用滚动文件集初始化 emit

fn main() {
let rt = emit::setup()
.emit_to(emit_file::set("./target/logs/my_app.txt").spawn().unwrap())
.init();

// Your app code goes here

rt.blocking_flush(std::time::Duration::from_secs(30));
}

[\code]set 的输入是日志文件命名的模板。前面示例中使用了 ./target/logs/my_app.txt。从该模板,日志文件将写入 ./target/logs,每个日志文件名将以 my_app 开头,并使用 .txt 作为其扩展名。

文件命名

日志文件使用以下命名方案创建

{prefix}.{date}.{counter}.{id}.{ext}

其中

  • prefix: 一个用户定义的名称,用于将所有与同一应用程序相关的日志文件分组在一起。
  • date: 文件创建时所在的滚动间隔。这不一定与文件中事件的戳记有关。
  • counter: 文件创建时从当前滚动间隔开始以来的毫秒数。
  • id: 文件在该间隔中的唯一标识符。
  • ext: 一个用户定义的文件扩展名。

在以下日志文件中

log.2024-05-27-03-00.00012557.37c57fa1.txt

部分是

  • prefix: log.
  • 日期2024-05-27-03-00
  • 计数器00012557
  • ID37c57fa1
  • 扩展名txt

当文件滚动时

诊断事件一次只写入一个文件。该文件在以下情况下会发生变化:

  1. 应用程序重启,并且 FileSetBuilder::reuse_files 为 false。
  2. 滚动周期改变。这由 FileSetBuilder::roll_by_dayFileSetBuilder::roll_by_hourFileSetBuilder::roll_by_minute 设置。
  3. 文件大小超过 FileSetBuilder::max_file_size_bytes
  4. 写入文件失败。

持久性

诊断事件以异步批量的形式写入文件。在正常操作中,在调用 emit::Emitter::blocking_flush 之后,所有在调用之前发出的事件都将被确保写入并通过 Rust 的 std::fs::File::sync_all 方法进行同步。这通常足以保证持久性。

处理IO故障

如果在尝试写入文件时写入批处理失败,则正在写入的文件被视为已中毒,并且不会尝试再次写入。批处理将而是在新文件上重试。尝试同步失败的批处理不会重试。由于批处理没有显式的事务,在失败的情况下,可能实际上在原始文件中存在失败批处理的部分或全部。这意味着在写入诊断事件时可能发生IO错误,导致诊断事件重复。

故障排除

如果您没有看到诊断文件如预期的那样出现,可以通过配置 emit 的内部日志记录器来排除 emit_file 中的配置问题,并收集其指标

use emit::metric::Source;

fn main() {
// 1. Initialize the internal logger
//    Diagnostics produced by `emit_file` itself will go here
let internal = emit::setup()
.emit_to(emit_term::stdout())
.init_internal();

let mut reporter = emit::metric::Reporter::new();

let rt = emit::setup()
.emit_to({
let files = emit_file::set("./target/logs/my_app.txt").spawn().unwrap();

// 2. Add `emit_file`'s metrics to a reporter so we can see what it's up to
//    You can do this independently of the internal emitter
reporter.add_source(files.metric_source());

files
})
.init();

// Your app code goes here

rt.blocking_flush(std::time::Duration::from_secs(30));

// 3. Report metrics after attempting to flush
//    You could also do this periodically as your application runs
reporter.emit_metrics(&internal.emitter());
}

诊断包括批处理写入的时间和观察到的任何失败。

依赖项

~1.4–2.1MB
~48K SLoC