Skip to content

Commit d3990fe

Browse files
committed
refactor: unify metadata management into single shared state
1 parent 55d30c1 commit d3990fe

File tree

4 files changed

+119
-118
lines changed

4 files changed

+119
-118
lines changed

metrics-exporter-opentelemetry/src/description.rs

Lines changed: 0 additions & 54 deletions
This file was deleted.

metrics-exporter-opentelemetry/src/lib.rs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
//! An OpenTelemetry metrics exporter for `metrics`.
2-
mod description;
32
mod instruments;
3+
mod metadata;
44
mod storage;
55

6-
use crate::description::DescriptionTable;
6+
use crate::metadata::MetricMetadata;
77
use crate::storage::OtelMetricStorage;
88
use metrics::{Counter, Gauge, Histogram, Key, KeyName, Recorder, SharedString, Unit};
99
use metrics_util::registry::Registry;
1010
use metrics_util::MetricKind;
1111
use opentelemetry::metrics::Meter;
12-
use std::collections::HashMap;
13-
use std::sync::{Arc, RwLock};
1412

1513
/// The OpenTelemetry recorder.
1614
pub struct OpenTelemetryRecorder {
1715
registry: Registry<Key, OtelMetricStorage>,
18-
description_table: Arc<DescriptionTable>,
19-
histogram_bounds: Arc<RwLock<HashMap<KeyName, Vec<f64>>>>,
16+
metadata: MetricMetadata,
2017
}
2118

2219
impl OpenTelemetryRecorder {
2320
/// Creates a new OpenTelemetry recorder with the given meter.
2421
pub fn new(meter: Meter) -> Self {
25-
let description_table = Arc::new(DescriptionTable::default());
26-
let histogram_bounds = Arc::new(RwLock::new(HashMap::new()));
27-
let storage = OtelMetricStorage::new(meter, description_table.clone(), histogram_bounds.clone());
22+
let metadata = MetricMetadata::new();
23+
let storage = OtelMetricStorage::new(meter, metadata.clone());
2824
Self {
2925
registry: Registry::new(storage),
30-
description_table,
31-
histogram_bounds,
26+
metadata,
3227
}
3328
}
3429

@@ -37,8 +32,7 @@ impl OpenTelemetryRecorder {
3732
key: &KeyName,
3833
bounds: Vec<f64>,
3934
) {
40-
let mut bounds_map = self.histogram_bounds.write().unwrap();
41-
bounds_map.insert(key.clone(), bounds);
35+
self.metadata.set_histogram_bounds(key.clone(), bounds);
4236
}
4337

4438
/// Gets a description entry for testing purposes.
@@ -47,8 +41,8 @@ impl OpenTelemetryRecorder {
4741
&self,
4842
key_name: KeyName,
4943
metric_kind: MetricKind,
50-
) -> Option<crate::description::DescriptionEntry> {
51-
self.description_table.get_describe(key_name, metric_kind)
44+
) -> Option<crate::metadata::MetricDescription> {
45+
self.metadata.get_description(&key_name, metric_kind)
5246
}
5347
}
5448

@@ -59,11 +53,11 @@ impl Recorder for OpenTelemetryRecorder {
5953
_unit: Option<Unit>,
6054
_description: SharedString,
6155
) {
62-
self.description_table.add_describe(_key_name, MetricKind::Counter, _unit, _description);
56+
self.metadata.add_description(_key_name, MetricKind::Counter, _unit, _description);
6357
}
6458

6559
fn describe_gauge(&self, _key_name: KeyName, _unit: Option<Unit>, _description: SharedString) {
66-
self.description_table.add_describe(_key_name, MetricKind::Gauge, _unit, _description);
60+
self.metadata.add_description(_key_name, MetricKind::Gauge, _unit, _description);
6761
}
6862

