Skip to main content

rylv_metrics/
lib.rs

1//! # rylv-metrics
2//!
3//! A high-performance `DogStatsD` metrics client for Rust with client-side aggregation.
4//!
5//! ## Features
6//!
7//! - **High Performance**: Lock-free data structures and optimized UDP batching
8//! - **Client-Side Aggregation**: Reduces network overhead by aggregating metrics before sending
9//! - **Multiple Writer Backends**: Simple, `LinuxBatch`, `AppleBatch`, and Custom writers
10//! - **Metric Types**: Histograms, Counters, and Gauges
11//!
12//! ## Quick Start
13//!
14//! ```no_run
15//! # #[cfg(feature = "udp")] {
16//! use rylv_metrics::{
17//!     MetricCollector, MetricCollectorOptions, MetricCollectorTrait, RylvStr, SharedCollector,
18//! };
19//! use rylv_metrics::{histogram, count, count_add, gauge};
20//! use std::net::SocketAddr;
21//! use std::time::Duration;
22//!
23//! let options = MetricCollectorOptions {
24//!     max_udp_packet_size: 1432,
25//!     max_udp_batch_size: 10,
26//!     flush_interval: Duration::from_secs(10),
27//!     writer_type: rylv_metrics::DEFAULT_STATS_WRITER_TYPE,
28//!     ..Default::default()
29//! };
30//!
31//! let bind_addr: SocketAddr = "0.0.0.0:0".parse().unwrap();
32//! let dst_addr: SocketAddr = "127.0.0.1:8125".parse().unwrap();
33//! let inner = SharedCollector::default();
34//! let collector = MetricCollector::new(bind_addr, dst_addr, options, inner).unwrap();
35//!
36//! // Direct API — use RylvStr::from_static() for zero-copy aggregation keys
37//! collector.histogram(RylvStr::from_static("request.latency"), 42, &mut [RylvStr::from_static("endpoint:api")]);
38//! collector.count(RylvStr::from_static("request.count"), &mut [RylvStr::from_static("endpoint:api")]);
39//! collector.count_add(RylvStr::from_static("bytes.sent"), 1024, &mut [RylvStr::from_static("endpoint:api")]);
40//! collector.gauge(RylvStr::from_static("connections.active"), 100, &mut [RylvStr::from_static("pool:main")]);
41//!
42//! // Convenience macros — allocate on first key insertion, but more ergonomic
43//! histogram!(collector, "request.latency", 42, "endpoint:api");
44//! count!(collector, "request.count", "endpoint:api");
45//! count_add!(collector, "bytes.sent", 1024, "endpoint:api");
46//! gauge!(collector, "connections.active", 100, "pool:main");
47//! # }
48//! ```
49
50// #![deny(unsafe_code)]
51#![deny(clippy::unwrap_used)]
52#![deny(clippy::expect_used)]
53#![deny(clippy::panic)]
54//#![deny(clippy::unreachable)]
55#![deny(clippy::todo)]
56#![deny(clippy::unimplemented)]
57#![deny(clippy::pedantic)]
58#![deny(clippy::nursery)]
59// Disabled because it reports false duplicate-crate errors from dev-dependencies
60#![deny(clippy::cargo)]
61#![allow(clippy::multiple_crate_versions)]
62#![deny(missing_docs)]
63#![deny(clippy::missing_errors_doc)]
64#![deny(clippy::missing_panics_doc)]
65#![allow(clippy::module_name_repetitions)]
66#![cfg_attr(test, allow(clippy::unwrap_used))]
67#![cfg_attr(test, allow(clippy::expect_used))]
68#![cfg_attr(test, allow(clippy::panic))]
69
70// https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics
71mod dogstats;
72mod error;
73
74#[cfg(all(feature = "custom_writer", feature = "udp"))]
75pub use dogstats::writer::StatsWriterTrait;
76pub use dogstats::{
77    DrainMetricCollectorTrait, HistogramBaseMetric, HistogramConfig, MetricCollectorTrait,
78    MetricFrameRef, MetricKind, MetricSuffix, PreparedMetric, SortedTags,
79};
80#[cfg(feature = "udp")]
81pub use dogstats::{
82    MetricCollector, MetricCollectorOptions, StatsWriterType, DEFAULT_STATS_WRITER_TYPE,
83};
84pub use dogstats::{RylvStr, SigFig};
85#[cfg(feature = "shared-collector")]
86pub use dogstats::{SharedCollector, SharedCollectorOptions};
87#[cfg(feature = "tls-collector")]
88pub use dogstats::{TLSCollector, TLSCollectorOptions};
89pub use error::MetricsError;
90
91/// Result type for metric operations.
92///
93/// Wraps errors that can occur during metric collection and transmission.
94pub type MetricResult<T> = Result<T, MetricsError>;
95
96/// Default hasher builder used by metric aggregation maps.
97pub(crate) type DefaultMetricHasher = std::hash::RandomState;
98
99/// Internal benchmark hook for measuring lookup-key comparison behavior.
100#[cfg(feature = "__bench-internals")]
101#[doc(hidden)]
102#[must_use]
103pub fn benchmark_lookup_compare(
104    metric: &str,
105    lookup_tags: &[&str],
106    entry_tags: &[&str],
107    hash: u64,
108) -> bool {
109    use crate::dogstats::{AggregatorEntryKey, LookupKey};
110
111    let lookup_tags_owned: Box<[RylvStr<'static>]> = lookup_tags
112        .iter()
113        .map(|tag| RylvStr::from((*tag).to_owned()))
114        .collect::<Vec<_>>()
115        .into_boxed_slice();
116    let entry_tags_owned: Vec<RylvStr<'static>> = entry_tags
117        .iter()
118        .map(|tag| RylvStr::from((*tag).to_owned()))
119        .collect();
120
121    let entry = AggregatorEntryKey {
122        metric: RylvStr::from(metric.to_owned()),
123        tags: SortedTags::new(entry_tags_owned, &std::hash::RandomState::default()),
124        hash,
125        fingerprint: 0,
126        id: 1,
127    };
128
129    let lookup = LookupKey {
130        metric: RylvStr::from(metric.to_owned()),
131        tags: &lookup_tags_owned,
132        tags_hash: 0,
133        hash,
134    };
135    lookup.compare(&entry)
136}