Skip to content

Commit 3f2c697

Browse files
committed
fix: support description
1 parent 1a0566b commit 3f2c697

File tree

3 files changed

+151
-40
lines changed

3 files changed

+151
-40
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use metrics::{KeyName, SharedString, Unit};
2+
use metrics_util::MetricKind;
3+
4+
use std::collections::HashMap;
5+
use std::sync::{PoisonError, RwLock};
6+
7+
#[derive(Clone)]
8+
pub struct DescriptionEntry {
9+
unit: Option<Unit>,
10+
description: SharedString,
11+
}
12+
13+
impl DescriptionEntry {
14+
pub fn unit(&self) -> Option<Unit> {
15+
self.unit
16+
}
17+
pub fn description(&self) -> SharedString {
18+
self.description.clone()
19+
}
20+
}
21+
22+
#[derive(Default)]
23+
pub struct DescriptionTable {
24+
table: RwLock<HashMap<(KeyName, MetricKind), DescriptionEntry>>,
25+
}
26+
27+
impl DescriptionTable {
28+
pub fn add_describe(
29+
&self,
30+
key_name: KeyName,
31+
metric_kind: MetricKind,
32+
unit: Option<Unit>,
33+
description: SharedString,
34+
) {
35+
let new_entry = DescriptionEntry { unit, description };
36+
self.table
37+
.write()
38+
.unwrap_or_else(PoisonError::into_inner)
39+
.entry((key_name, metric_kind))
40+
.and_modify(|e| {
41+
*e = new_entry.clone();
42+
})
43+
.or_insert(new_entry);
44+
}
45+
46+
pub fn get_describe(
47+
&self,
48+
key_name: KeyName,
49+
metric_kind: MetricKind,
50+
) -> Option<DescriptionEntry> {
51+
let table = self.table.read().unwrap_or_else(PoisonError::into_inner);
52+
table.get(&(key_name, metric_kind)).cloned()
53+
}
54+
}
Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,53 @@
11
//! An OpenTelemetry metrics exporter for `metrics`.
2+
mod description;
23
mod instruments;
34
mod storage;
45

6+
use crate::description::DescriptionTable;
7+
use crate::storage::OtelMetricStorage;
58
use metrics::{Counter, Gauge, Histogram, Key, KeyName, Recorder, SharedString, Unit};
6-
use metrics_util::registry::{Registry, Storage};
9+
use metrics_util::registry::Registry;
10+
use metrics_util::MetricKind;
711
use opentelemetry::metrics::Meter;
8-
use crate::storage::OtelMetricStorage;
12+
use std::sync::Arc;
913

1014
/// The OpenTelemetry recorder.
1115
pub struct OpenTelemetryRecorder {
1216
registry: Registry<Key, OtelMetricStorage>,
17+
description_table: Arc<DescriptionTable>,
1318
}
1419

1520
impl OpenTelemetryRecorder {
1621
/// Creates a new OpenTelemetry recorder with the given meter.
1722
pub fn new(meter: Meter) -> Self {
18-
let storage = OtelMetricStorage::new(meter);
19-
Self {
20-
registry: Registry::new(storage),
21-
}
23+
let description_table = Arc::new(DescriptionTable::default());
24+
let storage = OtelMetricStorage::new(meter, description_table.clone());
25+
Self { registry: Registry::new(storage), description_table }
26+
}
27+
28+
/// Gets a description entry for testing purposes.
29+
#[cfg(test)]
30+
pub fn get_description(
31+
&self,
32+
key_name: KeyName,
33+
metric_kind: MetricKind,
34+
) -> Option<crate::description::DescriptionEntry> {
35+
self.description_table.get_describe(key_name, metric_kind)
2236
}
2337
}
2438

