#![cfg_attr(feature = "unstable", feature(test))]
#![deny(
warnings,
missing_debug_implementations,
missing_copy_implementations,
missing_docs
)]
extern crate chrono;
use chrono::Utc;
use std::borrow::Cow;
use std::future::Future;
use std::net::UdpSocket;
#[cfg(unix)]
use std::os::unix::net::UnixDatagram;
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Mutex};
use std::thread;
use std::time::Duration;
pub use self::error::DogstatsdError;
use self::metrics::*;
pub use self::metrics::{EventAlertType, EventPriority, ServiceCheckOptions, ServiceStatus};
mod error;
mod metrics;
pub type DogstatsdResult = Result<(), DogstatsdError>;
const DEFAULT_FROM_ADDR: &str = "0.0.0.0:0";
const DEFAULT_TO_ADDR: &str = "127.0.0.1:8125";
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct BatchingOptions {
pub max_buffer_size: usize,
pub max_time: Duration,
pub max_retry_attempts: usize,
pub initial_retry_delay: u64,
}
#[derive(Debug, PartialEq)]
pub struct Options {
pub from_addr: String,
pub to_addr: String,
pub namespace: String,
pub default_tags: Vec<String>,
pub socket_path: Option<String>,
pub batching_options: Option<BatchingOptions>,
}
impl Default for Options {
fn default() -> Self {
Options {
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
default_tags: vec![],
socket_path: None,
batching_options: None,
}
}
}
impl Options {
pub fn new(
from_addr: &str,
to_addr: &str,
namespace: &str,
default_tags: Vec<String>,
socket_path: Option<String>,
batching_options: Option<BatchingOptions>,
) -> Self {
Options {
from_addr: from_addr.into(),
to_addr: to_addr.into(),
namespace: namespace.into(),
default_tags,
socket_path,
batching_options,
}
}
fn merge_with_system_tags(default_tags: Vec<String>) -> Vec<String> {
let mut merged_tags = default_tags;
if !merged_tags.iter().any(|tag| tag.starts_with("env:")) {
if let Ok(env) = std::env::var("DD_ENV") {
merged_tags.push(format!("env:{}", env));
}
}
if !merged_tags.iter().any(|tag| tag.starts_with("service:")) {
if let Ok(service) = std::env::var("DD_SERVICE") {
merged_tags.push(format!("service:{}", service));
}
}
if !merged_tags.iter().any(|tag| tag.starts_with("version:")) {
if let Ok(version) = std::env::var("DD_VERSION") {
merged_tags.push(format!("version:{}", version));
}
}
merged_tags
}
}
#[derive(Default, Debug)]
pub struct OptionsBuilder {
from_addr: Option<String>,
to_addr: Option<String>,
namespace: Option<String>,
default_tags: Vec<String>,
socket_path: Option<String>,
batching_options: Option<BatchingOptions>,
}
impl OptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn from_addr(&mut self, from_addr: String) -> &mut OptionsBuilder {
self.from_addr = Some(from_addr);
self
}
pub fn to_addr(&mut self, to_addr: String) -> &mut OptionsBuilder {
self.to_addr = Some(to_addr);
self
}
pub fn namespace(&mut self, namespace: String) -> &mut OptionsBuilder {
self.namespace = Some(namespace);
self
}
pub fn default_tag(&mut self, default_tag: String) -> &mut OptionsBuilder {
self.default_tags.push(default_tag);
self
}
pub fn socket_path(&mut self, socket_path: Option<String>) -> &mut OptionsBuilder {
self.socket_path = socket_path;
self
}
pub fn batching_options(&mut self, batching_options: BatchingOptions) -> &mut OptionsBuilder {
self.batching_options = Some(batching_options);
self
}
pub fn build(&self) -> Options {
Options::new(
self.from_addr
.as_ref()
.unwrap_or(&String::from(DEFAULT_FROM_ADDR)),
self.to_addr
.as_ref()
.unwrap_or(&String::from(DEFAULT_TO_ADDR)),
self.namespace.as_ref().unwrap_or(&String::default()),
self.default_tags.to_vec(),
self.socket_path.clone(),
self.batching_options,
)
}
}
#[derive(Debug)]
enum SocketType {
Udp(UdpSocket),
#[cfg(unix)]
Uds(UnixDatagram),
BatchableUdp(Mutex<Sender<batch_processor::Message>>),
#[cfg(unix)]
BatchableUds(Mutex<Sender<batch_processor::Message>>),
}
#[derive(Debug)]
pub struct Client {
socket: SocketType,
from_addr: String,
to_addr: String,
namespace: String,
default_tags: Vec<u8>,
}
impl PartialEq for Client {
fn eq(&self, other: &Self) -> bool {
self.from_addr == other.from_addr
&& self.to_addr == other.to_addr
&& self.namespace == other.namespace
&& self.default_tags == other.default_tags
}
}
impl Drop for Client {
fn drop(&mut self) {
match &self.socket {
SocketType::BatchableUdp(tx_channel) => {
let _ = tx_channel
.lock()
.unwrap()
.send(batch_processor::Message::Shutdown);
}
#[cfg(unix)]
SocketType::BatchableUds(tx_channel) => {
let _ = tx_channel
.lock()
.unwrap()
.send(batch_processor::Message::Shutdown);
}
_ => {}
}
}
}
impl Client {
pub fn new(options: Options) -> Result<Self, DogstatsdError> {
let fn_create_tx_channel = |socket: SocketType,
batching_options: BatchingOptions,
to_addr: String,
socket_path: Option<String>|
-> Mutex<Sender<batch_processor::Message>> {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
batch_processor::process_events(batching_options, to_addr, socket, socket_path, rx);
});
Mutex::from(tx)
};
let socket = if options.socket_path.is_some() {
#[cfg(unix)]
{
let socket_path = options
.socket_path
.clone()
.expect("checked is_some above");
let mut uds_socket = UnixDatagram::unbound()?;
match uds_socket.connect(socket_path.clone()) {
Ok(socket) => socket,
Err(e) => {
println!(
"Couldn't connect to uds socket.. attempting to re-create by binding directly: {e:?}"
);
uds_socket = UnixDatagram::bind(socket_path.clone())?;
}
};
uds_socket.set_nonblocking(true)?;
let wrapped_socket = SocketType::Uds(uds_socket);
if let Some(batching_options) = options.batching_options {
SocketType::BatchableUds(fn_create_tx_channel(
wrapped_socket,
batching_options,
options.to_addr.clone(),
Some(socket_path),
))
} else {
wrapped_socket
}
}
#[cfg(not(unix))]
{
return Err(DogstatsdError::from(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Unix domain sockets are not supported on this platform",
)));
}
} else {
let wrapped_socket = SocketType::Udp(UdpSocket::bind(&options.from_addr)?);
if let Some(batching_options) = options.batching_options {
SocketType::BatchableUdp(fn_create_tx_channel(
wrapped_socket,
batching_options,
options.to_addr.clone(),
None,
))
} else {
wrapped_socket
}
};
let default_tags = Options::merge_with_system_tags(options.default_tags);
Ok(Client {
socket,
from_addr: options.from_addr,
to_addr: options.to_addr,
namespace: options.namespace,
default_tags: default_tags.join(",").into_bytes(),
})
}
pub fn incr<'a, I, S, T>(&self, stat: S, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&CountMetric::Incr(stat.into().as_ref(), 1), tags)
}
pub fn incr_by_value<'a, I, S, T>(&self, stat: S, value: i64, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&CountMetric::Incr(stat.into().as_ref(), value), tags)
}
pub fn decr<'a, I, S, T>(&self, stat: S, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&CountMetric::Decr(stat.into().as_ref(), 1), tags)
}
pub fn decr_by_value<'a, I, S, T>(&self, stat: S, value: i64, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&CountMetric::Decr(stat.into().as_ref(), value), tags)
}
pub fn count<'a, I, S, T>(&self, stat: S, count: i64, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&CountMetric::Arbitrary(stat.into().as_ref(), count), tags)
}
pub fn time<'a, F, O, I, S, T>(
&self,
stat: S,
tags: I,
block: F,
) -> Result<O, (O, DogstatsdError)>
where
F: FnOnce() -> O,
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
let start_time = Utc::now();
let output = block();
let end_time = Utc::now();
let stat = stat.into();
let metric = TimeMetric::new(stat.as_ref(), &start_time, &end_time);
match self.send(&metric, tags) {
Ok(()) => Ok(output),
Err(error) => Err((output, error)),
}
}
pub async fn async_time<'a, Fn, Fut, O, I, S, T>(
&self,
stat: S,
tags: I,
block: Fn,
) -> Result<O, (O, DogstatsdError)>
where
Fn: FnOnce() -> Fut,
Fut: Future<Output = O>,
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
let start_time = Utc::now();
let output = block().await;
let end_time = Utc::now();
let stat = stat.into();
match self.send(
&TimeMetric::new(stat.as_ref(), &start_time, &end_time),
tags,
) {
Ok(()) => Ok(output),
Err(error) => Err((output, error)),
}
}
pub fn timing<'a, I, S, T>(&self, stat: S, ms: i64, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(&TimingMetric::new(stat.into().as_ref(), ms), tags)
}
pub fn gauge<'a, I, S, SS, T>(&self, stat: S, val: SS, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(
&GaugeMetric::new(stat.into().as_ref(), val.into().as_ref()),
tags,
)
}
pub fn histogram<'a, I, S, SS, T>(&self, stat: S, val: SS, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(
&HistogramMetric::new(stat.into().as_ref(), val.into().as_ref()),
tags,
)
}
pub fn distribution<'a, I, S, SS, T>(&self, stat: S, val: SS, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(
&DistributionMetric::new(stat.into().as_ref(), val.into().as_ref()),
tags,
)
}
pub fn set<'a, I, S, SS, T>(&self, stat: S, val: SS, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(
&SetMetric::new(stat.into().as_ref(), val.into().as_ref()),
tags,
)
}
pub fn service_check<'a, I, S, T>(
&self,
stat: S,
val: ServiceStatus,
tags: I,
options: Option<ServiceCheckOptions>,
) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
T: AsRef<str>,
{
let unwrapped_options = options.unwrap_or_default();
self.send(
&ServiceCheck::new(stat.into().as_ref(), val, unwrapped_options),
tags,
)
}
pub fn event<'a, I, S, SS, T>(&self, title: S, text: SS, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
self.send(
&Event::new(title.into().as_ref(), text.into().as_ref()),
tags,
)
}
pub fn event_with_options<'a, I, S, SS, T>(
&self,
title: S,
text: SS,
tags: I,
options: Option<EventOptions<'a>>,
) -> DogstatsdResult
where
I: IntoIterator<Item = T>,
S: Into<Cow<'a, str>>,
SS: Into<Cow<'a, str>>,
T: AsRef<str>,
{
let title_owned = title.into();
let text_owned = text.into();
let mut event = Event::new(title_owned.as_ref(), text_owned.as_ref());
if let Some(options) = options {
if let Some(timestamp) = options.timestamp {
event = event.with_timestamp(timestamp);
}
if let Some(hostname) = options.hostname {
event = event.with_hostname(hostname);
}
if let Some(aggregation_key) = options.aggregation_key {
event = event.with_aggregation_key(aggregation_key);
}
if let Some(priority) = options.priority {
event = event.with_priority(priority);
}
if let Some(source_type_name) = options.source_type_name {
event = event.with_source_type_name(source_type_name);
}
if let Some(alert_type) = options.alert_type {
event = event.with_alert_type(alert_type);
}
}
self.send(&event, tags)
}
fn send<I, M, S>(&self, metric: &M, tags: I) -> DogstatsdResult
where
I: IntoIterator<Item = S>,
M: Metric,
S: AsRef<str>,
{
let formatted_metric = format_for_send(metric, &self.namespace, tags, &self.default_tags);
match &self.socket {
SocketType::Udp(socket) => {
socket.send_to(formatted_metric.as_slice(), &self.to_addr)?;
}
#[cfg(unix)]
SocketType::Uds(socket) => {
socket.send(formatted_metric.as_slice())?;
}
SocketType::BatchableUdp(tx_channel) => {
tx_channel
.lock()
.expect("Mutex poisoned...")
.send(batch_processor::Message::Data(formatted_metric))
.unwrap_or_else(|error| {
println!("Exception occurred when writing to channel: {:?}", error);
});
}
#[cfg(unix)]
SocketType::BatchableUds(tx_channel) => {
tx_channel
.lock()
.expect("Mutex poisoned...")
.send(batch_processor::Message::Data(formatted_metric))
.unwrap_or_else(|error| {
println!("Exception occurred when writing to channel: {:?}", error);
});
}
}
Ok(())
}
}
#[derive(Default, Clone, Copy, Debug)]
pub struct EventOptions<'a> {
pub timestamp: Option<u64>,
pub hostname: Option<&'a str>,
pub aggregation_key: Option<&'a str>,
pub priority: Option<EventPriority>,
pub source_type_name: Option<&'a str>,
pub alert_type: Option<EventAlertType>,
}
impl<'a> EventOptions<'a> {
pub fn new() -> Self {
EventOptions {
timestamp: None,
hostname: None,
aggregation_key: None,
priority: None,
source_type_name: None,
alert_type: None,
}
}
pub fn with_timestamp(mut self, timestamp: u64) -> Self {
self.timestamp = Some(timestamp);
self
}
pub fn with_hostname(mut self, hostname: &'a str) -> Self {
self.hostname = Some(hostname);
self
}
pub fn with_aggregation_key(mut self, aggregation_key: &'a str) -> Self {
self.aggregation_key = Some(aggregation_key);
self
}
pub fn with_priority(mut self, priority: EventPriority) -> Self {
self.priority = Some(priority);
self
}
pub fn with_source_type_name(mut self, source_type_name: &'a str) -> Self {
self.source_type_name = Some(source_type_name);
self
}
pub fn with_alert_type(mut self, alert_type: EventAlertType) -> Self {
self.alert_type = Some(alert_type);
self
}
}
mod batch_processor {
use std::sync::mpsc::Receiver;
use std::time::SystemTime;
use retry::{delay::jitter, delay::Exponential, retry};
use crate::{BatchingOptions, SocketType};
pub(crate) enum Message {
Data(Vec<u8>),
Shutdown,
}
fn send_to_socket_with_retries(
batching_options: &BatchingOptions,
socket: &SocketType,
data: &Vec<u8>,
to_addr: &String,
_socket_path: &Option<String>,
) {
retry(
Exponential::from_millis(batching_options.initial_retry_delay)
.map(jitter)
.take(batching_options.max_retry_attempts),
|| -> Result<(), std::io::Error> {
match socket {
SocketType::Udp(socket) => {
socket.send_to(data.as_slice(), to_addr)?;
}
#[cfg(unix)]
SocketType::Uds(socket) => {
if let Err(error) = socket.send(data.as_slice()) {
let socket_path_unwrapped = _socket_path
.as_ref()
.expect("Only invoked if socket path is defined.");
socket.connect(socket_path_unwrapped)?;
return Err(error);
}
}
SocketType::BatchableUdp(_tx_channel) => {
panic!("Logic Error - socket type should not be batchable.");
}
#[cfg(unix)]
SocketType::BatchableUds(_tx_channel) => {
panic!("Logic Error - socket type should not be batchable.");
}
}
Ok(())
},
)
.unwrap_or_else(|error| {
println!(
"Failed to send within retry policy... Dropping metrics: {:?}",
error
)
});
}
pub(crate) fn process_events(
batching_options: BatchingOptions,
to_addr: String,
socket: SocketType,
socket_path: Option<String>,
rx: Receiver<Message>,
) {
let mut last_updated = SystemTime::now();
let mut buffer: Vec<u8> = vec![];
loop {
match rx.recv() {
Ok(Message::Data(data)) => {
for ch in data {
buffer.push(ch);
}
buffer.push(b'\n');
let current_time = SystemTime::now();
if buffer.len() >= batching_options.max_buffer_size
|| last_updated + batching_options.max_time < current_time
{
send_to_socket_with_retries(
&batching_options,
&socket,
&buffer,
&to_addr,
&socket_path,
);
buffer.clear();
last_updated = current_time;
}
}
Ok(Message::Shutdown) => {
send_to_socket_with_retries(
&batching_options,
&socket,
&buffer,
&to_addr,
&socket_path,
);
buffer.clear();
}
Err(e) => {
println!("Exception occurred when reading from channel: {:?}", e);
break;
}
}
}
}
}
#[cfg(test)]
mod tests {
use metrics::GaugeMetric;
use serial_test::serial;
use super::*;
#[test]
fn test_options_default() {
let options = Options::default();
let expected_options = Options {
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
..Default::default()
};
assert_eq!(expected_options, options)
}
#[test]
fn test_options_builder_none() {
let options = OptionsBuilder::new().build();
let expected_options = Options {
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
..Default::default()
};
assert_eq!(expected_options, options);
}
#[test]
fn teset_options_builder_all() {
let options = OptionsBuilder::new()
.from_addr("127.0.0.2:0".into())
.to_addr("127.0.0.2:8125".into())
.namespace("mynamespace".into())
.default_tag(String::from("tag1:tag1val"))
.build();
let expected_options = Options {
from_addr: "127.0.0.2:0".into(),
to_addr: "127.0.0.2:8125".into(),
namespace: "mynamespace".into(),
default_tags: vec!["tag1:tag1val".into()].to_vec(),
socket_path: None,
batching_options: None,
};
assert_eq!(expected_options, options);
}
#[test]
#[serial]
fn test_new() {
let client = Client::new(Options::default()).unwrap();
let expected_client = Client {
socket: SocketType::Udp(UdpSocket::bind(DEFAULT_FROM_ADDR).unwrap()),
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
default_tags: String::new().into_bytes(),
};
assert_eq!(expected_client, client)
}
#[test]
#[serial]
fn test_new_default_tags() {
let options = Options::new(
DEFAULT_FROM_ADDR,
DEFAULT_TO_ADDR,
"",
vec![String::from("tag1:tag1val")],
None,
None,
);
let client = Client::new(options).unwrap();
let expected_client = Client {
socket: SocketType::Udp(UdpSocket::bind(DEFAULT_FROM_ADDR).unwrap()),
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
default_tags: String::from("tag1:tag1val").into_bytes(),
};
assert_eq!(expected_client, client)
}
#[test]
#[serial]
fn test_system_tags() {
let options = Options::new(
DEFAULT_FROM_ADDR,
DEFAULT_TO_ADDR,
"",
vec![String::from("tag1:tag1val"), String::from("version:0.0.2")],
None,
None,
);
let client = with_default_system_tags(|| Client::new(options).unwrap());
dbg!(String::from_utf8_lossy(client.default_tags.as_ref()));
let expected_client = Client {
socket: SocketType::Udp(UdpSocket::bind(DEFAULT_FROM_ADDR).unwrap()),
from_addr: DEFAULT_FROM_ADDR.into(),
to_addr: DEFAULT_TO_ADDR.into(),
namespace: String::new(),
default_tags: String::from("tag1:tag1val,version:0.0.2,env:production,service:service")
.into_bytes(),
};
assert_eq!(expected_client, client)
}
#[test]
fn test_send() {
let options = Options::new("127.0.0.1:9001", "127.0.0.1:9002", "", vec![], None, None);
let client = Client::new(options).unwrap();
client
.send(
&GaugeMetric::new("gauge".into(), "1234".into()),
&["tag1", "tag2"],
)
.unwrap();
}
fn with_default_system_tags<T, F: FnOnce() -> T>(f: F) -> T {
unsafe {
std::env::set_var("DD_ENV", "production");
std::env::set_var("DD_SERVICE", "service");
std::env::set_var("DD_VERSION", "0.0.1");
}
let t = f();
unsafe {
std::env::remove_var("DD_ENV");
std::env::remove_var("DD_SERVICE");
std::env::remove_var("DD_VERSION");
}
t
}
}
#[cfg(all(feature = "unstable", test))]
mod bench {
extern crate test;
use super::*;
use self::test::Bencher;
#[bench]
fn bench_incr(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = &["name1:value1"];
b.iter(|| {
client.incr("bench.incr", tags).unwrap();
})
}
#[bench]
fn bench_decr(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = &["name1:value1"];
b.iter(|| {
client.decr("bench.decr", tags).unwrap();
})
}
#[bench]
fn bench_count(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = &["name1:value1"];
let mut i = 0;
b.iter(|| {
client.count("bench.count", i, tags).unwrap();
i += 1;
})
}
#[bench]
fn bench_timing(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = &["name1:value1"];
let mut i = 0;
b.iter(|| {
client.timing("bench.timing", i, tags).unwrap();
i += 1;
})
}
#[bench]
fn bench_gauge(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let mut i = 0;
b.iter(|| {
client.gauge("bench.guage", &i.to_string(), &tags).unwrap();
i += 1;
})
}
#[bench]
fn bench_histogram(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let mut i = 0;
b.iter(|| {
client
.histogram("bench.histogram", &i.to_string(), &tags)
.unwrap();
i += 1;
})
}
#[bench]
fn bench_distribution(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let mut i = 0;
b.iter(|| {
client
.distribution("bench.distribution", &i.to_string(), &tags)
.unwrap();
i += 1;
})
}
#[bench]
fn bench_set(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let mut i = 0;
b.iter(|| {
client.set("bench.set", &i.to_string(), &tags).unwrap();
i += 1;
})
}
#[bench]
fn bench_service_check(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let all_options = ServiceCheckOptions {
hostname: Some("my-host.localhost"),
timestamp: Some(1510326433),
message: Some("Message about the check or service"),
};
b.iter(|| {
client
.service_check(
"bench.service_check",
ServiceStatus::Critical,
&tags,
Some(all_options),
)
.unwrap();
})
}
#[bench]
fn bench_event(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
b.iter(|| {
client
.event("Test Event Title", "Test Event Message", &tags, None)
.unwrap();
})
}
fn bench_event_options(b: &mut Bencher) {
let options = Options::default();
let client = Client::new(options).unwrap();
let tags = vec!["name1:value1"];
let event_options = EventOptions::new()
.with_timestamp(1638480000)
.with_hostname("localhost")
.with_priority(EventPriority::Normal)
.with_alert_type(EventAlertType::Error);
b.iter(|| {
client
.event(
"Test Event Title",
"Test Event Message",
&tags,
Some(event_options),
)
.unwrap();
})
}
}