use opentelemetry::KeyValue;
use opentelemetry::metrics::UpDownCounter;
#[must_use]
pub struct TrackGuard<T: From<i8>> {
counter: UpDownCounter<T>,
attributes: Vec<KeyValue>,
}
impl<T: From<i8>> TrackGuard<T> {
pub fn new(counter: UpDownCounter<T>, attributes: impl Into<Vec<KeyValue>>) -> Self {
let attributes = attributes.into();
counter.add(T::from(1_i8), &attributes);
Self {
counter,
attributes,
}
}
pub fn set(&mut self, kv: KeyValue) {
self.counter.add(T::from(-1_i8), &self.attributes);
if let Some(existing) = self.attributes.iter_mut().find(|a| a.key == kv.key) {
*existing = kv;
} else {
self.attributes.push(kv);
}
self.counter.add(T::from(1_i8), &self.attributes);
}
}
impl<T: From<i8>> Drop for TrackGuard<T> {
fn drop(&mut self) {
self.counter.add(T::from(-1_i8), &self.attributes);
}
}
#[cfg(test)]
mod tests {
use apollo_opentelemetry_test::{TelemetryContext, assert_metrics_snapshot};
use opentelemetry::KeyValue;
use opentelemetry::global;
use super::TrackGuard;
#[test]
fn increments_on_new_decrements_on_drop() {
let ctx = TelemetryContext::new();
let counter = global::meter_provider()
.meter("test")
.i64_up_down_counter("test.active")
.build();
let guard = TrackGuard::new(counter, []);
assert_metrics_snapshot!(ctx, @r"
- name: test.active
data:
type: Sum
data_points:
- value: 1
is_monotonic: false
temporality: Cumulative
");
drop(guard);
assert_metrics_snapshot!(ctx, @r"
- name: test.active
data:
type: Sum
data_points:
- value: 0
is_monotonic: false
temporality: Cumulative
");
}
#[test]
fn set_transitions_between_attribute_sets() {
let ctx = TelemetryContext::new();
let counter = global::meter_provider()
.meter("test")
.i64_up_down_counter("test.connections")
.build();
let mut guard = TrackGuard::new(counter, [KeyValue::new("state", "idle")]);
assert_metrics_snapshot!(ctx, @r"
- name: test.connections
data:
type: Sum
data_points:
- attributes:
state: idle
value: 1
is_monotonic: false
temporality: Cumulative
");
guard.set(KeyValue::new("state", "active"));
assert_metrics_snapshot!(ctx, @r"
- name: test.connections
data:
type: Sum
data_points:
- attributes:
state: active
value: 1
- attributes:
state: idle
value: 0
is_monotonic: false
temporality: Cumulative
");
guard.set(KeyValue::new("state", "idle"));
assert_metrics_snapshot!(ctx, @r"
- name: test.connections
data:
type: Sum
data_points:
- attributes:
state: active
value: 0
- attributes:
state: idle
value: 1
is_monotonic: false
temporality: Cumulative
");
drop(guard);
assert_metrics_snapshot!(ctx, @r"
- name: test.connections
data:
type: Sum
data_points:
- attributes:
state: active
value: 0
- attributes:
state: idle
value: 0
is_monotonic: false
temporality: Cumulative
");
}
#[test]
fn drop_decrements_last_set_attribute() {
let ctx = TelemetryContext::new();
let counter = global::meter_provider()
.meter("test")
.i64_up_down_counter("test.connections")
.build();
let mut guard = TrackGuard::new(counter, [KeyValue::new("state", "idle")]);
guard.set(KeyValue::new("state", "active"));
drop(guard);
assert_metrics_snapshot!(ctx, @r"
- name: test.connections
data:
type: Sum
data_points:
- attributes:
state: active
value: 0
- attributes:
state: idle
value: 0
is_monotonic: false
temporality: Cumulative
");
}
}