Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenTelemetry exporter? #275

Open
ericsampson opened this issue Feb 18, 2022 · 5 comments
Open

OpenTelemetry exporter? #275

ericsampson opened this issue Feb 18, 2022 · 5 comments
Labels
C-exporter Component: exporters such as Prometheus, TCP, etc. E-intermediate Effort: intermediate. T-enhancement Type: enhancement. T-request Type: request.

Comments

@ericsampson
Copy link

Hi! Has anyone discussed/worked on an OTel Metrics compliant exporter?

Cheers!
Eric

@tobz
Copy link
Member

tobz commented Mar 1, 2022

There's been limited discussion a while back about it: #19.

It's not an item I'd personally spend time, but I'm definitely open to any PRs adding support for it. 👍🏻

@tobz tobz added C-exporter Component: exporters such as Prometheus, TCP, etc. E-intermediate Effort: intermediate. T-enhancement Type: enhancement. T-request Type: request. labels Mar 1, 2022
@ericsampson
Copy link
Author

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!

@AdriaanPrinsloo
Copy link

Hi, I'm interested in this functionality as well. Does anybody know if this has gotten any traction?

@ericsampson
Copy link
Author

@AdriaanPrinsloo if you have time to work on it, we could perhaps work together.

@ewoolsey
Copy link

ewoolsey commented Jan 19, 2024

So, I gave this a quick shot. I'll post below, but long story short it seems impossible with the current opentelemetry api.

  1. Not all 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.
  2. It's impossible in otel to describe a guage after it's been innitialized, something that's required in 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!()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-exporter Component: exporters such as Prometheus, TCP, etc. E-intermediate Effort: intermediate. T-enhancement Type: enhancement. T-request Type: request.
Projects
None yet
Development

No branches or pull requests

4 participants