48个版本 (5个稳定版)

1.4.0 2024年4月28日
1.3.0 2024年3月23日
1.2.0 2024年2月21日
1.0.0 2023年12月2日
0.2.1 2015年12月27日

#271性能分析

Download history 135276/week @ 2024-04-26 129949/week @ 2024-05-03 117488/week @ 2024-05-10 123924/week @ 2024-05-17 135496/week @ 2024-05-24 176149/week @ 2024-05-31 201877/week @ 2024-06-07 150890/week @ 2024-06-14 158506/week @ 2024-06-21 156205/week @ 2024-06-28 186028/week @ 2024-07-05 197881/week @ 2024-07-12 201045/week @ 2024-07-19 177387/week @ 2024-07-26 173165/week @ 2024-08-02 172494/week @ 2024-08-09

每月758,991次下载
19 个Crates中使用(17 个直接使用)

Apache-2.0/MIT

215KB
3K SLoC

Cadence

build status docs.rs crates.io Rust 1.60+

Cadence 文档

宏文档

适用于Rust的扩展Statsd客户端!

Cadence 是一种快速灵活的方法,可以从您的应用程序中发出Statsd指标。

特性

  • 支持 通过UDP(或可选的Unix套接字)向Statsd发出计数器、计时器、直方图、分布、仪表、测量器和集合。
  • 通过 MetricSink 特性支持替代后端。
  • 支持 Datadog 风格的指标标签。
  • 以简化发出指标的常见调用
  • 用于发送指标的一个简单而灵活的API。

安装

要在项目中使用 cadence,请将其添加到您的 Cargo.toml 文件中的依赖项中。

[dependencies]
cadence = "x.y.z"

这就足够了!

用法

以下展示了Cadence的一些使用示例。这些示例从简单开始,逐步展示如何在生产应用程序中使用Cadence。

简单使用

以下展示了Cadence的简单使用方法。在这个例子中,我们仅导入客户端,创建一个将写入某个虚拟指标服务器的实例,并发送一些指标。

use std::net::UdpSocket;
use cadence::prelude::*;
use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};
// Create client that will write to the given host over UDP.
//
// Note that you'll probably want to actually handle any errors creating
// the client when you use it for real in your application. We're just
// using .unwrap() here since this is an example!
let host = ("metrics.example.com", DEFAULT_PORT);
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let sink = UdpMetricSink::from(host, socket).unwrap();
let client = StatsdClient::from_sink("my.metrics", sink);

// Emit metrics!
client.incr("some.counter");
client.time("some.methodCall", 42);
client.gauge("some.thing", 7);
client.meter("some.value", 5);

缓冲UDP接收器

虽然通过UDP发送指标非常快,但频繁网络调用的开销可能会开始累积。这尤其适用于您正在编写一个高性能应用程序,该应用程序发出大量指标。

为确保指标不会干扰您应用程序的性能,您可能需要使用一个在发送前先缓冲多个指标的单个网络操作的MetricSink实现。为此,有BufferedUdpMetricSink。下面给出了使用此接收器的示例。

use std::net::UdpSocket;
use cadence::prelude::*;
use cadence::{StatsdClient, BufferedUdpMetricSink, DEFAULT_PORT};

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.set_nonblocking(true).unwrap();

let host = ("metrics.example.com", DEFAULT_PORT);
let sink = BufferedUdpMetricSink::from(host, socket).unwrap();
let client = StatsdClient::from_sink("my.prefix", sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);
client.incr("some.event");

如您所见,使用此缓冲UDP接收器并不比使用常规的非缓冲UDP接收器复杂。

此接收器的唯一缺点是,直到缓冲区满,指标不会写入Statsd服务器。如果您有一个不断发出指标的繁忙应用程序,这不应该是个问题。然而,如果您的应用程序只是偶尔发出指标,这个接收器可能会导致指标延迟一段时间,直到缓冲区填满。在这种情况下,使用UdpMetricSink可能更有意义,因为它不做任何缓冲。

