27 个不稳定版本
0.15.0 | 2024 年 8 月 6 日 |
---|---|
0.14.0 | 2021 年 6 月 15 日 |
0.13.0 | 2020 年 5 月 10 日 |
0.11.0 | 2019 年 10 月 30 日 |
0.2.0 | 2017 年 11 月 30 日 |
#89 in 操作系统
每月 233 次下载
170KB
4K SLoC
用 Rust 编写 Collectd 插件
Collectd 是一种常见的系统统计收集守护进程。这个 Rust 库利用 collectd 的动态加载插件的能力,创建了一个易于使用且成本极低的抽象 API,用于与 collectd 接口。支持 collectd 5.7 及以上版本。
特性
- 提交/接收值、记录时无需进行不必要的分配
- 注册多个插件实例
- 通过 Serde 自动反序列化插件配置(可选退出)
- 部署:针对 collectd 版本进行编译,然后通过 scp 传输到服务器
- 引用的 Rust 库是静态链接的
- 由于 Rust 编译器的帮助,有助于编写线程安全的插件
用法
添加到您的 Cargo.toml
[dependencies]
collectd-plugin = "0.15.0"
Serde 支持默认启用配置解析。
快速入门
以下是一个完整的插件,它作为 负载 值的示例报告到 collectd,因为它注册了一个 READ
钩子。有关重新实现 collectd 自身负载插件的实现,请参阅 examples/load
use collectd_plugin::{
collectd_plugin, ConfigItem, Plugin, PluginCapabilities, PluginManager, PluginRegistration,
Value, ValueListBuilder,
};
use std::error;
#[derive(Default)]
struct MyPlugin;
// A manager decides the name of the family of plugins and also registers one or more plugins based
// on collectd's configuration files
impl PluginManager for MyPlugin {
// A plugin needs a unique name to be referenced by collectd
fn name() -> &'static str {
"myplugin"
}
// Our plugin might have configuration section in collectd.conf, which will be passed here if
// present. Our contrived plugin doesn't care about configuration so it returns only a single
// plugin (itself).
fn plugins(
_config: Option<&[ConfigItem<'_>]>,
) -> Result<PluginRegistration, Box<dyn error::Error>> {
Ok(PluginRegistration::Single(Box::new(MyPlugin)))
}
}
impl Plugin for MyPlugin {
// We define that our plugin will only be reporting / submitting values to writers
fn capabilities(&self) -> PluginCapabilities {
PluginCapabilities::READ
}
fn read_values(&self) -> Result<(), Box<dyn error::Error>> {
// Create a list of values to submit to collectd. We'll be sending in a vector representing the
// "load" type. Short-term load is first (15.0) followed by mid-term and long-term. The number
// of values that you submit at a time depends on types.db in collectd configurations
let values = vec![Value::Gauge(15.0), Value::Gauge(10.0), Value::Gauge(12.0)];
// Submit our values to collectd. A plugin can submit any number of times.
ValueListBuilder::new(Self::name(), "load")
.values(&values)
.submit()?;
Ok(())
}
}
// We pass in our plugin manager type
collectd_plugin!(MyPlugin);
动机
扩展 collectd 的主要有五种方式
- 针对 C API 编写插件:
<collectd/core/daemon/plugin.h>
- 为 collectd-python 编写插件
- 为 collectd-java 编写插件
- 为 exec 插件 编写 cli
- 编写将 写入 UNIX 套接字 的服务
以及我的想法
- 我没有足够的信心编写没有泄漏的C代码,而且还没有一个非常好的C包管理器。
- Python和Java不是自包含的,不一定部署在服务器上,比较重,我怀疑维护工作不如C API重要。
- exec插件成本高昂,因为它为每个收集项创建一个新的进程。
- 根据具体情况,写入Unix套接字可能是一个不错的选择,但我喜欢部署的简便性以及collectd的集成——无需重新发明日志方案、配置和系统初始化文件。
Rust的生态系统、包管理器、C ffi、单文件动态库和优化代码使它成为了一个自然的选择。
构建
为确保构建成功,请根据您的项目Cargo文件进行调整。
[lib]
crate-type = ["cdylib"]
name = "<your plugin name>"
[features]
bindgen = ["collectd-plugin/bindgen"]
default = []
collectd-rust-plugin
假定与5.7
-兼容的API(5.7
至少适用于5.12
)。这可以通过以下方式配置:- 使用
bindgen
特性,将COLLECTD_PATH
指向collectd的根git目录- 如果安装了
collectd-dev
,bindgen
特性也将生效
- 如果安装了
- 将来,当
collectd-rust-plugin
重新引入对不兼容API的不同collectd版本的编译支持时,可以使用COLLECTD_VERSION
- 当系统上存在collectd时,版本是通过执行
collectd -h
得到的
- 使用
- collectd期望插件不以
lib
为前缀,因此cp target/debug/libmyplugin.so /usr/lib/collectd/myplugin.so
- 将
LoadPlugin myplugin
添加到collectd.conf
插件配置
examples/load中的加载插件演示了如何将配置值公开给collectd。
# In this example configuration we provide short and long term load and leave
# Mid to the default value. Yes, this is very much contrived
<Plugin loadrust>
ReportRelative true
</Plugin>
基准测试开销
为了衡量在写入和报告值时适配collectd的数据类型时的开销
cargo bench --features stub
如果您想使用我机器上的计时
- 创建并提交一个
ValueListBuilder
需要60纳秒 - 为写入值的插件创建一个
ValueList
需要130纳秒
除非您在每个时间间隔内报告或写入数百万个指标(在这种情况下,您很可能会遇到早期瓶颈),否则您会没事的。
插件
您使用collectd-rust-plugin吗?请随意将您的插件添加到列表中。
- pg-collectd:一个替代的、有自己看法的PostgreSQL collectd写入器
依赖
~3.5–6MB
~105K SLoC