#metrics #macro #measure #attributes #proc-macro #default #metered

metered-macro

为 Rust 提供快速、高效的度量!

8 次重大发布

0.9.0 2022 年 6 月 5 日
0.8.0 2021 年 3 月 26 日
0.7.0 2020 年 10 月 31 日
0.4.0 2020 年 5 月 25 日
0.1.2 2019 年 2 月 12 日

#73#measure

Download history 15641/week @ 2024-04-05 14551/week @ 2024-04-12 16997/week @ 2024-04-19 17745/week @ 2024-04-26 18819/week @ 2024-05-03 16885/week @ 2024-05-10 15978/week @ 2024-05-17 17604/week @ 2024-05-24 15109/week @ 2024-05-31 14399/week @ 2024-06-07 14192/week @ 2024-06-14 11807/week @ 2024-06-21 11196/week @ 2024-06-28 12538/week @ 2024-07-05 11614/week @ 2024-07-12 11707/week @ 2024-07-19

49,501 每月下载量
5 个包中(通过 metered)使用

Apache-2.0 OR MIT

39KB
868

metered-rs

Build Status License Cargo Documentation Rust 1.31+

为 Rust 提供快速、高效的度量!

Metered 帮助您在生产环境中测量程序的性能。受 Coda Hale 的 Java 度量库的启发,Metered 通过提供声明式和过程宏来测量程序,而不改变您的逻辑,使得实时测量变得简单。

Metered 的构建基于以下原则

  • 高易用性但无魔法:度量代码只需注释代码即可。Metered 允许您从裸度量构建自己的度量注册表,或使用过程宏生成一个。它不使用共享的全局变量或静态变量。

  • 恒定、非常低的开销:好的易用性不应带来开销;唯一的开销是由实际的度量后端本身(例如,计数器、仪表、直方图)所施加的,而 Metered 提供的这些后端在初始化后不会分配。Metered 将度量注册表生成为常规 Rust struct,因此查找度量时没有涉及查找。Metered 提供了无同步和线程安全的度量后端,以便单线程或无共享架构无需为同步付费。尽可能使用 Metered 提供的线程安全的度量后端,这些后端使用无锁数据结构。

  • 可扩展:度量只是实现了具有特定行为的 Metric 特质的常规类型。Metered 的宏允许您引用任何 Rust 类型,从而产生用户可扩展的属性!

许多指标只有在获得精确统计时才有意义。在低延迟、高范围直方图方面,没有比Gil Tene的动态范围直方图更好的了,而Metered默认使用官方的Rust端口为其直方图。

