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
49,501 每月下载量
在 5 个包中(通过 metered)使用
39KB
868 行
metered-rs
为 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
自动触发不依赖于结果值的指标(影响InFlight
、ResponseTime
、Throughput
)
- 0.4.0:
- 向生成的结构体添加
allow(missing_docs)
(这允许在Rust代码中使用带有lint级别warn(missing_docs)或甚至deny(missing_docs)的metered结构体)(由@reyk贡献) - 为生成的注册表实现
Clear
(由@eliaslevy贡献) - 为
RefCell<HdrHistogram>
实现Histogram
和Clear
(由@eliaslevy贡献) - 引入一个具有微秒精度的
Instant
(由@eliaslevy贡献)- API破坏性更改:
Instant.elapsed_millis
重命名为elapsed_time
,并引入了一个新的关联常量,ONE_SEC
,用于指定一秒的瞬时单位。
- API破坏性更改:
- 通过重新导出使
AtomicTxPerSec
和TxPerSec
可见(由@eliaslevy贡献) - 将
StdInstant
作为TxPerSec
中T: Instant
的默认类型参数(由@eliaslevy贡献) - 修改HdrHistogram以与serde_prometheus一起工作(由@w4贡献)
- 请与 serde_prometheus 以及任何 HTTP 服务器一起使用。
- 依赖项更新
indexmap
: 1.1 -> 1.3hdrhistogram
: 6.3 -> 7.1parking_lot
: 0.9 -> 0.10
- 向生成的结构体添加
- 0.3.0:
- 修复了在
async
测量方法中保留 span 的问题。 - 更新了夜间构建样本以支持新语法和 Tokio 0.2-alpha(使用 std futures,需要 Rust >= 1.39,nightly 或非 nightly)
- 更新了依赖项以使用
syn
,proc-macro2
和quote
1.0
- 修复了在
- 0.2.2:
- 在
#measured
方法中的异步支持不再依赖于异步闭包,因此客户端代码不需要async_closure
功能门。 - 更新了依赖项版本
- 在
- 0.2.1:
- 在特定情况下,Serde 会为
PhantomData
标记中的ResponseTime
和Throughput
指标序列化 "nulls"。现在它们被明确排除。
- 在特定情况下,Serde 会为
- 0.2.0:
- 支持
.await
语法用户(不再需要await!()
)
- 支持
- 0.1.3:
- 修复了在
#[measure]
’ed 方法中的早期返回问题 - 移除了对 crate
AtomicRefCell
的使用,它有时会引发恐慌。 - 支持自定义注册表可见性。
- 支持
async
+await!()
宏用户。
- 修复了在
使用 Metered
Metered 提供了各种有用的指标,开箱即用。
HitCount
:一个计数器,跟踪代码被击中的次数。ErrorCount
:一个计数器,跟踪返回的错误数量 -- (适用于返回 stdResult
的任何表达式)InFlight
:一个仪表,跟踪活跃请求的数量ResponseTime
:由 HdrHistogram 支持的表达式持续时间的统计数据Throughput
:由 HdrHistogram 支持的表达式每秒钟被调用的次数的统计数据
这些指标通常应用于方法,使用提供的程序宏生成样板代码。
为了提高性能,这些库存指标可以自定义以使用非线程安全(!Sync
/!Send
)数据结构,但默认情况下会尽可能使用使用无锁策略实现的线程安全数据结构。这是一个为了在各种情况下提供默认值而做出的舒适选择。
Metered 设计为零开销抽象 -- 在这个意义上,高级舒适度不应超过手动添加指标。值得注意的是,库存指标在第一次初始化后 不会 分配内存。然而,它们在每个方法调用时都会触发,因此在热点代码路径中使用较轻的指标(例如 HitCount
)和在高级入口点中使用较重的指标(例如 Throughput
,ResponseTime
)可能很有趣。
如果您需要的指标缺失,或者您想自定义一个指标(例如,跟踪特定错误发生的次数,或根据您的返回类型做出反应),您可以通过实现 trait metered::metric::Metric
来简单地实现自己的指标。
计量器不使用静态变量或共享的全局状态。相反,它允许您使用所需的度量指标构建自己的度量注册表,或者可以使用方法属性为您生成度量注册表。计量器将为每个带有 metered
属性的 impl
块生成一个注册表,该注册表名称由 registry
参数提供。默认情况下,计量器将期望注册表以 self.metrics
的形式访问,但可以通过 registry_expr
属性参数来覆盖表达式。有关更多示例,请参阅演示。
计量器将生成从 Debug
和 serde::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);
}
}
在上面的片段中,我们将测量 HitCount
和 Throughput
。
这通过首先用 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 License 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确声明,否则您有意提交并包含在作品中的任何贡献,根据Apache-2.0许可定义,应以上述双重许可形式,不附加任何额外条款或条件。
依赖项
约2.5MB
约53K SLoC