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}