#hdr-histogram #histogram #statistics #sampling #analytics #process-file #log-file

histlog

用于与 hdrhistogram crate 一起使用,提供 HdrHistogram 时间间隔日志的离线程序列化到文件

15 个版本 (4 个稳定版)

2.1.1 2023 年 12 月 21 日
2.0.0 2023 年 12 月 21 日
1.0.0 2021 年 3 月 4 日
0.1.7 2021 年 3 月 4 日
0.1.6 2018 年 12 月 12 日

性能分析 中排名第 316

每月下载 45

MIT 许可证

35KB
433 行代码(不含注释)

histlog

提供将 HdrHistogram 时间间隔日志离线程序列化到文件的功能。

对于与 hdrhistogram crate 一起使用,这是一个 Rust 版本的 Gil Tene 的 HdrHistogram,它提供了一个干净且合理的默认设置,用于离线程序列化 HdrHistogram 时间间隔日志到文件。

用途

HdrHistogram 常用于测量延迟。通常,如果您想测量延迟,则不太可能希望在同一线程上写入文件。

一个选项是将序列化到内存缓冲区中(例如 Vec<u8>)。但是,这仍然需要在缓冲区中分配内存,并且对于长时间运行的进程最终需要大量的内存。

histlog::HistLog 允许热线程定期将其 hdrhistogram::Histogram 传递给指定的写入线程,该线程可以处理 IO 操作。时间间隔日志是增量写入的,并且在程序仍在运行时可以进行审查和分析。

HistLog 完全依赖于 hdrhistogram crate,无论是内存中记录值还是序列化。它提供的功能是离线程写入,具有干净的用户界面和合理的默认设置,使得使用相对容易。

示例

一个 HistLog 有一个 "series" 名称和一个 "tag"。HdrHistogram 区间日志格式为每个条目提供一个 tag。系列名称用于命名写入的区间日志文件。

use std::time::*;

let log_dir = "/tmp/path/to/logs";
let series = "server-latency";          // used to name the log file
let tag = "xeon-e7-8891-v2";            // recorded with each entry
let freq = Duration::from_secs(1);      // how often results sent to writer thread

// `HistLog::new` could fail creating file, `hdrhistogram::Histogram`
let mut server1 = histlog::HistLog::new(log_dir, series, tag, freq).unwrap();

// use `HistLog::clone_with_tag` to serialize a separate tag to same file.
let mut server2 = server1.clone_with_tag("xeon-e5-2670");

for i in 0..1000u64 { // dummy data
    server1.record(i).unwrap(); // call to `hdrhistogram::Histogram::record` could fail
    server2.record(i * 2).unwrap();
}

assert_eq!(server1.path(), server2.path()); // both being saved to same file, via same writer thread

HistLog 的 API 设计是针对事件循环的。循环的每次迭代都会记录新的值,并检查当前时间以确定当前 Histogram 是否应该传递给写线程。

use std::time::*;

let mut spintime = histlog::HistLog::new("/tmp/var/hist", "spintime", "main", Duration::from_secs(60)).unwrap();

let mut loop_time = Instant::now();
let mut prev: Instant;

loop {
    prev = loop_time;
    loop_time = Instant::now();
    spintime.record(histlog::nanos(loop_time - prev)).unwrap(); // nanos: Duration -> u64
    spintime.check_send(loop_time); // sends to writer thread if elapsed > freq,
    // or...
    spintime.check_try_send(loop_time).unwrap(); // non-blocking equivalent (can fail)

    // do important stuff ...
}

日志

日志保存在 <log dir>/<series name>.<datetime>.hdrhistogram-interval-log.v2.gz

日志的格式如下

#[StartTime: 1544631293.283 (seconds since epoch)]
#[BaseTime: 0.000 (seconds since epoch)]
Tag=xeon-e7-8891-v2,1544631293.283,0.003,999.000,HISTFAAAAC94Ae3GMRUAMAgD0bRI6FovNVcHmGREAgNR [...]
Tag=xeon-e5-2670,1544631293.283,0.003,999.000,HISTFAAAABx4AZNpmSzMwMDAxAABzFCaEUoz2X+AsQA/awK [...]
[...]

只有直方图数据被压缩(deflate),因此 .gz 扩展名可能有些误导。

日志文件可以在此处查看/分析 这里(javascript,本地运行)或使用基于 Java 的 HistogramLogAnalyzer

日志序列化的完整文档 可在 hdrhistogram 包中找到。

功能

  • minstant:用 minstant::Instant 替换 std::time::Instant,它通常更快,并且是一个具有相同 API 的直接替换。
  • smol_str:将 smol_str::SmolStr 用于系列名称(SeriesName 类型别名)和 tag(Tag 类型别名)的类型。 SmolStr 允许动态字符串,且无需堆分配,大小可达一定限制,因此该功能使系列名称和 tag 值都可以是动态的。

限制

  • HistLog::check_sendHistLog::check_try_send 在每个区间创建一个新的 hdrhistogram::Histogram,并将当前/上一个发送到写线程。内部,一个 hdrhistogram::Histogram 使用一个 Vec 来存储其计数,因此涉及到分配。
  • 只能记录 u64 值(请参阅 C 类型别名)。

依赖项

~4MB
~74K SLoC