set up opentelemetry for metrics

Also adds an `allow_prometheus` option (disabled by default) to expose
a `/metrics` endpoint that returns Prometheus data.
This commit is contained in:
Charles Hall 2024-05-29 14:49:49 -07:00
parent 94fda7c875
commit a0b92c82e8
No known key found for this signature in database
GPG key ID: 7B8E0645816E07CF
5 changed files with 97 additions and 2 deletions

View file

@ -3,13 +3,17 @@
use std::{fs::File, io::BufWriter};
use opentelemetry::KeyValue;
use opentelemetry_sdk::Resource;
use once_cell::sync::Lazy;
use opentelemetry::{metrics::MeterProvider, KeyValue};
use opentelemetry_sdk::{metrics::SdkMeterProvider, Resource};
use tracing_flame::{FlameLayer, FlushGuard};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry};
use crate::{config::Config, error, utils::error::Result};
/// Globally accessible metrics state
pub(crate) static METRICS: Lazy<Metrics> = Lazy::new(Metrics::new);
/// Cleans up resources relating to observability when [`Drop`]ped
pub(crate) struct Guard {
/// Drop guard used to flush [`tracing_flame`] data on exit
@ -85,3 +89,43 @@ fn standard_resource() -> Resource {
env!("CARGO_PKG_NAME"),
)]))
}
/// Holds state relating to metrics
pub(crate) struct Metrics {
/// Internal state for OpenTelemetry metrics
///
/// We never directly read from [`SdkMeterProvider`], but it needs to
/// outlive all calls to `self.otel_state.0.gather()`, otherwise
/// metrics collection will fail.
otel_state: (prometheus::Registry, SdkMeterProvider),
}
impl Metrics {
/// Initializes metric-collecting and exporting facilities
fn new() -> Self {
// Set up OpenTelemetry state
let registry = prometheus::Registry::new();
let exporter = opentelemetry_prometheus::exporter()
.with_registry(registry.clone())
.build()
.expect("exporter configuration should be valid");
let provider = SdkMeterProvider::builder()
.with_reader(exporter)
.with_resource(standard_resource())
.build();
let _meter = provider.meter(env!("CARGO_PKG_NAME"));
// TODO: Add some metrics
Metrics {
otel_state: (registry, provider),
}
}
/// Export metrics to a string suitable for consumption by e.g. Prometheus
pub(crate) fn export(&self) -> String {
prometheus::TextEncoder::new()
.encode_to_string(&self.otel_state.0.gather())
.expect("should be able to encode metrics")
}
}