Skip to content

Commit

Permalink
Metrics/gauge (#129)
Browse files Browse the repository at this point in the history
The
[opentelemetry](https://github.com/open-telemetry/opentelemetry-rust/blob/a7a47a745dfe9ded79ffb64722fc53847638d47a/examples/metrics-basic/src/main.rs#L112)
crate actually supports Gauge metric behind otel_unstable feature.

Co-authored-by: Sistemas <sistemas@academiares.com>
  • Loading branch information
biryukovmaxim and academiaresf committed May 24, 2024
1 parent 5623937 commit d0123e4
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 5 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
- name: install cargo-nextest
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --profile ci --workspace
run: cargo nextest run --profile ci --workspace --all-features
# TODO(eliza): punt on this for now because the generated JUnit report is
# missing some fields that this action needs to give good output.
# - name: Publish Test Report
Expand All @@ -146,7 +146,7 @@ jobs:
# check_name: "cargo test (Rust ${{ matrix.rust }} on ${{ matrix.os }})"
# check_title_template: "{{SUITE_NAME}}::{{TEST_NAME}}"
- name: Run doctests
run: cargo test --doc --workspace
run: cargo test --doc --workspace --all-features

test-build-wasm:
name: build tests (wasm)
Expand All @@ -158,7 +158,7 @@ jobs:
with:
target: wasm32-unknown-unknown
- name: build all tests
run: cargo test --no-run
run: cargo test --no-run --all-features

# all required checks except for the main test run (which we only require
# specific matrix combinations from)
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ rust-version = "1.65.0"
default = ["tracing-log", "metrics"]
# Enables support for exporting OpenTelemetry metrics
metrics = ["opentelemetry/metrics","opentelemetry_sdk/metrics", "smallvec"]
# Enables experimental support for OpenTelemetry gauge metrics
metrics_gauge_unstable = ["opentelemetry/otel_unstable"]

[dependencies]
opentelemetry = { version = "0.23.0", default-features = false, features = ["trace"] }
Expand Down
75 changes: 74 additions & 1 deletion src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::{collections::HashMap, fmt, sync::RwLock};
use tracing::{field::Visit, Subscriber};
use tracing_core::{Field, Interest, Metadata};

#[cfg(feature = "metrics_gauge_unstable")]
use opentelemetry::metrics::Gauge;
use opentelemetry::{
metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter},
KeyValue, Value,
Expand All @@ -21,6 +23,9 @@ const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry";
const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter.";
const METRIC_PREFIX_COUNTER: &str = "counter.";
const METRIC_PREFIX_HISTOGRAM: &str = "histogram.";
#[cfg(feature = "metrics_gauge_unstable")]
const METRIC_PREFIX_GAUGE: &str = "gauge.";

const I64_MAX: u64 = i64::MAX as u64;

#[derive(Default)]
Expand All @@ -31,6 +36,12 @@ pub(crate) struct Instruments {
f64_up_down_counter: MetricsMap<UpDownCounter<f64>>,
u64_histogram: MetricsMap<Histogram<u64>>,
f64_histogram: MetricsMap<Histogram<f64>>,
#[cfg(feature = "metrics_gauge_unstable")]
u64_gauge: MetricsMap<Gauge<u64>>,
#[cfg(feature = "metrics_gauge_unstable")]
i64_gauge: MetricsMap<Gauge<i64>>,
#[cfg(feature = "metrics_gauge_unstable")]
f64_gauge: MetricsMap<Gauge<f64>>,
}

type MetricsMap<T> = RwLock<HashMap<&'static str, T>>;
Expand All @@ -43,6 +54,12 @@ pub(crate) enum InstrumentType {
UpDownCounterF64(f64),
HistogramU64(u64),
HistogramF64(f64),
#[cfg(feature = "metrics_gauge_unstable")]
GaugeU64(u64),
#[cfg(feature = "metrics_gauge_unstable")]
GaugeI64(i64),
#[cfg(feature = "metrics_gauge_unstable")]
GaugeF64(f64),
}

impl Instruments {
Expand Down Expand Up @@ -125,6 +142,33 @@ impl Instruments {
|rec| rec.record(value, attributes),
);
}
#[cfg(feature = "metrics_gauge_unstable")]
InstrumentType::GaugeU64(value) => {
update_or_insert(
&self.u64_gauge,
metric_name,
|| meter.u64_gauge(metric_name).init(),
|rec| rec.record(value, attributes),
);
}
#[cfg(feature = "metrics_gauge_unstable")]
InstrumentType::GaugeI64(value) => {
update_or_insert(
&self.i64_gauge,
metric_name,
|| meter.i64_gauge(metric_name).init(),
|rec| rec.record(value, attributes),
);
}
#[cfg(feature = "metrics_gauge_unstable")]
InstrumentType::GaugeF64(value) => {
update_or_insert(
&self.f64_gauge,
metric_name,
|| meter.f64_gauge(metric_name).init(),
|rec| rec.record(value, attributes),
);
}
};
}
}
Expand All @@ -141,6 +185,12 @@ impl<'a> Visit for MetricVisitor<'a> {
}

