#[cfg(all(feature = "metrics", feature = "opentelemetry"))]
compile_error!(
"Features `metrics` and `opentelemetry` are mutually exclusive. \
Please enable only one backend."
);
#[cfg(feature = "metrics")]
mod metrics;
#[cfg(feature = "metrics")]
pub(crate) use metrics::*;
#[cfg(feature = "opentelemetry")]
mod otel;
#[cfg(feature = "opentelemetry")]
pub(crate) use otel::*;
#[cfg(not(any(feature = "metrics", feature = "opentelemetry")))]
mod noop {
use super::Label;
#[inline]
pub fn increment_counter(_name: &'static str, _value: u64, _labels: &[Label]) {}
#[inline]
pub fn record_histogram(_name: &'static str, _value: f64, _label: &[Label]) {}
#[inline]
pub fn set_gauge(_name: &'static str, _value: f64, _labels: &[Label]) {}
}
#[cfg(not(any(feature = "metrics", feature = "opentelemetry")))]
pub(crate) use noop::*;
pub const FILE_POLLER_LATENCY_MS: &str = "prestige.file_poller.latency_ms";
pub const FILE_POLLER_LATEST_TIMESTAMP_MS: &str = "prestige.file_poller.latest_timestamp_ms";
pub const FILE_POLLER_FILES_PROCESSED: &str = "prestige.file_poller.files_processed";
pub const FILE_UPLOAD_DURATION_MS: &str = "prestige.file_upload.duration_ms";
pub const FILE_UPLOAD_COUNT: &str = "prestige.file_upload.count";
pub const FILE_UPLOAD_SIZE_BYTES: &str = "prestige.file_upload.size_bytes";
pub const FILE_SOURCE_FILES_READ: &str = "prestige.file_source.files_read";
pub const FILE_SOURCE_ROWS_READ: &str = "prestige.file_source.rows_read";
pub const FILE_SOURCE_READ_ERRORS: &str = "prestige.file_source.read_errors";
pub const FILE_SOURCE_READ_DURATION_MS: &str = "prestige.file_source.read_duration_ms";
pub const FILE_SOURCE_BYTES_DOWNLOADED: &str = "prestige.file_source.bytes_downloaded";
pub const SINK_RECORDS_WRITTEN: &str = "prestige.file_sink.records_written";
pub const SINK_WRITE_ERRORS: &str = "prestige.file_sink.write_errors";
pub const SINK_FILES_ROTATED: &str = "prestige.file_sink.files_rotated";
pub const SINK_BATCH_SIZE: &str = "prestige.file_sink.batch_size";
#[derive(Debug, Clone)]
#[cfg_attr(
not(any(feature = "metrics", feature = "opentelemetry")),
allow(dead_code)
)]
pub(crate) struct Label {
pub key: &'static str,
pub value: String,
}
impl Label {
pub fn new(key: &'static str, value: impl Into<String>) -> Self {
Self {
key,
value: value.into(),
}
}
}
macro_rules! telemetry_labels {
() => {
&[] as &[$crate::telemetry::Label]
};
($($key:expr => $value:expr),+ $(,)?) => {
&{
let mut labels = Vec::new();
$(
if let Some(v) = $crate::telemetry::IntoOptionString::into_option($value) {
labels.push($crate::telemetry::Label::new($key, v));
}
)+
labels
}
};
}
pub(crate) use telemetry_labels;
pub(crate) trait IntoOptionString {
fn into_option(self) -> Option<String>;
}
impl<T: Into<String>> IntoOptionString for Option<T> {
fn into_option(self) -> Option<String> {
self.map(Into::into)
}
}
impl IntoOptionString for &str {
fn into_option(self) -> Option<String> {
Some(self.to_string())
}
}
impl IntoOptionString for String {
fn into_option(self) -> Option<String> {
Some(self)
}
}
impl IntoOptionString for &String {
fn into_option(self) -> Option<String> {
Some(self.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_label_creation() {
let label = Label::new("key", "value");
assert_eq!(label.key, "key");
assert_eq!(label.value, "value");
}
#[test]
fn test_label_from_string() {
let label = Label::new("key", "value".to_string());
assert_eq!(label.value, "value");
}
#[test]
fn test_metrics_with_empty_labels() {
increment_counter("test.counter", 1, &[]);
record_histogram("test.histogram", 1.0, &[]);
set_gauge("test.gauge", 1.0, &[]);
}
#[test]
fn test_metrics_with_non_empty_labels() {
let labels = &[Label::new("key", "value")];
increment_counter("test.counter", 1, labels);
record_histogram("test.histogram", 1.0, labels);
set_gauge("test.gauge", 1.0, labels);
}
}