6963
fn describe_histogram(
@@ -72,7 +66,7 @@ impl Recorder for OpenTelemetryRecorder {
7266
_unit: Option<Unit>,
7367
_description: SharedString,
7468
) {
75-
self.description_table.add_describe(_key_name, MetricKind::Histogram, _unit, _description);
69+
self.metadata.add_description(_key_name, MetricKind::Histogram, _unit, _description);
7670
}
7771

7872
fn register_counter(&self, key: &Key, _metadata: &metrics::Metadata<'_>) -> Counter {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use metrics::{KeyName, SharedString, Unit};
2+
use metrics_util::MetricKind;
3+
use std::collections::HashMap;
4+
use std::sync::{Arc, PoisonError, RwLock};
5+
6+
#[derive(Clone)]
7+
pub struct MetricDescription {
8+
unit: Option<Unit>,
9+
description: SharedString,
10+
}
11+
12+
impl MetricDescription {
13+
pub fn unit(&self) -> Option<Unit> {
14+
self.unit
15+
}
16+
17+
pub fn description(&self) -> SharedString {
18+
self.description.clone()
19+
}
20+
}
21+
22+
/// Stores all metric metadata including descriptions and histogram bounds
23+
#[derive(Default)]
24+
pub struct MetricMetadata {
25+
inner: Arc<RwLock<MetricMetadataInner>>,
26+
}
27+
28+
#[derive(Default)]
29+
struct MetricMetadataInner {
30+
descriptions: HashMap<(KeyName, MetricKind), MetricDescription>,
31+
histogram_bounds: HashMap<KeyName, Vec<f64>>,
32+
}
33+
34+
impl MetricMetadata {
35+
pub fn new() -> Self {
36+
Self::default()
37+
}
38+
39+
pub fn add_description(
40+
&self,
41+
key_name: KeyName,
42+
metric_kind: MetricKind,
43+
unit: Option<Unit>,
44+
description: SharedString,
45+
) {
46+
let new_entry = MetricDescription { unit, description };
47+
let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
48+
inner.descriptions.insert((key_name, metric_kind), new_entry);
49+
}
50+
51+
pub fn get_description(
52+
&self,
53+
key_name: &KeyName,
54+
metric_kind: MetricKind,
55+
) -> Option<MetricDescription> {
56+
let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
57+
inner.descriptions.get(&(key_name.clone(), metric_kind)).cloned()
58+
}
59+
60+
pub fn set_histogram_bounds(&self, key_name: KeyName, bounds: Vec<f64>) {
61+
let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
62+
inner.histogram_bounds.insert(key_name, bounds);
63+
}
64+
65+
pub fn get_histogram_bounds(&self, key_name: &KeyName) -> Option<Vec<f64>> {
66+
let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
67+
inner.histogram_bounds.get(key_name).cloned()
68+
}
69+
}
70+
71+
impl Clone for MetricMetadata {
72+
fn clone(&self) -> Self {
73+
Self {
74+
inner: Arc::clone(&self.inner),
75+
}
76+
}
77+
}

metrics-exporter-opentelemetry/src/storage.rs

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
1-
use crate::description::{DescriptionEntry, DescriptionTable};
21
use crate::instruments::{OtelCounter, OtelGauge, OtelHistogram};
2+
use crate::metadata::{MetricDescription, MetricMetadata};
33
use metrics::{Key, KeyName};
44
use metrics_util::registry::Storage;
55
use metrics_util::MetricKind;
66
use opentelemetry::metrics::{AsyncInstrumentBuilder, HistogramBuilder, Meter};
77
use opentelemetry::KeyValue;
8-
use std::collections::HashMap;
9-
use std::sync::{Arc, PoisonError, RwLock};
8+
use std::sync::Arc;
109

1110
pub struct OtelMetricStorage {
1211
meter: Meter,
13-
description_table: Arc<DescriptionTable>,
14-
histogram_bounds: Arc<RwLock<HashMap<KeyName, Vec<f64>>>>,
12+
metadata: MetricMetadata,
1513
}
1614

1715
impl OtelMetricStorage {
18-
pub fn new(meter: Meter, description_table: Arc<DescriptionTable>, histogram_bounds: Arc<RwLock<HashMap<KeyName, Vec<f64>>>>) -> Self {
19-
Self {
20-
meter,
21-
description_table,
22-
histogram_bounds,
23-
}
16+
pub fn new(meter: Meter, metadata: MetricMetadata) -> Self {
17+
Self { meter, metadata }
2418
}
2519

2620
fn get_attributes(key: &Key) -> Vec<KeyValue> {
@@ -29,28 +23,27 @@ impl OtelMetricStorage {
2923
.collect()
3024
}
3125

32-
fn with_description_entry<'a, I, M>(
33-
description_entry: &DescriptionEntry,
26+
fn with_description<'a, I, M>(
27+
description: &MetricDescription,
3428
builder: AsyncInstrumentBuilder<'a, I, M>,
3529
) -> AsyncInstrumentBuilder<'a, I, M> {
36-
// let builder = builder.with_description(self.description.to_string());
37-
match description_entry.unit() {
30+
match description.unit() {
3831
Some(unit) => builder
39-
.with_description(description_entry.description().to_string())
32+
.with_description(description.description().to_string())
4033
.with_unit(unit.as_canonical_label()),
41-
None => builder.with_description(description_entry.description().to_string()),
34+
None => builder.with_description(description.description().to_string()),
4235
}
4336
}
44-
fn with_description_entry_histogram<'a, T>(
45-
description_entry: &DescriptionEntry,
37+
38+
fn with_description_histogram<'a, T>(
39+
description: &MetricDescription,
4640
builder: HistogramBuilder<'a, T>,
4741
) -> HistogramBuilder<'a, T> {
48-
// let builder = builder.with_description(self.description.to_string());
49-
match description_entry.unit() {
42+
match description.unit() {
5043
Some(unit) => builder
51-
.with_description(description_entry.description().to_string())
44+
.with_description(description.description().to_string())
5245
.with_unit(unit.as_canonical_label()),
53-
None => builder.with_description(description_entry.description().to_string()),
46+
None => builder.with_description(description.description().to_string()),
5447
}
5548
}
5649
}
@@ -62,11 +55,9 @@ impl Storage<Key> for OtelMetricStorage {
6255

6356
fn counter(&self, key: &Key) -> Self::Counter {
6457
let builder = self.meter.u64_observable_counter(key.name().to_string());
65-
let description = self
66-
.description_table
67-
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Counter);
68-
let builder = if let Some(description) = description {
69-
Self::with_description_entry(&description, builder)
58+
let key_name = KeyName::from(key.name().to_string());
59+
let builder = if let Some(description) = self.metadata.get_description(&key_name, MetricKind::Counter) {
60+
Self::with_description(&description, builder)
7061
} else {
7162
builder
7263
};
@@ -76,11 +67,9 @@ impl Storage<Key> for OtelMetricStorage {
7667

7768
fn gauge(&self, key: &Key) -> Self::Gauge {
7869
let builder = self.meter.f64_observable_gauge(key.name().to_string());
79-
let description = self
80-
.description_table
81-
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Gauge);
82-
let builder = if let Some(description) = description {
83-
Self::with_description_entry(&description, builder)
70+
let key_name = KeyName::from(key.name().to_string());
71+
let builder = if let Some(description) = self.metadata.get_description(&key_name, MetricKind::Gauge) {
72+
Self::with_description(&description, builder)
8473
} else {
8574
builder
8675
};
@@ -90,24 +79,19 @@ impl Storage<Key> for OtelMetricStorage {
9079

9180
fn histogram(&self, key: &Key) -> Self::Histogram {
9281
let builder = self.meter.f64_histogram(key.name().to_string());
93-
let description = self
94-
.description_table
95-
.get_describe(KeyName::from(key.name().to_string()), MetricKind::Histogram);
96-
let builder = if let Some(description) = description {
97-
Self::with_description_entry_histogram(&description, builder)
82+
let key_name = KeyName::from(key.name().to_string());
83+
84+
let builder = if let Some(description) = self.metadata.get_description(&key_name, MetricKind::Histogram) {
85+
Self::with_description_histogram(&description, builder)
9886
} else {
9987
builder
10088
};
10189

10290
// Apply histogram bounds if they exist
103-
let key_name = KeyName::from(key.name().to_string());
104-
let builder = {
105-
let bounds_map = self.histogram_bounds.read().unwrap_or_else(PoisonError::into_inner);;
106-
if let Some(bounds) = bounds_map.get(&key_name) {
107-
builder.with_boundaries(bounds.clone())
108-
} else {
109-
builder
110-
}
91+
let builder = if let Some(bounds) = self.metadata.get_histogram_bounds(&key_name) {
92+
builder.with_boundaries(bounds)
93+
} else {
94+
builder
11195
};
11296

11397
let attributes = Self::get_attributes(key);

0 commit comments

Comments
 (0)