变更日志

  • 0.9.0:
    • 使用包装int指标代替下溢/上溢
    • 提供方法以递增或递减int指标超过1,这对于批处理计算很有用
    • 添加了对Clear的泛型实现(由@plankton6贡献)
    • HdrHistogram添加了len方法(由@plankton6贡献)
    • 代码质量修复和依赖更新
  • 0.8.0:
    • 通过OnResultMut而不是OnResult更新指标,以支持需要可变访问结果指标的指标 - 例如消费一个Stream(由@w4贡献)
  • 0.7.0:
    • 公开内部指标后端类型Throughput(修复了问题#30)
    • 为所有顶级指标实现Deref
    • 公开内部指标后端类型Throughput
    • error_count属性添加skip_cleared选项(由@w4贡献)
      • 引入一个新的Clearable特质,该特质公开了实现Clear的指标的行为(为了向后兼容)。目前仅在计数器上实现。
      • 可以通过构建时的功能来控制默认行为,error-count-skip-cleared-by-default
  • 0.6.0:
    • 扩展error_count宏,允许报告嵌套枚举错误变体,为嵌套错误提供零成本的错误跟踪(由@w4贡献)
  • 0.5.0:
    • 使内部指标公开(由@nemosupremo贡献)
    • 提供error_count宏来为错误枚举生成定制的ErrorCount指标计数变体(由@w4贡献)
    • 使用Drop自动触发不依赖于结果值的指标(影响InFlightResponseTimeThroughput
  • 0.4.0:
    • 向生成的结构体添加allow(missing_docs)(这允许在Rust代码中使用带有lint级别warn(missing_docs)或甚至deny(missing_docs)的metered结构体)(由@reyk贡献)
    • 为生成的注册表实现Clear(由@eliaslevy贡献)
    • RefCell<HdrHistogram>实现HistogramClear(由@eliaslevy贡献)
    • 引入一个具有微秒精度的Instant(由@eliaslevy贡献)
      • API破坏性更改:Instant.elapsed_millis重命名为elapsed_time,并引入了一个新的关联常量,ONE_SEC,用于指定一秒的瞬时单位。
    • 通过重新导出使AtomicTxPerSecTxPerSec可见(由@eliaslevy贡献)
    • StdInstant作为TxPerSecT: Instant的默认类型参数(由@eliaslevy贡献)
    • 修改HdrHistogram以与serde_prometheus一起工作(由@w4贡献)
    • 依赖项更新
      • indexmap: 1.1 -> 1.3
      • hdrhistogram: 6.3 -> 7.1
      • parking_lot: 0.9 -> 0.10
  • 0.3.0:
    • 修复了在 async 测量方法中保留 span 的问题。
    • 更新了夜间构建样本以支持新语法和 Tokio 0.2-alpha(使用 std futures,需要 Rust >= 1.39,nightly 或非 nightly)
    • 更新了依赖项以使用 synproc-macro2quote 1.0
  • 0.2.2:
    • #measured 方法中的异步支持不再依赖于异步闭包,因此客户端代码不需要 async_closure 功能门。
    • 更新了依赖项版本
  • 0.2.1:
    • 在特定情况下,Serde 会为 PhantomData 标记中的 ResponseTimeThroughput 指标序列化 "nulls"。现在它们被明确排除。
  • 0.2.0:
    • 支持 .await 语法用户(不再需要 await!()
  • 0.1.3:
    • 修复了在 #[measure]’ed 方法中的早期返回问题
    • 移除了对 crate AtomicRefCell 的使用,它有时会引发恐慌。
    • 支持自定义注册表可见性。
    • 支持 async + await!() 宏用户。

使用 Metered

Metered 提供了各种有用的指标,开箱即用。

  • HitCount:一个计数器,跟踪代码被击中的次数。
  • ErrorCount:一个计数器,跟踪返回的错误数量 -- (适用于返回 std Result 的任何表达式)
  • InFlight:一个仪表,跟踪活跃请求的数量
  • ResponseTime:由 HdrHistogram 支持的表达式持续时间的统计数据
  • Throughput:由 HdrHistogram 支持的表达式每秒钟被调用的次数的统计数据

这些指标通常应用于方法,使用提供的程序宏生成样板代码。

为了提高性能,这些库存指标可以自定义以使用非线程安全(!Sync/!Send)数据结构,但默认情况下会尽可能使用使用无锁策略实现的线程安全数据结构。这是一个为了在各种情况下提供默认值而做出的舒适选择。

Metered 设计为零开销抽象 -- 在这个意义上,高级舒适度不应超过手动添加指标。值得注意的是,库存指标在第一次初始化后 不会 分配内存。然而,它们在每个方法调用时都会触发,因此在热点代码路径中使用较轻的指标(例如 HitCount)和在高级入口点中使用较重的指标(例如 ThroughputResponseTime)可能很有趣。

如果您需要的指标缺失,或者您想自定义一个指标(例如,跟踪特定错误发生的次数,或根据您的返回类型做出反应),您可以通过实现 trait metered::metric::Metric 来简单地实现自己的指标。

计量器不使用静态变量或共享的全局状态。相反,它允许您使用所需的度量指标构建自己的度量注册表,或者可以使用方法属性为您生成度量注册表。计量器将为每个带有 metered 属性的 impl 块生成一个注册表,该注册表名称由 registry 参数提供。默认情况下,计量器将期望注册表以 self.metrics 的形式访问,但可以通过 registry_expr 属性参数来覆盖表达式。有关更多示例,请参阅演示。

计量器将生成从 Debugserde::Serialize 派生的度量注册表,以便轻松提取您的指标。计量器为每个带有 measure 属性的方法生成一个子注册表,因此组织了层次化的指标。这确保了生成的注册表中访问指标的时间始终是恒定的(并且在可能的情况下,缓存友好),没有任何除指标本身之外的额外开销。

计量器可以愉快地测量任何方法,无论它是否是 async,并且指标将按预期工作(例如,ResponseTime 将返回跨 await 的调用完成的完成时间)。

目前,计量器不提供连接到外部度量存储或监控系统的桥梁。此类支持计划在单独的模块中实现(欢迎贡献!)。

所需的 Rust 版本

计量器从 1.31.0 版本的稳定版 Rust 开始工作。

它不使用任何夜间功能。在某些时候可能会添加一个 nightly 功能标志来使用即将推出的 Rust 功能(例如,const fn),以及计量器所依赖的 crate 中的类似功能,但这优先级较低(欢迎贡献)。

use metered::{metered, Throughput, HitCount};

#[derive(Default, Debug, serde::Serialize)]
pub struct Biz {
    metrics: BizMetrics,
}

#[metered(registry = BizMetrics)]
impl Biz {
    #[measure([HitCount, Throughput])]
    pub fn biz(&self) {        
        let delay = std::time::Duration::from_millis(rand::random::<u64>() % 200);
        std::thread::sleep(delay);
    }   
}

在上面的片段中,我们将测量 HitCountThroughput

这通过首先用 metered 注解 impl 块并指定计量器应提供给度量注册表的名称(这里为 BizMetrics)来实现。稍后,计量器将假设访问该存储库的表达式为 self.metrics,因此我们需要在 Biz 中有一个 metrics 字段,其类型为 BizMetrics。可以通过指定另一个注册表达式来使用另一个字段名,例如 #[metered(registry = BizMetrics, registry_expr = self.my_custom_metrics)]

然后,我们必须使用 measure 属性注释我们希望测量的方法,并指定我们希望应用的指标:这里的指标是实现了 Metric 特性的结构体的类型,您可以定义自己的。由于没有魔法,我们必须确保 self.metrics 可以访问,并且这仅在具有 &self&mut self 接收器的方法上才能正常工作。

让我们看看biz的代码:它是一个阻塞方法,使用rand::random返回,在0到200毫秒之间。由于random具有随机分布,我们可以预期平均睡眠时间为约100毫秒。这意味着每秒大约有10次调用。

在接下来的测试中,我们创建了5个线程,每个线程将调用biz() 200次。因此,我们可以预期调用次数为1000,大约需要20秒(这意味着20个样本,因为我们每秒收集一个样本),并且大约每秒50次调用(每个线程10次,共5个线程)。

use std::thread;
use std::sync::Arc;

fn test_biz() {
    let biz = Arc::new(Biz::default());
    let mut threads = Vec::new();
    for _ in 0..5 {
        let biz = Arc::clone(&biz);
        let t = thread::spawn(move || {
            for _ in 0..200 {
                biz.biz();
            }
        });
        threads.push(t);
    }
    for t in threads {
        t.join().unwrap();
    }
    // Print the results!
    let serialized = serde_yaml::to_string(&*biz).unwrap();
    println!("{}", serialized);
}

然后我们可以使用serde将我们的类型序列化为YAML

metrics:
  biz:
    hit_count: 1000
    throughput:
      - samples: 20
        min: 35
        max: 58
        mean: 49.75
        stdev: 5.146600819958742
        90%ile: 55
        95%ile: 55
        99%ile: 58
        99.9%ile: 58
        99.99%ile: 58
      - ~

我们看到我们确实有每秒49.75次的平均值,这与我们的预期相符。

支持这些统计数据的Hdr Histogram能够给出比固定百分位数更多的信息,但在使用文本时这是一个实用的视图。为了更好的性能分析,请观看Gil Tene的演讲 ;-).

宏参考

metered属性

#[metered(注册表=您的注册表名称,注册表表达式=self.wrapper.my_registry)]

registry是强制性的,并且必须是一个有效的Rust标识符。

registry_expr默认为self.metrics,其他值必须是一个有效的Rust表达式。此设置允许您配置解析为注册表的表达式。请注意,这将触发该表达式的不可变借用。

visibility默认为pub(crate),并且必须是一个有效的Rust结构可见性(例如,pub<nothing>pub(self)等)。此设置允许您更改生成的注册表struct的可见性。注册表字段始终是公共的,并按蛇形方法或度量名称命名。

measure属性

单个度量

#[measure(path::to::MyMetric<u64>)]

#[measure(类型=path::to::MyMetric<u64>)]

多个度量

#[measure([path::to::MyMetric<u64>,path::AnotherMetric])]

#[measure(类型=[path::to::MyMetric<u64>,path::AnotherMetric])]

type关键字是允许的,因为其他关键字计划用于未来的额外属性(例如,实例化选项)。

measure属性应用于一个impl块时,它应用于具有measure属性的每个方法。如果一个方法不需要额外的度量信息,它可以简单地用#[measure]注释,并将impl块的measure配置应用于它。

可以在impl块或方法上多次添加measure关键字,这将添加到应用的度量列表。添加相同的度量多次将导致名称冲突。

设计

Metered的自定义属性解析支持使用保留关键字和任意的Rust语法。代码已被提取到Synattra项目,该项目在Syn解析器之上提供了有用的方法,用于属性解析。

计量器的指标可以包裹任何代码片段,无论它们是异步块还是非异步块,使用卫生宏来模拟类似面向切面编程的方法。这段代码已经被提取到Aspect-rs项目中!

许可

许可协议为以下之一:

任选其一。

贡献

除非您明确声明,否则您有意提交并包含在作品中的任何贡献,根据Apache-2.0许可定义,应以上述双重许可形式,不附加任何额外条款或条件。

依赖项

约2.5MB
约53K SLoC