异步指标接收器队列

为确保发出指标不会干扰您应用程序的性能(尽管发出指标通常非常快),确保指标在应用程序线程之外的不同线程中发出可能是个好主意。

为此,有QueuingMetricSink。这个接收器允许您将任何其他指标接收器包装起来,并通过队列将其发送到它,因为它在另一个线程中异步发出指标,与应用程序流程不同。

包装接收器的需求是它是线程安全的,这意味着它实现了SendSync特性。如果您在使用来自Cadence的另一个接收器与QueuingMetricSink,您不必担心:它们都是线程安全的。

下面给出了使用QueuingMetricSink包装缓冲UDP指标接收器的示例。这是在生产环境中使用Cadence的首选方式。

use std::net::UdpSocket;
use cadence::prelude::*;
use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink, DEFAULT_PORT};

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.set_nonblocking(true).unwrap();

let host = ("metrics.example.com", DEFAULT_PORT);
let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
let queuing_sink = QueuingMetricSink::from(udp_sink);
let client = StatsdClient::from_sink("my.prefix", queuing_sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);

在上面的示例中,我们使用了队列接收器的默认构造函数,它创建了一个无界队列,没有最大大小,将客户端发送指标的线程连接到包装接收器运行的背景线程。如果您想创建一个有界队列并具有最大大小,您可以使用with_capacity构造函数。下面给出了一个示例。

use std::net::UdpSocket;
use cadence::prelude::*;
use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink,
              DEFAULT_PORT};

// Queue with a maximum capacity of 128K elements
const QUEUE_SIZE: usize = 128 * 1024;

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.set_nonblocking(true).unwrap();

let host = ("metrics.example.com", DEFAULT_PORT);
let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
let queuing_sink = QueuingMetricSink::with_capacity(udp_sink, QUEUE_SIZE);
let client = StatsdClient::from_sink("my.prefix", queuing_sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);
client.incr("some.event");

使用具有容量设置的QueuingMetricSink意味着当队列满时,通过StatsdClient发出指标的尝试将失败。虽然这很糟糕,但如果您使用的是无界队列,未发送的指标会逐渐消耗更多和更多的内存,直到您的应用程序耗尽所有内存。

使用无界队列意味着指标发送可以吸收发送指标的速度减慢,直到您的应用程序耗尽内存。使用有界队列将限制发送指标在您的应用程序中使用的内存量。这是Cadence用户必须自己决定的权衡。

还可以为QueuingMetricSink提供错误处理程序,以便在包装接收器因任何原因无法发送指标时调用。

use std::net::UdpSocket;
use cadence::prelude::*;
use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink,
              DEFAULT_PORT};

// Queue with a maximum capacity of 128K elements
const QUEUE_SIZE: usize = 128 * 1024;

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.set_nonblocking(true).unwrap();

let host = ("metrics.example.com", DEFAULT_PORT);
let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
let queuing_sink = QueuingMetricSink::builder()
    .with_capacity(QUEUE_SIZE)
    .with_error_handler(|e| {
         eprintln!("Error while sending metrics: {:?}", e);
    })
    .build(udp_sink);
let client = StatsdClient::from_sink("my.prefix", queuing_sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);

使用带标签的

通过使用Cadence StatsdClient结构中的每个_with_tags方法添加指标标签。下面给出了使用这些方法的示例。请注意,标签是Statsd协议的扩展,因此可能不是所有服务器都支持。

有关更多信息,请参阅Datadog文档

use cadence::prelude::*;
use cadence::{Metric, StatsdClient, NopMetricSink};

let client = StatsdClient::from_sink("my.prefix", NopMetricSink);

let res = client.count_with_tags("my.counter", 29)
    .with_tag("host", "web03.example.com")
    .with_tag_value("beta-test")
    .try_send();

