fast-telemetry 0.2.0

High-performance, cache-friendly telemetry primitives and export formats for Rust
Documentation
use std::time::Duration;

use fast_telemetry::{DeriveLabel, ExportMetrics, LabeledSampledTimer, SampledTimer, Temporality};

#[derive(Copy, Clone, Debug, DeriveLabel)]
#[label_name = "phase"]
enum Phase {
    Parse,
    Execute,
}

#[derive(ExportMetrics)]
#[metric_prefix = "test"]
#[cfg_attr(feature = "otlp", otlp)]
struct SampledMetrics {
    #[help = "Request latency"]
    latency: SampledTimer,

    #[help = "Phase latency"]
    phase_latency: LabeledSampledTimer<Phase>,
}

impl SampledMetrics {
    fn new() -> Self {
        Self {
            latency: SampledTimer::with_latency_buckets(4, 1),
            phase_latency: LabeledSampledTimer::with_latency_buckets(4, 1),
        }
    }
}

#[test]
fn test_sampled_timer_prometheus_export() {
    let metrics = SampledMetrics::new();
    metrics.latency.record_elapsed(Duration::from_nanos(50_000));

    let mut output = String::new();
    metrics.export_prometheus(&mut output);

    assert!(output.contains("# HELP test_latency_calls Request latency total calls"));
    assert!(output.contains("test_latency_calls 1"));
    assert!(output.contains("# TYPE test_latency_samples histogram"));
    assert!(output.contains("test_latency_samples_count 1"));
}

#[test]
fn test_labeled_sampled_timer_prometheus_export() {
    let metrics = SampledMetrics::new();
    metrics
        .phase_latency
        .record_elapsed(Phase::Parse, Duration::from_nanos(100_000));

    let mut output = String::new();
    metrics.export_prometheus(&mut output);

    assert!(output.contains("test_phase_latency_calls{phase=\"parse\"} 1"));
    assert!(output.contains("test_phase_latency_samples_count{phase=\"parse\"} 1"));
    assert!(output.contains("test_phase_latency_calls{phase=\"execute\"} 0"));
}

#[test]
fn test_sampled_timer_dogstatsd_export() {
    let metrics = SampledMetrics::new();
    metrics.latency.record_elapsed(Duration::from_nanos(50_000));

    let mut output = String::new();
    metrics.export_dogstatsd(&mut output, &[]);

    assert!(output.contains("test.latency.calls:1|c\n"));
    assert!(output.contains("test.latency.samples.count:1|c\n"));
    assert!(output.contains("test.latency.samples.sum:50000|c\n"));
}

#[test]
fn test_sampled_timer_dogstatsd_delta_export() {
    let metrics = SampledMetrics::new();
    let mut state = SampledMetricsDogStatsDState::new();

    metrics.latency.record_elapsed(Duration::from_nanos(50_000));

    let mut output = String::new();
    metrics.export_dogstatsd_with_temporality(&mut output, &[], Temporality::Delta, &mut state);
    assert!(output.contains("test.latency.calls:1|c\n"));
    assert!(output.contains("test.latency.samples.count:1|c\n"));
    assert!(output.contains("test.latency.samples.sum:50000|c\n"));

    metrics.latency.record_elapsed(Duration::from_nanos(70_000));

    let mut output = String::new();
    metrics.export_dogstatsd_with_temporality(&mut output, &[], Temporality::Delta, &mut state);
    assert!(output.contains("test.latency.calls:1|c\n"));
    assert!(output.contains("test.latency.samples.count:1|c\n"));
    assert!(output.contains("test.latency.samples.sum:70000|c\n"));
}

#[cfg(feature = "otlp")]
#[test]
fn test_sampled_timer_otlp_export() {
    let metrics = SampledMetrics::new();
    metrics.latency.record_elapsed(Duration::from_nanos(50_000));

    let mut exported = Vec::new();
    metrics.export_otlp(&mut exported, 123);

    assert_eq!(exported.len(), 4);
    assert_eq!(exported[0].name, "test_latency.calls");
    assert_eq!(exported[1].name, "test_latency.samples");
    assert_eq!(exported[2].name, "test_phase_latency.calls");
    assert_eq!(exported[3].name, "test_phase_latency.samples");
}