Open ericsampson opened 2 years ago
There's been limited discussion a while back about it: https://github.com/metrics-rs/metrics/issues/19.
It's not an item I'd personally spend time, but I'm definitely open to any PRs adding support for it. 👍🏻
ok thanks! the OpenTelemetry metrics spec has stabilized by now so it should be doable.
Not sure if and when i’ll have time, but i just wanted to make sure not to be duplicating work with someone. cheers!
Hi, I'm interested in this functionality as well. Does anybody know if this has gotten any traction?
@AdriaanPrinsloo if you have time to work on it, we could perhaps work together.
So, I gave this a quick shot. I'll post below, but long story short it seems impossible with the current opentelemetry
api.
metrics
trait methods for counter, guage, etc. are possible to implement with opentelemetry, so you would have to ignore calls like increment
on the otel Guage.metrics
.Here's my prototype.
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
};
use metrics::{CounterFn, GaugeFn, Key, KeyName, Metadata, Recorder, SharedString, Unit};
use opentelemetry::{
metrics::{Meter, MeterProvider as _},
KeyValue,
};
use opentelemetry_sdk::metrics::SdkMeterProvider;
pub struct OtelRecorder {
meter_provider: SdkMeterProvider,
meters: Mutex<BTreeMap<String, Meter>>,
counters: Mutex<BTreeMap<String, Arc<OtelCounter>>>,
}
struct OtelCounter {
counter: opentelemetry::metrics::Counter<u64>,
attributes: Vec<KeyValue>,
}
impl CounterFn for OtelCounter {
fn increment(&self, value: u64) {
self.counter.add(value, &self.attributes);
}
fn absolute(&self, _value: u64) {
panic!("Absolute not supported");
}
}
struct OtelGuage {
guage: opentelemetry::metrics::Gauge<f64>,
attributes: Vec<KeyValue>,
}
impl GaugeFn for OtelGuage {
fn increment(&self, _value: f64) {
panic!("increment not supported");
}
fn decrement(&self, _value: f64) {
panic!("decrement not supported");
}
fn set(&self, value: f64) {
self.guage.record(value, &self.attributes);
}
}
impl OtelRecorder {
fn get_meter(&self, metadata: &Metadata<'_>) -> Meter {
// First get the module path
let module_path = metadata.target().to_string();
// Check to see if the meter already exists
let mut meters = self.meters.lock().unwrap();
match meters.get(&module_path) {
Some(meter) => meter.clone(),
None => {
// Create a new meter
let meter = self.meter_provider.meter(module_path.clone());
meters.insert(module_path.to_string(), meter.clone());
meter
}
}
}
}
impl Recorder for OtelRecorder {
fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
todo!()
}
fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
todo!()
}
fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
todo!()
}
fn register_counter(&self, key: &Key, metadata: &Metadata<'_>) -> metrics::Counter {
// let observer
let meter = self.get_meter(metadata);
let counter = meter.u64_counter(key.name().to_string()).init();
let otel_counter = Arc::new(OtelCounter {
counter,
attributes: key
.labels()
.map(|label| KeyValue::new(label.key().to_string(), label.value().to_string()))
.collect(),
});
self.counters
.lock()
.unwrap()
.insert(key.name().to_string(), otel_counter.clone());
metrics::Counter::from_arc(otel_counter)
}
fn register_gauge(&self, key: &Key, _metadata: &Metadata<'_>) -> metrics::Gauge {
todo!()
}
fn register_histogram(&self, key: &Key, _metadata: &Metadata<'_>) -> metrics::Histogram {
todo!()
}
}
Hi! Has anyone discussed/worked on an OTel Metrics compliant exporter?
Cheers! Eric