2539
impl Recorder for OpenTelemetryRecorder {
26-
fn describe_counter(&self, _key_name: KeyName, _unit: Option<Unit>, _description: SharedString) {
27-
// Descriptions are handled when creating instruments
40+
fn describe_counter(
41+
&self,
42+
_key_name: KeyName,
43+
_unit: Option<Unit>,
44+
_description: SharedString,
45+
) {
46+
self.description_table.add_describe(_key_name, MetricKind::Counter, _unit, _description);
2847
}
2948

3049
fn describe_gauge(&self, _key_name: KeyName, _unit: Option<Unit>, _description: SharedString) {
31-
// Descriptions are handled when creating instruments
50+
self.description_table.add_describe(_key_name, MetricKind::Gauge, _unit, _description);
3251
}
3352

3453
fn describe_histogram(
@@ -37,24 +56,18 @@ impl Recorder for OpenTelemetryRecorder {
3756
_unit: Option<Unit>,
3857
_description: SharedString,
3958
) {
40-
// Descriptions are handled when creating instruments
59+
self.description_table.add_describe(_key_name, MetricKind::Histogram, _unit, _description);
4160
}
4261

4362
fn register_counter(&self, key: &Key, _metadata: &metrics::Metadata<'_>) -> Counter {
44-
self.registry.get_or_create_counter(key, |c| {
45-
Counter::from_arc(c.clone())
46-
})
63+
self.registry.get_or_create_counter(key, |c| Counter::from_arc(c.clone()))
4764
}
4865

4966
fn register_gauge(&self, key: &Key, _metadata: &metrics::Metadata<'_>) -> Gauge {
50-
self.registry.get_or_create_gauge(key, |g| {
51-
Gauge::from_arc(g.clone())
52-
})
67+
self.registry.get_or_create_gauge(key, |g| Gauge::from_arc(g.clone()))
5368
}
5469

5570
fn register_histogram(&self, key: &Key, _metadata: &metrics::Metadata<'_>) -> Histogram {
56-
self.registry.get_or_create_histogram(key, |h| {
57-
Histogram::from_arc(h.clone())
58-
})
71+
self.registry.get_or_create_histogram(key, |h| Histogram::from_arc(h.clone()))
5972
}
60-
}
73+
}
Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,52 @@
1-
use std::sync::Arc;
2-
use opentelemetry::KeyValue;
3-
use opentelemetry::metrics::Meter;
4-
use metrics::Key;
5-
use metrics_util::registry::Storage;
1+
use crate::description::{DescriptionEntry, DescriptionTable};
62
use crate::instruments::{OtelCounter, OtelGauge, OtelHistogram};
3+
use metrics::{Key, KeyName};
4+
use metrics_util::registry::Storage;
5+
use metrics_util::MetricKind;
6+
use opentelemetry::metrics::{AsyncInstrumentBuilder, HistogramBuilder, Meter};
7+
use opentelemetry::KeyValue;
8+
use std::sync::Arc;
79

810
pub struct OtelMetricStorage {
911
meter: Meter,
12+
description_table: Arc<DescriptionTable>,
1013
}
1114

1215
impl OtelMetricStorage {
13-
pub fn new(meter: Meter) -> Self {
14-
Self { meter }
16+
pub fn new(meter: Meter, description_table: Arc<DescriptionTable>) -> Self {
17+
Self { meter, description_table }
1518
}
1619

1720
fn get_attributes(key: &Key) -> Vec<KeyValue> {
1821
key.labels()
1922
.map(|label| KeyValue::new(label.key().to_string(), label.value().to_string()))
2023
.collect()
2124
}
25+
26+
fn with_description_entry<'a, I, M>(
27+
description_entry: &DescriptionEntry,
28+
builder: AsyncInstrumentBuilder<'a, I, M>,
29+
) -> AsyncInstrumentBuilder<'a, I, M> {
30+
// let builder = builder.with_description(self.description.to_string());
31+
match description_entry.unit() {
32+
Some(unit) => builder
33+
.with_description(description_entry.description().to_string())
34+
.with_unit(unit.as_canonical_label()),
35+
None => builder.with_description(description_entry.description().to_string()),
36+
}
37+
}
38+
fn with_description_entry_histogram<'a, T>(
39+
description_entry: &DescriptionEntry,
40+
builder: HistogramBuilder<'a, T>,
41+
) -> HistogramBuilder<'a, T> {
42+
// let builder = builder.with_description(self.description.to_string());
43+
match description_entry.unit() {
44+
Some(unit) => builder
45+
.with_description(description_entry.description().to_string())
46+
.with_unit(unit.as_canonical_label()),
47+
None => builder.with_description(description_entry.description().to_string()),
48+
}
49+
}
2250
}
2351

2452
impl Storage<Key> for OtelMetricStorage {
@@ -27,28 +55,44 @@ impl Storage<Key> for OtelMetricStorage {
2755
type Histogram = Arc<OtelHistogram>;
2856

2957
fn counter(&self, key: &Key) -> Self::Counter {
30-
let otel_counter_builder = self
31-
.meter
32-
.u64_observable_counter(key.name().to_string());
58+
let builder = self.meter.u64_observable_counter(key.name().to_string());
59+
let description = self
60+
.description_table
61+
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Counter);
62+
let builder = if let Some(description) = description {
63+
Self::with_description_entry(&description, builder)
64+
} else {
65+
builder
66+
};
3367
let attributes = Self::get_attributes(key);
34-
Arc::new(OtelCounter::new(otel_counter_builder, attributes))
68+
Arc::new(OtelCounter::new(builder, attributes))
3569
}
3670

3771
fn gauge(&self, key: &Key) -> Self::Gauge {
38-
let builder = self
39-
.meter
40-
.f64_observable_gauge(key.name().to_string());
72+
let builder = self.meter.f64_observable_gauge(key.name().to_string());
73+
let description = self
74+
.description_table
75+
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Gauge);
76+
let builder = if let Some(description) = description {
77+
Self::with_description_entry(&description, builder)
78+
} else {
79+
builder
80+
};
4181
let attributes = Self::get_attributes(key);
4282
Arc::new(OtelGauge::new(builder, attributes))
4383
}
4484

4585
fn histogram(&self, key: &Key) -> Self::Histogram {
46-
let histogram = self
47-
.meter
48-
.f64_histogram(key.name().to_string())
49-
.build();
86+
let builder = self.meter.f64_histogram(key.name().to_string());
87+
let description = self
88+
.description_table
89+
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Histogram);
90+
let builder = if let Some(description) = description {
91+
Self::with_description_entry_histogram(&description, builder)
92+
} else {
93+
builder
94+
};
5095
let attributes = Self::get_attributes(key);
51-
Arc::new(OtelHistogram::new(histogram, attributes))
52-
96+
Arc::new(OtelHistogram::new(builder.build(), attributes))
5397
}
5498
}

0 commit comments

Comments
 (0)