fn record_u64(&mut self, field: &Field, value: u64) {
#[cfg(feature = "metrics_gauge_unstable")]
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_GAUGE) {
self.visited_metrics
.push((metric_name, InstrumentType::GaugeU64(value)));
return;
}
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
self.visited_metrics
.push((metric_name, InstrumentType::CounterU64(value)));
Expand All @@ -166,6 +216,12 @@ impl<'a> Visit for MetricVisitor<'a> {
}

fn record_f64(&mut self, field: &Field, value: f64) {
#[cfg(feature = "metrics_gauge_unstable")]
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_GAUGE) {
self.visited_metrics
.push((metric_name, InstrumentType::GaugeF64(value)));
return;
}
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
self.visited_metrics
.push((metric_name, InstrumentType::CounterF64(value)));
Expand All @@ -182,6 +238,12 @@ impl<'a> Visit for MetricVisitor<'a> {
}

fn record_i64(&mut self, field: &Field, value: i64) {
#[cfg(feature = "metrics_gauge_unstable")]
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_GAUGE) {
self.visited_metrics
.push((metric_name, InstrumentType::GaugeI64(value)));
return;
}
if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
self.visited_metrics
.push((metric_name, InstrumentType::CounterU64(value as u64)));
Expand Down Expand Up @@ -358,9 +420,20 @@ impl MetricsFilter {
meta.is_event()
&& meta.fields().iter().any(|field| {
let name = field.name();
name.starts_with(METRIC_PREFIX_COUNTER)

if name.starts_with(METRIC_PREFIX_COUNTER)
|| name.starts_with(METRIC_PREFIX_MONOTONIC_COUNTER)
|| name.starts_with(METRIC_PREFIX_HISTOGRAM)
{
return true;
}

#[cfg(feature = "metrics_gauge_unstable")]
if name.starts_with(METRIC_PREFIX_GAUGE) {
return true;
}

false
})
}
}
Expand Down
161 changes: 160 additions & 1 deletion tests/metrics_publishing.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use opentelemetry::{metrics::MetricsError, KeyValue};
use opentelemetry_sdk::{
metrics::{
data::{self, Histogram, Sum},
data::{self, Gauge, Histogram, Sum},
reader::{
AggregationSelector, DefaultAggregationSelector, DefaultTemporalitySelector,
MetricReader, TemporalitySelector,
Expand Down Expand Up @@ -115,6 +115,48 @@ async fn f64_up_down_counter_is_exported() {
exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn u64_gauge_is_exported() {
let (subscriber, exporter) =
init_subscriber("gygygy".to_string(), InstrumentKind::Gauge, 2_u64, None);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(gauge.gygygy = 1_u64);
tracing::info!(gauge.gygygy = 2_u64);
});

exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn f64_gauge_is_exported() {
let (subscriber, exporter) =
init_subscriber("huitt".to_string(), InstrumentKind::Gauge, 2_f64, None);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(gauge.huitt = 1_f64);
tracing::info!(gauge.huitt = 2_f64);
});

exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn i64_gauge_is_exported() {
let (subscriber, exporter) =
init_subscriber("samsagaz".to_string(), InstrumentKind::Gauge, 2_i64, None);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(gauge.samsagaz = 1_i64);
tracing::info!(gauge.samsagaz = 2_i64);
});

exporter.export().unwrap();
}