assert_eq!(
    concat!(
        "my.prefix.my.counter:29|c|#",
        "host:web03.example.com,",
        "beta-test"
    ),
    res.unwrap().as_metric_str()
);

默认标签

可以使用构建器在构建时向StatsdClient添加默认标签。默认标签会添加到StatsdClient发出的每个指标中,无需在构建客户端后进行任何额外操作。请注意,标签是Statsd协议的扩展,因此可能不是所有服务器都支持。

有关更多信息,请参阅Datadog文档

use cadence::prelude::*;
use cadence::{Metric, StatsdClient, NopMetricSink};

let client = StatsdClient::builder("my.prefix", NopMetricSink)
    .with_tag("env", "prod")
    .with_tag("app", "auth")
    .build();

let res = client.count_with_tags("my.counter", 29)
    .with_tag("host", "web03.example.com")
    .with_tag_value("beta-test")
    .try_send();

assert_eq!(
    concat!(
        "my.prefix.my.counter:29|c|#",
        "env:prod,",
        "app:auth,",
        "host:web03.example.com,",
        "beta-test"
    ),
    res.unwrap().as_metric_str()
);

值打包

值打包允许将多个值作为单个指标发送到直方图、分布和计时器类型。Cadence客户端接受直方图、分布和计时器方法的Vec<T>,并将多个值格式化为以下描述。请注意,此功能是Datadog扩展,因此可能不支持您的服务器。它由Datadog代理的版本>=v6.25.0 && <v7.0.0>=v7.25.0支持。

打包指标具有以下格式

<METRIC_NAME>:<VALUE1>:<VALUE2>:<VALUE3>|<TYPE>|#<TAG_KEY_1>:<TAG_VALUE_1>,<TAG_2>`

有关更多信息,请参阅Datadog 文档

use cadence::prelude::*;
use cadence::{Metric, StatsdClient, NopMetricSink};

let client = StatsdClient::from_sink("my.prefix", NopMetricSink);

let res = client.distribution_with_tags("my.distribution", vec![29, 30, 31, 32])
    .with_tag("host", "web03.example.com")
    .with_tag_value("beta-test")
    .try_send();

assert_eq!(
    concat!(
        "my.prefix.my.distribution:29:30:31:32|d|#",
        "host:web03.example.com,",
        "beta-test"
    ),
    res.unwrap().as_metric_str()
);

实现的特性

Cadence StatsdClient 结构体使用的每个发送指标的方法都实现为特性。还有一个特性将所有这些其他特性组合在一起。如果我们愿意,我们可以只使用其中一个特性类型来引用客户端实例。如果您想在单元测试代码时用模拟版本替换实际Cadence客户端,或者想将客户端使用的所有实现细节抽象化,这可能会很有用。

这些特性都导出在预定义模块中。它们也在主模块中可用,但通常不会这样使用。

use cadence::prelude::*;
use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};

pub struct User {
    id: u64,
    username: String,
    email: String
}

// Here's a simple DAO (Data Access Object) that doesn't do anything but
// uses a metric client to keep track of the number of times the
// 'getUserById' method gets called.
pub struct MyUserDao {
    metrics: Box<dyn MetricClient>
}

impl MyUserDao {
    // Create a new instance that will use the StatsdClient
    pub fn new<T: MetricClient + 'static>(metrics: T) -> MyUserDao {
        MyUserDao { metrics: Box::new(metrics) }
    }

    /// Get a new user by their ID
    pub fn get_user_by_id(&self, id: u64) -> Option<User> {
        self.metrics.incr("getUserById");
        None
    }
}

// Create a new Statsd client that writes to "metrics.example.com"
let host = ("metrics.example.com", DEFAULT_PORT);
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let sink = UdpMetricSink::from(host, socket).unwrap();
let metrics = StatsdClient::from_sink("counter.example", sink);

// Create a new instance of the DAO that will use the client
let dao = MyUserDao::new(metrics);

// Try to lookup a user by ID!
match dao.get_user_by_id(123) {
    Some(u) => println!("Found a user!"),
    None => println!("No user!")
};

静默指标发送和错误处理

在发送指标时,有时您并不关心尝试发送它的结果,或者您可能只是不想与代码的其他部分一起处理它。为了处理这种情况,Cadence允许您设置默认的错误处理器。当发送指标出现错误时,将调用此处理器,以便调用代码不必处理它们。

以下提供了一个配置错误处理器及其可能被调用的示例。

use cadence::prelude::*;
use cadence::{MetricError, StatsdClient, NopMetricSink};

fn my_error_handler(err: MetricError) {
    println!("Metric error! {}", err);
}

let client = StatsdClient::builder("prefix", NopMetricSink)
    .with_error_handler(my_error_handler)
    .build();

// When sending metrics via the `MetricBuilder` used for assembling tags,
// callers may opt into sending metrics quietly via the `.send()` method
// as opposed to the `.try_send()` method
client.count_with_tags("some.counter", 42)
    .with_tag("region", "us-east-2")
    .send();

自定义指标接收器

Cadence StatsdClient 使用MetricSink特性的实现将指标发送到指标服务器。Cadence库的大多数用户可能想使用包装BufferedMetricSink实例的QueuingMetricSink

但是,也许您想做一些现有接收器不支持的事情。以下是一个创建自定义接收器的示例。

use std::io;
use cadence::prelude::*;
use cadence::{StatsdClient, MetricSink, DEFAULT_PORT};

pub struct MyMetricSink;

impl MetricSink for MyMetricSink {
    fn emit(&self, metric: &str) -> io::Result<usize> {
        // Your custom metric sink implementation goes here!
        Ok(0)
    }
}

let sink = MyMetricSink;
let client = StatsdClient::from_sink("my.prefix", sink);

client.count("my.counter.thing", 42);
client.time("my.method.time", 25);
client.incr("some.other.counter");

自定义UDP套接字

大多数Cadence StatsdClient用户将使用它通过UDP套接字发送指标。如果您需要自定义套接字,例如您想使用阻塞模式的套接字但设置写入超时,您可以像以下演示的那样做。

use std::net::UdpSocket;
use std::time::Duration;
use cadence::prelude::*;
use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.set_write_timeout(Some(Duration::from_millis(1))).unwrap();

let host = ("metrics.example.com", DEFAULT_PORT);
let sink = UdpMetricSink::from(host, socket).unwrap();
let client = StatsdClient::from_sink("my.prefix", sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);
client.incr("some.event");
client.set("users.uniques", 42);

Unix套接字

Cadence还支持使用UnixMetricSinkBufferedUnixMetricSink与Unix数据报套接字一起使用。Unix套接字可用于将指标发送到与您的应用程序在同一台机器(物理机器、虚拟机、Pod中的容器)上运行的服务器或代理。Unix套接字与UDP套接字在几个重要方面类似

  • 在不存在或不被监听的套接字上发送指标会导致错误。
  • 在已连接的套接字上发送的指标保证将被投递(即它们是可靠的,而不是UDP套接字)。然而,由于各种环境和服务器特定的原因,指标仍然可能不会被服务器读取。

以下提供了一个使用接收器的示例。

use std::os::unix::net::UnixDatagram;
use cadence::prelude::*;
use cadence::{StatsdClient, BufferedUnixMetricSink};

let socket = UnixDatagram::unbound().unwrap();
socket.set_nonblocking(true).unwrap();
let sink = BufferedUnixMetricSink::from("/run/statsd.sock", socket);
let client = StatsdClient::from_sink("my.prefix", sink);

client.count("my.counter.thing", 29);
client.time("my.service.call", 214);
client.incr("some.event");
client.set("users.uniques", 42);

注意:此功能仅在Unix平台上(Linux、BSD、MacOS)可用。

其他

有关Cadence的更多信息,请参阅仓库根目录中的README

依赖项

~345KB