#[tokio::test]
async fn u64_histogram_is_exported() {
let (subscriber, exporter) = init_subscriber(
Expand Down Expand Up @@ -275,6 +317,105 @@ async fn f64_up_down_counter_with_attributes_is_exported() {
exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn f64_gauge_with_attributes_is_exported() {
let (subscriber, exporter) = init_subscriber(
"hello_world".to_string(),
InstrumentKind::Gauge,
1_f64,
Some(AttributeSet::from(
[
KeyValue::new("u64_key_1", 1_i64),
KeyValue::new("i64_key_1", 2_i64),
KeyValue::new("f64_key_1", 3_f64),
KeyValue::new("str_key_1", "foo"),
KeyValue::new("bool_key_1", true),
]
.as_slice(),
)),
);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(
gauge.hello_world = 1_f64,
u64_key_1 = 1_u64,
i64_key_1 = 2_i64,
f64_key_1 = 3_f64,
str_key_1 = "foo",
bool_key_1 = true,
);
});

exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn u64_gauge_with_attributes_is_exported() {
let (subscriber, exporter) = init_subscriber(
"hello_world".to_string(),
InstrumentKind::Gauge,
1_u64,
Some(AttributeSet::from(
[
KeyValue::new("u64_key_1", 1_i64),
KeyValue::new("i64_key_1", 2_i64),
KeyValue::new("f64_key_1", 3_f64),
KeyValue::new("str_key_1", "foo"),
KeyValue::new("bool_key_1", true),
]
.as_slice(),
)),
);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(
gauge.hello_world = 1_u64,
u64_key_1 = 1_u64,
i64_key_1 = 2_i64,
f64_key_1 = 3_f64,
str_key_1 = "foo",
bool_key_1 = true,
);
});

exporter.export().unwrap();
}

#[cfg(feature = "metrics_gauge_unstable")]
#[tokio::test]
async fn i64_gauge_with_attributes_is_exported() {
let (subscriber, exporter) = init_subscriber(
"hello_world".to_string(),
InstrumentKind::Gauge,
1_i64,
Some(AttributeSet::from(
[
KeyValue::new("u64_key_1", 1_i64),
KeyValue::new("i64_key_1", 2_i64),
KeyValue::new("f64_key_1", 3_f64),
KeyValue::new("str_key_1", "foo"),
KeyValue::new("bool_key_1", true),
]
.as_slice(),
)),
);

tracing::subscriber::with_default(subscriber, || {
tracing::info!(
gauge.hello_world = 1_i64,
u64_key_1 = 1_u64,
i64_key_1 = 2_i64,
f64_key_1 = 3_f64,
str_key_1 = "foo",
bool_key_1 = true,
);
});

exporter.export().unwrap();
}

#[tokio::test]
async fn u64_histogram_with_attributes_is_exported() {
let (subscriber, exporter) = init_subscriber(
Expand Down Expand Up @@ -517,6 +658,24 @@ where
});
}
}
InstrumentKind::Gauge => {
let gauge = metric.data.as_any().downcast_ref::<Gauge<T>>().unwrap();
assert_eq!(
self.expected_value,
gauge
.data_points
.iter()
.map(|data_point| data_point.value)
.last()
.unwrap()
);

if let Some(expected_attributes) = self.expected_attributes.as_ref() {
gauge.data_points.iter().for_each(|data_point| {
assert_eq!(expected_attributes, &data_point.attributes,)
});
}
}
InstrumentKind::Histogram => {
let histogram =
metric.data.as_any().downcast_ref::<Histogram<T>>().unwrap();
Expand Down

0 comments on commit d0123e4

Please sign in to comment.