1#[cfg(test)]
71use std::future::Future;
72use std::marker::PhantomData;
73#[cfg(test)]
74use std::pin::Pin;
75use std::sync::OnceLock;
76
77#[cfg(test)]
78use futures::FutureExt;
79
80use crate::metrics::aggregation::AggregateMeterProvider;
81
82pub(crate) mod aggregation;
83pub(crate) mod filter;
84
85#[derive(Debug)]
95#[doc(hidden)]
96#[must_use = "without holding the guard updown counters will immediately zero out"]
97pub struct UpDownCounterGuard<T>
98where
99 T: std::ops::Neg<Output = T> + Copy,
100{
101 counter: opentelemetry::metrics::UpDownCounter<T>,
102 value: T,
103 attributes: Vec<opentelemetry::KeyValue>,
104}
105
106impl<T> UpDownCounterGuard<T>
107where
108 T: std::ops::Neg<Output = T> + Copy,
109{
110 #[doc(hidden)]
112 pub fn new(
113 counter: std::sync::Arc<opentelemetry::metrics::UpDownCounter<T>>,
114 value: T,
115 attributes: &[opentelemetry::KeyValue],
116 ) -> Self {
117 Self {
122 counter: (*counter).clone(),
123 value,
124 attributes: attributes.to_vec(),
125 }
126 }
127}
128
129impl<T> Drop for UpDownCounterGuard<T>
130where
131 T: std::ops::Neg<Output = T> + Copy,
132{
133 fn drop(&mut self) {
138 self.counter.add(-self.value, &self.attributes);
139 }
140}
141
142#[doc(hidden)]
144pub struct NoopGuard<I, T> {
145 _phantom: PhantomData<(I, T)>,
146}
147impl<I, T> NoopGuard<I, T> {
148 #[doc(hidden)]
150 pub fn new(_instrument: I, _value: T, _attributes: &[opentelemetry::KeyValue]) -> Self {
151 NoopGuard {
152 _phantom: Default::default(),
153 }
154 }
155}
156
157#[cfg(test)]
158pub(crate) mod test_utils {
159 use std::cmp::Ordering;
160 use std::collections::BTreeMap;
161 use std::fmt::Debug;
162 use std::fmt::Display;
163 use std::sync::Arc;
164 use std::sync::OnceLock;
165 use std::sync::Weak;
166
167 use itertools::Itertools;
168 use num_traits::NumCast;
169 use num_traits::ToPrimitive;
170 use opentelemetry::Array;
171 use opentelemetry::KeyValue;
172 use opentelemetry::StringValue;
173 use opentelemetry::Value;
174 use opentelemetry_sdk::metrics::Aggregation;
175 use opentelemetry_sdk::metrics::AttributeSet;
176 use opentelemetry_sdk::metrics::InstrumentKind;
177 use opentelemetry_sdk::metrics::ManualReader;
178 use opentelemetry_sdk::metrics::MeterProviderBuilder;
179 use opentelemetry_sdk::metrics::Pipeline;
180 use opentelemetry_sdk::metrics::data::DataPoint;
181 use opentelemetry_sdk::metrics::data::Gauge;
182 use opentelemetry_sdk::metrics::data::Histogram;
183 use opentelemetry_sdk::metrics::data::HistogramDataPoint;
184 use opentelemetry_sdk::metrics::data::Metric;
185 use opentelemetry_sdk::metrics::data::ResourceMetrics;
186 use opentelemetry_sdk::metrics::data::Sum;
187 use opentelemetry_sdk::metrics::data::Temporality;
188 use opentelemetry_sdk::metrics::reader::AggregationSelector;
189 use opentelemetry_sdk::metrics::reader::MetricReader;
190 use opentelemetry_sdk::metrics::reader::TemporalitySelector;
191 use serde::Serialize;
192 use tokio::task_local;
193
194 use crate::metrics::aggregation::AggregateMeterProvider;
195 use crate::metrics::aggregation::MeterProviderType;
196 use crate::metrics::filter::FilterMeterProvider;
197 task_local! {
198 pub(crate) static AGGREGATE_METER_PROVIDER_ASYNC: OnceLock<(AggregateMeterProvider, ClonableManualReader)>;
199 }
200 thread_local! {
201 pub(crate) static AGGREGATE_METER_PROVIDER: OnceLock<(AggregateMeterProvider, ClonableManualReader)> = const { OnceLock::new() };
202 }
203
204 #[derive(Debug, Clone, Default)]
205 pub(crate) struct ClonableManualReader {
206 reader: Arc<ManualReader>,
207 }
208
209 impl TemporalitySelector for ClonableManualReader {
210 fn temporality(&self, kind: InstrumentKind) -> Temporality {
211 self.reader.temporality(kind)
212 }
213 }
214
215 impl AggregationSelector for ClonableManualReader {
216 fn aggregation(&self, kind: InstrumentKind) -> Aggregation {
217 self.reader.aggregation(kind)
218 }
219 }
220 impl MetricReader for ClonableManualReader {
221 fn register_pipeline(&self, pipeline: Weak<Pipeline>) {
222 self.reader.register_pipeline(pipeline)
223 }
224
225 fn collect(&self, rm: &mut ResourceMetrics) -> opentelemetry::metrics::Result<()> {
226 self.reader.collect(rm)
227 }
228
229 fn force_flush(&self) -> opentelemetry::metrics::Result<()> {
230 self.reader.force_flush()
231 }
232
233 fn shutdown(&self) -> opentelemetry::metrics::Result<()> {
234 self.reader.shutdown()
235 }
236 }
237
238 fn create_test_meter_provider() -> (AggregateMeterProvider, ClonableManualReader) {
239 {
240 let meter_provider = AggregateMeterProvider::default();
241 let reader = ClonableManualReader::default();
242
243 meter_provider.set(
244 MeterProviderType::Public,
245 FilterMeterProvider::all(
246 MeterProviderBuilder::default()
247 .with_reader(reader.clone())
248 .build(),
249 ),
250 );
251
252 (meter_provider, reader)
253 }
254 }
255 pub(crate) fn meter_provider_and_readers() -> (AggregateMeterProvider, ClonableManualReader) {
256 if tokio::runtime::Handle::try_current().is_ok() {
257 AGGREGATE_METER_PROVIDER_ASYNC
258 .try_with(|cell| cell.get_or_init(create_test_meter_provider).clone())
259 .unwrap_or_default()
262 } else {
263 AGGREGATE_METER_PROVIDER
264 .with(|cell| cell.get_or_init(create_test_meter_provider).clone())
265 }
266 }
267
268 pub(crate) struct Metrics {
269 resource_metrics: ResourceMetrics,
270 }
271
272 impl Default for Metrics {
273 fn default() -> Self {
274 Metrics {
275 resource_metrics: ResourceMetrics {
276 resource: Default::default(),
277 scope_metrics: vec![],
278 },
279 }
280 }
281 }
282
283 pub(crate) fn collect_metrics() -> Metrics {
284 let mut metrics = Metrics::default();
285 let (_, reader) = meter_provider_and_readers();
286 reader
287 .collect(&mut metrics.resource_metrics)
288 .expect("Failed to collect metrics. Did you forget to use `async{}.with_metrics()`? See dev-docs/metrics.md");
289 metrics
290 }
291
292 impl Metrics {
293 pub(crate) fn find(&self, name: &str) -> Option<&opentelemetry_sdk::metrics::data::Metric> {
294 self.resource_metrics
295 .scope_metrics
296 .iter()
297 .flat_map(|scope_metrics| {
298 scope_metrics
299 .metrics
300 .iter()
301 .filter(|metric| metric.name == name)
302 })
303 .next()
304 }
305
306 pub(crate) fn assert<T: NumCast + Display + 'static>(
307 &self,
308 name: &str,
309 ty: MetricType,
310 value: T,
311 count: bool,
313 attributes: &[KeyValue],
314 ) -> bool {
315 let attributes = AttributeSet::from(attributes);
316 if let Some(value) = value.to_u64()
317 && self.metric_matches(name, &ty, value, count, &attributes)
318 {
319 return true;
320 }
321
322 if let Some(value) = value.to_i64()
323 && self.metric_matches(name, &ty, value, count, &attributes)
324 {
325 return true;
326 }
327
328 if let Some(value) = value.to_f64()
329 && self.metric_matches(name, &ty, value, count, &attributes)
330 {
331 return true;
332 }
333
334 false
335 }
336
337 pub(crate) fn metric_matches<T: Debug + PartialEq + Display + ToPrimitive + 'static>(
338 &self,
339 name: &str,
340 ty: &MetricType,
341 value: T,
342 count: bool,
343 attributes: &AttributeSet,
344 ) -> bool {
345 if let Some(metric) = self.find(name) {
346 if let Some(gauge) = metric.data.as_any().downcast_ref::<Gauge<T>>() {
348 if matches!(ty, MetricType::Gauge) {
350 return gauge.data_points.iter().any(|datapoint| {
351 datapoint.value == value
352 && Self::equal_attributes(attributes, &datapoint.attributes)
353 });
354 }
355 } else if let Some(sum) = metric.data.as_any().downcast_ref::<Sum<T>>() {
356 if matches!(ty, MetricType::Counter | MetricType::UpDownCounter) {
358 return sum.data_points.iter().any(|datapoint| {
359 datapoint.value == value
360 && Self::equal_attributes(attributes, &datapoint.attributes)
361 });
362 }
363 } else if let Some(histogram) = metric.data.as_any().downcast_ref::<Histogram<T>>()
364 && matches!(ty, MetricType::Histogram)
365 {
366 if count {
367 return histogram.data_points.iter().any(|datapoint| {
368 datapoint.count == value.to_u64().unwrap()
369 && Self::equal_attributes(attributes, &datapoint.attributes)
370 });
371 } else {
372 return histogram.data_points.iter().any(|datapoint| {
373 datapoint.sum == value
374 && Self::equal_attributes(attributes, &datapoint.attributes)
375 });
376 }
377 }
378 }
379 false
380 }
381
382 pub(crate) fn metric_exists<T: Debug + PartialEq + Display + ToPrimitive + 'static>(
383 &self,
384 name: &str,
385 ty: MetricType,
386 attributes: &[KeyValue],
387 ) -> bool {
388 let attributes = AttributeSet::from(attributes);
389 if let Some(metric) = self.find(name) {
390 if let Some(gauge) = metric.data.as_any().downcast_ref::<Gauge<T>>() {
392 if matches!(ty, MetricType::Gauge) {
394 return gauge.data_points.iter().any(|datapoint| {
395 Self::equal_attributes(&attributes, &datapoint.attributes)
396 });
397 }
398 } else if let Some(sum) = metric.data.as_any().downcast_ref::<Sum<T>>() {
399 if matches!(ty, MetricType::Counter | MetricType::UpDownCounter) {
401 return sum.data_points.iter().any(|datapoint| {
402 Self::equal_attributes(&attributes, &datapoint.attributes)
403 });
404 }
405 } else if let Some(histogram) = metric.data.as_any().downcast_ref::<Histogram<T>>()
406 && matches!(ty, MetricType::Histogram)
407 {
408 return histogram.data_points.iter().any(|datapoint| {
409 Self::equal_attributes(&attributes, &datapoint.attributes)
410 });
411 }
412 }
413 false
414 }
415
416 #[allow(dead_code)]
417 pub(crate) fn all(self) -> Vec<SerdeMetric> {
418 self.resource_metrics
419 .scope_metrics
420 .into_iter()
421 .flat_map(|scope_metrics| {
422 scope_metrics.metrics.into_iter().map(|metric| {
423 let serde_metric: SerdeMetric = metric.into();
424 serde_metric
425 })
426 })
427 .sorted()
428 .collect()
429 }
430
431 #[allow(dead_code)]
432 pub(crate) fn non_zero(self) -> Vec<SerdeMetric> {
433 self.all()
434 .into_iter()
435 .filter(|m| {
436 m.data.datapoints.iter().any(|d| {
437 d.value
438 .as_ref()
439 .map(|v| v.as_f64().unwrap_or_default() > 0.0)
440 .unwrap_or_default()
441 || d.sum
442 .as_ref()
443 .map(|v| v.as_f64().unwrap_or_default() > 0.0)
444 .unwrap_or_default()
445 })
446 })
447 .collect()
448 }
449
450 fn equal_attributes(expected: &AttributeSet, actual: &[KeyValue]) -> bool {
451 if expected.iter().count() != actual.len() {
455 return false;
456 }
457 expected.iter().zip(actual.iter()).all(|((k, v), kv)| {
459 kv.key == *k
460 && (kv.value == *v || kv.value == Value::String(StringValue::from("<any>")))
461 })
462 }
463 }
464
465 #[derive(Serialize, Eq, PartialEq)]
466 pub(crate) struct SerdeMetric {
467 pub(crate) name: String,
468 #[serde(skip_serializing_if = "String::is_empty")]
469 pub(crate) description: String,
470 #[serde(skip_serializing_if = "String::is_empty")]
471 pub(crate) unit: String,
472 pub(crate) data: SerdeMetricData,
473 }
474
475 impl Ord for SerdeMetric {
476 fn cmp(&self, other: &Self) -> Ordering {
477 self.name.cmp(&other.name)
478 }
479 }
480
481 impl PartialOrd for SerdeMetric {
482 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
483 Some(self.cmp(other))
484 }
485 }
486
487 #[derive(Clone, Serialize, Eq, PartialEq, Default)]
488 pub(crate) struct SerdeMetricData {
489 pub(crate) datapoints: Vec<SerdeMetricDataPoint>,
490 }
491
492 #[derive(Clone, Serialize, Eq, PartialEq)]
493 pub(crate) struct SerdeMetricDataPoint {
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub(crate) value: Option<serde_json::Value>,
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub(crate) sum: Option<serde_json::Value>,
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub(crate) count: Option<u64>,
500 pub(crate) attributes: BTreeMap<String, serde_json::Value>,
501 }
502
503 impl Ord for SerdeMetricDataPoint {
504 fn cmp(&self, other: &Self) -> Ordering {
505 let self_string = serde_json::to_string(&self.attributes).expect("serde failed");
507 let other_string = serde_json::to_string(&other.attributes).expect("serde failed");
508 self_string.cmp(&other_string)
509 }
510 }
511
512 impl PartialOrd for SerdeMetricDataPoint {
513 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
514 Some(self.cmp(other))
515 }
516 }
517
518 impl SerdeMetricData {
519 fn extract_datapoints<T: Into<serde_json::Value> + Clone + 'static>(
520 metric_data: &mut SerdeMetricData,
521 value: &dyn opentelemetry_sdk::metrics::data::Aggregation,
522 ) {
523 if let Some(gauge) = value.as_any().downcast_ref::<Gauge<T>>() {
524 gauge.data_points.iter().for_each(|datapoint| {
525 metric_data.datapoints.push(datapoint.into());
526 });
527 }
528 if let Some(sum) = value.as_any().downcast_ref::<Sum<T>>() {
529 sum.data_points.iter().for_each(|datapoint| {
530 metric_data.datapoints.push(datapoint.into());
531 });
532 }
533 if let Some(histogram) = value.as_any().downcast_ref::<Histogram<T>>() {
534 histogram.data_points.iter().for_each(|datapoint| {
535 metric_data.datapoints.push(datapoint.into());
536 });
537 }
538 }
539 }
540
541 impl From<Metric> for SerdeMetric {
542 fn from(value: Metric) -> Self {
543 let mut serde_metric = SerdeMetric {
544 name: value.name.into_owned(),
545 description: value.description.into_owned(),
546 unit: value.unit.to_string(),
547 data: value.data.into(),
548 };
549 serde_metric.data.datapoints.sort();
551
552 if serde_metric.name.ends_with(".duration") {
554 serde_metric
555 .data
556 .datapoints
557 .iter_mut()
558 .for_each(|datapoint| {
559 if let Some(sum) = &datapoint.sum
560 && sum.as_f64().unwrap_or_default() > 0.0
561 {
562 datapoint.sum = Some(0.1.into());
563 }
564 });
565 }
566 serde_metric
567 }
568 }
569
570 impl<T> From<&DataPoint<T>> for SerdeMetricDataPoint
571 where
572 T: Into<serde_json::Value> + Clone,
573 {
574 fn from(value: &DataPoint<T>) -> Self {
575 SerdeMetricDataPoint {
576 value: Some(value.value.clone().into()),
577 sum: None,
578 count: None,
579 attributes: value
580 .attributes
581 .iter()
582 .map(|kv| (kv.key.to_string(), Self::convert(&kv.value)))
583 .collect(),
584 }
585 }
586 }
587
588 impl SerdeMetricDataPoint {
589 pub(crate) fn convert(v: &Value) -> serde_json::Value {
590 match v.clone() {
591 Value::Bool(v) => v.into(),
592 Value::I64(v) => v.into(),
593 Value::F64(v) => v.into(),
594 Value::String(v) => v.to_string().into(),
595 Value::Array(v) => match v {
596 Array::Bool(v) => v.into(),
597 Array::I64(v) => v.into(),
598 Array::F64(v) => v.into(),
599 Array::String(v) => v.iter().map(|v| v.to_string()).collect::<Vec<_>>().into(),
600 },
601 }
602 }
603 }
604
605 impl<T> From<&HistogramDataPoint<T>> for SerdeMetricDataPoint
606 where
607 T: Into<serde_json::Value> + Clone,
608 {
609 fn from(value: &HistogramDataPoint<T>) -> Self {
610 SerdeMetricDataPoint {
611 sum: Some(value.sum.clone().into()),
612 value: None,
613 count: Some(value.count),
614 attributes: value
615 .attributes
616 .iter()
617 .map(|kv| (kv.key.to_string(), Self::convert(&kv.value)))
618 .collect(),
619 }
620 }
621 }
622
623 impl From<Box<dyn opentelemetry_sdk::metrics::data::Aggregation>> for SerdeMetricData {
624 fn from(value: Box<dyn opentelemetry_sdk::metrics::data::Aggregation>) -> Self {
625 let mut metric_data = SerdeMetricData::default();
626 Self::extract_datapoints::<u64>(&mut metric_data, value.as_ref());
627 Self::extract_datapoints::<f64>(&mut metric_data, value.as_ref());
628 Self::extract_datapoints::<i64>(&mut metric_data, value.as_ref());
629 metric_data
630 }
631 }
632
633 pub(crate) enum MetricType {
634 Counter,
635 UpDownCounter,
636 Histogram,
637 Gauge,
638 }
639}
640
641#[cfg(test)]
645pub(crate) fn meter_provider_internal() -> AggregateMeterProvider {
646 test_utils::meter_provider_and_readers().0
647}
648
649#[cfg(test)]
650pub(crate) use test_utils::collect_metrics;
651
652#[cfg(not(test))]
653static AGGREGATE_METER_PROVIDER: OnceLock<AggregateMeterProvider> = OnceLock::new();
654
655#[cfg(not(test))]
658pub(crate) fn meter_provider_internal() -> AggregateMeterProvider {
659 AGGREGATE_METER_PROVIDER
660 .get_or_init(Default::default)
661 .clone()
662}
663
664pub fn meter_provider() -> impl opentelemetry::metrics::MeterProvider {
671 meter_provider_internal()
672}
673
674macro_rules! parse_attributes {
677 ($($attr_key:literal = $attr_value:expr),+) => {[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]};
678 ($($($attr_key:ident).+ = $attr_value:expr),+) => {[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]};
679 ($attrs:expr) => {$attrs};
680}
681
682#[allow(unused_macros)]
688#[deprecated(since = "TBD", note = "use `u64_counter_with_unit` instead")]
689macro_rules! u64_counter {
690 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
691 metric!(u64, counter, crate::metrics::NoopGuard, add, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*));
692 };
693
694 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
695 metric!(u64, counter, crate::metrics::NoopGuard, add, $name, $description, $value, parse_attributes!($($attrs)*));
696 };
697
698 ($name:literal, $description:literal, $value: expr) => {
699 metric!(u64, counter, crate::metrics::NoopGuard, add, $name, $description, $value, []);
700 }
701}
702
703#[allow(unused_macros)]
711macro_rules! u64_counter_with_unit {
712 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
713 metric!(u64, counter, crate::metrics::NoopGuard, add, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*));
714 };
715
716 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
717 metric!(u64, counter, crate::metrics::NoopGuard, add, $name, $description, $unit, $value, parse_attributes!($($attrs)*));
718 };
719
720 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
721 metric!(u64, counter, crate::metrics::NoopGuard, add, $name, $description, $unit, $value, []);
722 }
723}
724
725#[allow(unused_macros)]
731#[deprecated(since = "TBD", note = "use `f64_counter_with_unit` instead")]
732macro_rules! f64_counter {
733 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
734 metric!(f64, counter, crate::metrics::NoopGuard, add, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*));
735 };
736
737 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
738 metric!(f64, counter, crate::metrics::NoopGuard, add, $name, $description, $value, parse_attributes!($($attrs)*));
739 };
740
741 ($name:literal, $description:literal, $value: expr) => {
742 metric!(f64, counter, crate::metrics::NoopGuard, add, $name, $description, $value, []);
743 }
744}
745
746#[allow(unused_macros)]
754macro_rules! f64_counter_with_unit {
755 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
756 metric!(f64, counter, crate::metrics::NoopGuard, add, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*));
757 };
758
759 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
760 metric!(f64, counter, crate::metrics::NoopGuard, add, $name, $description, $unit, $value, parse_attributes!($($attrs)*));
761 };
762
763 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
764 metric!(f64, counter, crate::metrics::NoopGuard, add, $name, $description, $unit, $value, []);
765 }
766}
767
768#[allow(unused_macros)]
798#[deprecated(since = "TBD", note = "use `i64_up_down_counter_with_unit` instead")]
799macro_rules! i64_up_down_counter {
800 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
801 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*))
802 };
803
804 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
805 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, $name, $description, $value, parse_attributes!($($attrs)*))
806 };
807
808 ($name:literal, $description:literal, $value: expr) => {
809 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, $name, $description, $value, [])
810 };
811}
812
813#[allow(unused_macros)]
846macro_rules! i64_up_down_counter_with_unit {
847 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
848 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*))
849 };
850
851 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
852 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, $name, $description, $unit, $value, parse_attributes!($($attrs)*))
853 };
854
855 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
856 metric!(i64, up_down_counter, crate::metrics::UpDownCounterGuard::<i64>, add, $name, $description, $unit, $value, [])
857 }
858}
859
860#[allow(unused_macros)]
890#[deprecated(since = "TBD", note = "use `f64_up_down_counter_with_unit` instead")]
891macro_rules! f64_up_down_counter {
892 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
893 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*))
894 };
895
896 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
897 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, $name, $description, $value, parse_attributes!($($attrs)*))
898 };
899
900 ($name:literal, $description:literal, $value: expr) => {
901 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, $name, $description, $value, [])
902 };
903}
904
905#[allow(unused_macros)]
938macro_rules! f64_up_down_counter_with_unit {
939 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
940 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*))
941 };
942
943 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
944 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, $name, $description, $unit, $value, parse_attributes!($($attrs)*))
945 };
946
947 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
948 metric!(f64, up_down_counter, crate::metrics::UpDownCounterGuard::<f64>, add, $name, $description, $unit, $value, [])
949 }
950}
951
952#[allow(unused_macros)]
958#[deprecated(since = "TBD", note = "use `f64_histogram_with_unit` instead")]
959macro_rules! f64_histogram {
960 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
961 metric!(f64, histogram, crate::metrics::NoopGuard, record, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*));
962 };
963
964 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
965 metric!(f64, histogram, crate::metrics::NoopGuard,record, $name, $description, $value, parse_attributes!($($attrs)*));
966 };
967
968 ($name:literal, $description:literal, $value: expr) => {
969 metric!(f64, histogram, crate::metrics::NoopGuard,record, $name, $description, $value, []);
970 };
971}
972
973#[allow(unused_macros)]
994macro_rules! f64_histogram_with_unit {
995 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
996 metric!(f64, histogram, crate::metrics::NoopGuard, record, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*));
997 };
998
999 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
1000 metric!(f64, histogram, crate::metrics::NoopGuard, record, $name, $description, $unit, $value, parse_attributes!($($attrs)*));
1001 };
1002
1003 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
1004 metric!(f64, histogram, crate::metrics::NoopGuard, record, $name, $description, $unit, $value, []);
1005 };
1006}
1007
1008#[allow(unused_macros)]
1014#[deprecated(since = "TBD", note = "use `u64_histogram_with_unit` instead")]
1015macro_rules! u64_histogram {
1016 ($($name:ident).+, $description:literal, $value: expr, $($attrs:tt)*) => {
1017 metric!(u64, histogram, crate::metrics::NoopGuard, record, stringify!($($name).+), $description, $value, parse_attributes!($($attrs)*));
1018 };
1019
1020 ($name:literal, $description:literal, $value: expr, $($attrs:tt)*) => {
1021 metric!(u64, histogram, crate::metrics::NoopGuard, record, $name, $description, $value, parse_attributes!($($attrs)*));
1022 };
1023
1024 ($name:literal, $description:literal, $value: expr) => {
1025 metric!(u64, histogram, crate::metrics::NoopGuard, record, $name, $description, $value, []);
1026 };
1027}
1028
1029#[allow(unused_macros)]
1037macro_rules! u64_histogram_with_unit {
1038 ($($name:ident).+, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
1039 metric!(u64, histogram, crate::metrics::NoopGuard, record, stringify!($($name).+), $description, $unit, $value, parse_attributes!($($attrs)*));
1040 };
1041
1042 ($name:literal, $description:literal, $unit:literal, $value: expr, $($attrs:tt)*) => {
1043 metric!(u64, histogram, crate::metrics::NoopGuard, record, $name, $description, $unit, $value, parse_attributes!($($attrs)*));
1044 };
1045
1046 ($name:literal, $description:literal, $unit:literal, $value: expr) => {
1047 metric!(u64, histogram, crate::metrics::NoopGuard, record, $name, $description, $unit, $value, []);
1048 };
1049}
1050
1051thread_local! {
1052 #[cfg(test)]
1054 pub(crate) static CACHE_CALLSITE: std::sync::atomic::AtomicBool = const {std::sync::atomic::AtomicBool::new(false)};
1055}
1056macro_rules! metric {
1057 ($ty:ident, $instrument:ident, $guard: ty, $mutation:ident, $name:expr, $description:literal, $unit:literal, $value:expr, $attrs:expr) => {
1058 paste::paste! {
1067 {
1068 #[cfg(test)]
1070 let cache_callsite = crate::metrics::CACHE_CALLSITE.with(|cell| cell.load(std::sync::atomic::Ordering::SeqCst));
1071
1072 #[cfg(not(test))]
1074 let cache_callsite = true;
1075
1076 let create_instrument_fn = |meter: opentelemetry::metrics::Meter| {
1077 let mut builder = meter.[<$ty _ $instrument>]($name);
1078 builder = builder.with_description($description);
1079
1080 if !$unit.is_empty() {
1081 builder = builder.with_unit($unit);
1082 }
1083
1084 builder.init()
1085 };
1086
1087 if cache_callsite {
1088 static INSTRUMENT_CACHE: std::sync::OnceLock<parking_lot::Mutex<std::sync::Weak<opentelemetry::metrics::[<$instrument:camel>]<$ty>>>> = std::sync::OnceLock::new();
1089
1090 let mut instrument_guard = INSTRUMENT_CACHE
1091 .get_or_init(|| {
1092 let meter_provider = crate::metrics::meter_provider_internal();
1093 let instrument_ref = meter_provider.create_registered_instrument(|p| create_instrument_fn(p.meter("apollo/router")));
1094 parking_lot::Mutex::new(std::sync::Arc::downgrade(&instrument_ref))
1095 })
1096 .lock();
1097 let instrument = if let Some(instrument) = instrument_guard.upgrade() {
1098 drop(instrument_guard);
1100 instrument
1101 } else {
1102 let meter_provider = crate::metrics::meter_provider_internal();
1104 let instrument_ref = meter_provider.create_registered_instrument(|p| create_instrument_fn(p.meter("apollo/router")));
1105 *instrument_guard = std::sync::Arc::downgrade(&instrument_ref);
1106 drop(instrument_guard);
1108 instrument_ref
1109 };
1110 let attrs : &[opentelemetry::KeyValue] = &$attrs;
1111 instrument.$mutation($value, attrs);
1112 $guard::new(instrument.clone(), $value, attrs)
1113 }
1114 else {
1115 let meter_provider = crate::metrics::meter_provider();
1119 let meter = opentelemetry::metrics::MeterProvider::meter(&meter_provider, "apollo/router");
1120 let instrument = create_instrument_fn(meter);
1121 let attrs : &[opentelemetry::KeyValue] = &$attrs;
1122 instrument.$mutation($value, attrs);
1123 $guard::new(std::sync::Arc::new(instrument.clone()), $value, attrs)
1124 }
1125 }
1126 }
1127 };
1128
1129 ($ty:ident, $instrument:ident, $guard: ty, $mutation:ident, $name:expr, $description:literal, $value: expr, $attrs: expr) => {
1130 metric!($ty, $instrument, $guard, $mutation, $name, $description, "", $value, $attrs)
1131 }
1132}
1133
1134#[cfg(test)]
1135macro_rules! assert_metric {
1136 ($result:expr, $name:expr, $value:expr, $sum:expr, $count:expr, $attrs:expr) => {
1137 if !$result {
1138 let metric = crate::metrics::test_utils::SerdeMetric {
1139 name: $name.to_string(),
1140 description: "".to_string(),
1141 unit: "".to_string(),
1142 data: crate::metrics::test_utils::SerdeMetricData {
1143 datapoints: [crate::metrics::test_utils::SerdeMetricDataPoint {
1144 value: $value,
1145 sum: $sum,
1146 count: $count,
1147 attributes: $attrs
1148 .iter()
1149 .map(|kv: &opentelemetry::KeyValue| {
1150 (
1151 kv.key.to_string(),
1152 crate::metrics::test_utils::SerdeMetricDataPoint::convert(
1153 &kv.value,
1154 ),
1155 )
1156 })
1157 .collect::<std::collections::BTreeMap<_, _>>(),
1158 }]
1159 .to_vec(),
1160 },
1161 };
1162 panic!(
1163 "metric not found:\n{}\nmetrics present:\n{}",
1164 serde_yaml::to_string(&metric).unwrap(),
1165 serde_yaml::to_string(&crate::metrics::collect_metrics().all()).unwrap()
1166 )
1167 }
1168 };
1169}
1170
1171#[cfg(test)]
1172macro_rules! assert_no_metric {
1173 ($result:expr, $name:expr, $value:expr, $sum:expr, $count:expr, $attrs:expr) => {
1174 if $result {
1175 let metric = crate::metrics::test_utils::SerdeMetric {
1176 name: $name.to_string(),
1177 description: "".to_string(),
1178 unit: "".to_string(),
1179 data: crate::metrics::test_utils::SerdeMetricData {
1180 datapoints: [crate::metrics::test_utils::SerdeMetricDataPoint {
1181 value: $value,
1182 sum: $sum,
1183 count: $count,
1184 attributes: $attrs
1185 .iter()
1186 .map(|kv: &opentelemetry::KeyValue| {
1187 (
1188 kv.key.to_string(),
1189 crate::metrics::test_utils::SerdeMetricDataPoint::convert(
1190 &kv.value,
1191 ),
1192 )
1193 })
1194 .collect::<std::collections::BTreeMap<_, _>>(),
1195 }]
1196 .to_vec(),
1197 },
1198 };
1199 panic!(
1200 "unexpected metric found:\n{}\nmetrics present:\n{}",
1201 serde_yaml::to_string(&metric).unwrap(),
1202 serde_yaml::to_string(&crate::metrics::collect_metrics().all()).unwrap()
1203 )
1204 }
1205 };
1206}
1207
1208#[cfg(test)]
1213macro_rules! assert_counter {
1214 ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1215 let name = stringify!($($name).+);
1216 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1217 let result = crate::metrics::collect_metrics().assert(name, crate::metrics::test_utils::MetricType::Counter, $value, false, attributes);
1218 assert_metric!(result, name, Some($value.into()), None, None, &attributes);
1219 };
1220
1221 ($($name:ident).+, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1222 let name = stringify!($($name).+);
1223 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1224 let result = crate::metrics::collect_metrics().assert(name, crate::metrics::test_utils::MetricType::Counter, $value, false, attributes);
1225 assert_metric!(result, name, Some($value.into()), None, None, &attributes);
1226 };
1227
1228 ($name:literal, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1229 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1230 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Counter, $value, false, attributes);
1231 assert_metric!(result, $name, Some($value.into()), None, None, &attributes);
1232 };
1233
1234 ($name:literal, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1235 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1236 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Counter, $value, false, attributes);
1237 assert_metric!(result, $name, Some($value.into()), None, None, &attributes);
1238 };
1239
1240 ($name:literal, $value: expr, $attributes: expr) => {
1241 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Counter, $value, false, $attributes);
1242 assert_metric!(result, $name, Some($value.into()), None, None, &$attributes);
1243 };
1244
1245 ($name:literal, $value: expr) => {
1246 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Counter, $value, false, &[]);
1247 assert_metric!(result, $name, Some($value.into()), None, None, &[]);
1248 };
1249}
1250
1251#[cfg(test)]
1256macro_rules! assert_counter_not_exists {
1257
1258 ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1259 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1260 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Counter, attributes);
1261 assert_no_metric!(result, $name, None, None, None, attributes);
1262 };
1263
1264 ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1265 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1266 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Counter, attributes);
1267 assert_no_metric!(result, $name, None, None, None, attributes);
1268 };
1269
1270 ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1271 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1272 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Counter, attributes);
1273 assert_no_metric!(result, $name, None, None, None, attributes);
1274 };
1275
1276 ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1277 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1278 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Counter, attributes);
1279 assert_no_metric!(result, $name, None, None, None, attributes);
1280 };
1281
1282
1283 ($name:literal, $value: ty, $attributes: expr) => {
1284 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Counter, $attributes);
1285 assert_no_metric!(result, $name, None, None, None, &$attributes);
1286 };
1287
1288 ($name:literal, $value: ty) => {
1289 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Counter, &[]);
1290 assert_no_metric!(result, $name, None, None, None, &[]);
1291 };
1292}
1293
1294#[cfg(test)]
1299macro_rules! assert_up_down_counter {
1300
1301 ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1302 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1303 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::UpDownCounter, $value, false, attributes);
1304 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1305 };
1306
1307 ($($name:ident).+, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1308 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1309 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::UpDownCounter, $value, false, attributes);
1310 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1311 };
1312
1313 ($name:literal, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1314 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1315 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::UpDownCounter, $value, false, attributes);
1316 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1317 };
1318
1319 ($name:literal, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1320 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1321 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::UpDownCounter, $value, false, attributes);
1322 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1323 };
1324
1325 ($name:literal, $value: expr) => {
1326 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::UpDownCounter, $value, false, &[]);
1327 assert_metric!(result, $name, Some($value.into()), None, None, &[]);
1328 };
1329}
1330
1331#[cfg(test)]
1336macro_rules! assert_gauge {
1337
1338 ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1339 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1340 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Gauge, $value, false, attributes);
1341 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1342 };
1343
1344 ($($name:ident).+, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1345 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1346 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Gauge, $value, false, attributes);
1347 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1348 };
1349
1350 ($name:literal, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1351 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1352 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Gauge, $value, false, attributes);
1353 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1354 };
1355
1356 ($name:literal, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1357 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1358 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Gauge, $value, false, attributes);
1359 assert_metric!(result, $name, Some($value.into()), None, None, attributes);
1360 };
1361
1362 ($name:literal, $value: expr) => {
1363 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Gauge, $value, false, &[]);
1364 assert_metric!(result, $name, Some($value.into()), None, None, &[]);
1365 };
1366}
1367
1368#[cfg(test)]
1369macro_rules! assert_histogram_count {
1370
1371 ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1372 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1373 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, $value, true, attributes);
1374 assert_metric!(result, $name, None, Some($value.into()), Some(num_traits::ToPrimitive::to_u64(&$value).expect("count should be convertible to u64")), attributes);
1375 };
1376
1377 ($($name:ident).+, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1378 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1379 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, $value, true, attributes);
1380 assert_metric!(result, $name, None, Some($value.into()), Some(num_traits::ToPrimitive::to_u64(&$value).expect("count should be convertible to u64")), attributes);
1381 };
1382
1383 ($name:literal, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1384 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1385 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, true, attributes);
1386 assert_metric!(result, $name, None, Some($value.into()), Some(num_traits::ToPrimitive::to_u64(&$value).expect("count should be convertible to u64")), attributes);
1387 };
1388
1389 ($name:literal, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1390 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1391 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, attributes);
1392 assert_metric!(result, $name, None, Some($value.into()), Some(num_traits::ToPrimitive::to_u64(&$value).expect("count should be convertible to u64")), attributes);
1393 };
1394
1395 ($name:literal, $value: expr) => {
1396 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, true, &[]);
1397 assert_metric!(result, $name, None, Some($value.into()), Some(num_traits::ToPrimitive::to_u64(&$value).expect("count should be convertible to u64")), &[]);
1398 };
1399}
1400
1401#[cfg(test)]
1406macro_rules! assert_histogram_sum {
1407
1408 ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1409 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1410 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, $value, false, attributes);
1411 assert_metric!(result, $name, None, Some($value.into()), None, attributes);
1412 };
1413
1414 ($($name:ident).+, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1415 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1416 let result = crate::metrics::collect_metrics().assert(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, $value, false, attributes);
1417 assert_metric!(result, $name, None, Some($value.into()), None, attributes);
1418 };
1419
1420 ($name:literal, $value: expr, $($attr_key:literal = $attr_value:expr),+) => {
1421 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1422 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, false, attributes);
1423 assert_metric!(result, $name, None, Some($value.into()), None, attributes);
1424 };
1425
1426 ($name:literal, $value: expr, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1427 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1428 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, false, attributes);
1429 assert_metric!(result, $name, None, Some($value.into()), None, attributes);
1430 };
1431
1432 ($name:literal, $value: expr) => {
1433 let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, false, &[]);
1434 assert_metric!(result, $name, None, Some($value.into()), None, &[]);
1435 };
1436}
1437
1438#[cfg(test)]
1443macro_rules! assert_histogram_exists {
1444
1445 ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1446 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1447 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, attributes);
1448 assert_metric!(result, $name, None, None, None, attributes);
1449 };
1450
1451 ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1452 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1453 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, attributes);
1454 assert_metric!(result, $name, None, None, None, attributes);
1455 };
1456
1457 ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1458 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1459 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, attributes);
1460 assert_metric!(result, $name, None, None, None, attributes);
1461 };
1462
1463 ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1464 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1465 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, attributes);
1466 assert_metric!(result, $name, None, None, None, attributes);
1467 };
1468
1469 ($name:literal, $value: ty) => {
1470 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &[]);
1471 assert_metric!(result, $name, None, None, None, &[]);
1472 };
1473}
1474
1475#[cfg(test)]
1480macro_rules! assert_histogram_not_exists {
1481
1482 ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1483 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1484 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, attributes);
1485 assert_no_metric!(result, $name, None, None, None, attributes);
1486 };
1487
1488 ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1489 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1490 let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, attributes);
1491 assert_no_metric!(result, $name, None, None, None, attributes);
1492 };
1493
1494 ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => {
1495 let attributes = &[$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+];
1496 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, attributes);
1497 assert_no_metric!(result, $name, None, None, None, attributes);
1498 };
1499
1500 ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => {
1501 let attributes = &[$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+];
1502 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, attributes);
1503 assert_no_metric!(result, $name, None, None, None, attributes);
1504 };
1505
1506 ($name:literal, $value: ty) => {
1507 let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &[]);
1508 assert_no_metric!(result, $name, None, None, None, &[]);
1509 };
1510}
1511
1512#[cfg(test)]
1520#[allow(unused_macros)]
1521macro_rules! assert_metrics_snapshot {
1522 ($file_name: expr) => {
1523 insta::with_settings!({sort_maps => true, snapshot_suffix => $file_name}, {
1524 let metrics = crate::metrics::collect_metrics();
1525 insta::assert_yaml_snapshot!(&metrics.all());
1526 });
1527
1528 };
1529 () => {
1530 insta::with_settings!({sort_maps => true}, {
1531 let metrics = crate::metrics::collect_metrics();
1532 insta::assert_yaml_snapshot!(&metrics.all());
1533 });
1534 };
1535}
1536
1537#[cfg(test)]
1542#[allow(unused_macros)]
1543macro_rules! assert_non_zero_metrics_snapshot {
1544 ($file_name: expr) => {
1545 insta::with_settings!({sort_maps => true, snapshot_suffix => $file_name}, {
1546 let metrics = crate::metrics::collect_metrics();
1547 insta::assert_yaml_snapshot!(&metrics.non_zero());
1548 });
1549 };
1550 () => {
1551 insta::with_settings!({sort_maps => true}, {
1552 let metrics = crate::metrics::collect_metrics();
1553 insta::assert_yaml_snapshot!(&metrics.non_zero());
1554 });
1555 };
1556}
1557
1558#[cfg(test)]
1559pub(crate) type MetricFuture<T> = Pin<Box<dyn Future<Output = <T as Future>::Output>>>;
1560
1561pub(crate) trait FutureMetricsExt<T> {
1563 #[cfg(test)]
1582 fn with_metrics(
1583 self,
1584 ) -> tokio::task::futures::TaskLocalFuture<
1585 OnceLock<(AggregateMeterProvider, test_utils::ClonableManualReader)>,
1586 MetricFuture<Self>,
1587 >
1588 where
1589 Self: Sized + Future + 'static,
1590 <Self as Future>::Output: 'static,
1591 {
1592 test_utils::AGGREGATE_METER_PROVIDER_ASYNC.scope(
1593 Default::default(),
1594 async move {
1595 let _ = meter_provider_internal();
1597 let result = self.await;
1598 let _ = tokio::task::spawn_blocking(|| {
1599 meter_provider_internal().shutdown();
1600 })
1601 .await;
1602 result
1603 }
1604 .boxed_local(),
1605 )
1606 }
1607
1608 #[cfg(test)]
1622 fn with_current_meter_provider(
1623 self,
1624 ) -> tokio::task::futures::TaskLocalFuture<
1625 OnceLock<(AggregateMeterProvider, test_utils::ClonableManualReader)>,
1626 Self,
1627 >
1628 where
1629 Self: Sized + Future + 'static,
1630 <Self as Future>::Output: 'static,
1631 {
1632 let meter_provider_set = test_utils::AGGREGATE_METER_PROVIDER_ASYNC
1634 .try_with(|_| {})
1635 .is_ok();
1636 if meter_provider_set {
1637 test_utils::AGGREGATE_METER_PROVIDER_ASYNC
1638 .scope(test_utils::AGGREGATE_METER_PROVIDER_ASYNC.get(), self)
1639 } else {
1640 test_utils::AGGREGATE_METER_PROVIDER_ASYNC.scope(Default::default(), self)
1641 }
1642 }
1643
1644 #[cfg(not(test))]
1645 fn with_current_meter_provider(self) -> Self
1646 where
1647 Self: Sized + Future + 'static,
1648 {
1649 self
1651 }
1652}
1653
1654impl<T> FutureMetricsExt<T> for T where T: Future {}
1655
1656#[cfg(test)]
1657mod test {
1658 use opentelemetry::KeyValue;
1659 use opentelemetry::metrics::MeterProvider;
1660
1661 use crate::metrics::FutureMetricsExt;
1662 use crate::metrics::meter_provider;
1663 use crate::metrics::meter_provider_internal;
1664
1665 fn assert_unit(name: &str, unit: &str) {
1666 let collected_metrics = crate::metrics::collect_metrics();
1667 let metric = collected_metrics.find(name).unwrap();
1668 assert_eq!(metric.unit, unit);
1669 }
1670
1671 #[test]
1672 fn test_gauge() {
1673 let _gauge = meter_provider()
1675 .meter("test")
1676 .u64_observable_gauge("test")
1677 .with_callback(|m| m.observe(5, &[]))
1678 .init();
1679 assert_gauge!("test", 5);
1680 }
1681
1682 #[test]
1683 fn test_gauge_record() {
1684 let gauge = meter_provider().meter("test").u64_gauge("test").init();
1685 gauge.record(5, &[]);
1686 assert_gauge!("test", 5);
1687 }
1688
1689 #[test]
1690 fn test_no_attributes() {
1691 u64_counter!("test", "test description", 1);
1692 assert_counter!("test", 1);
1693 }
1694
1695 #[test]
1696 fn test_dynamic_attributes() {
1697 let attributes = vec![KeyValue::new("attr", "val")];
1698 u64_counter!("test", "test description", 1, attributes);
1699 assert_counter!("test", 1, "attr" = "val");
1700 assert_counter!("test", 1, &attributes);
1701 }
1702
1703 #[test]
1704 fn test_multiple_calls() {
1705 fn my_method(val: &'static str) {
1706 u64_counter!("test", "test description", 1, "attr" = val);
1707 }
1708
1709 my_method("jill");
1710 my_method("jill");
1711 my_method("bob");
1712 assert_counter!("test", 2, "attr" = "jill");
1713 assert_counter!("test", 1, "attr" = "bob");
1714 }
1715
1716 #[test]
1717 fn test_non_async() {
1718 u64_counter!("test", "test description", 1, "attr" = "val");
1720 assert_counter!("test", 1, "attr" = "val");
1721 }
1722
1723 #[tokio::test(flavor = "multi_thread")]
1724 async fn test_async_multi() {
1725 async {
1727 u64_counter!("test", "test description", 1, "attr" = "val");
1728 assert_counter!("test", 1, "attr" = "val");
1729 }
1730 .with_metrics()
1731 .await;
1732 }
1733
1734 #[tokio::test]
1735 async fn test_async_single() {
1736 async {
1737 u64_counter!("test", "test description", 1, "attr" = "val");
1739 assert_counter!("test", 1, "attr" = "val");
1740 }
1741 .with_metrics()
1742 .await;
1743 }
1744
1745 #[tokio::test]
1746 async fn test_u64_counter() {
1747 async {
1748 u64_counter!("test", "test description", 1, attr = "val");
1749 u64_counter!("test", "test description", 1, attr.test = "val");
1750 u64_counter!("test", "test description", 1, attr.test_underscore = "val");
1751 u64_counter!(
1752 test.dot,
1753 "test description",
1754 1,
1755 "attr.test_underscore" = "val"
1756 );
1757 u64_counter!(
1758 test.dot,
1759 "test description",
1760 1,
1761 attr.test_underscore = "val"
1762 );
1763 assert_counter!("test", 1, "attr" = "val");
1764 assert_counter!("test", 1, "attr.test" = "val");
1765 assert_counter!("test", 1, attr.test_underscore = "val");
1766 assert_counter!(test.dot, 2, attr.test_underscore = "val");
1767 assert_counter!(test.dot, 2, "attr.test_underscore" = "val");
1768 }
1769 .with_metrics()
1770 .await;
1771 }
1772
1773 #[tokio::test]
1774 async fn test_f64_counter() {
1775 async {
1776 f64_counter!("test", "test description", 1.5, "attr" = "val");
1777 assert_counter!("test", 1.5, "attr" = "val");
1778 }
1779 .with_metrics()
1780 .await;
1781 }
1782
1783 #[tokio::test]
1784 async fn test_i64_up_down_counter() {
1785 async {
1786 let _guard = i64_up_down_counter!("test", "test description", 1, "attr" = "val");
1787 assert_up_down_counter!("test", 1, "attr" = "val");
1788 }
1789 .with_metrics()
1790 .await;
1791 }
1792
1793 #[tokio::test]
1794 async fn test_f64_up_down_counter() {
1795 async {
1796 let _guard = f64_up_down_counter!("test", "test description", 1.5, "attr" = "val");
1797 assert_up_down_counter!("test", 1.5, "attr" = "val");
1798 }
1799 .with_metrics()
1800 .await;
1801 }
1802
1803 #[tokio::test]
1804 async fn test_i64_up_down_counter_guard_auto_decrement() {
1805 async {
1806 {
1808 let _guard =
1809 i64_up_down_counter!("test_guard", "test description", 1, "attr" = "val");
1810 assert_up_down_counter!("test_guard", 1, "attr" = "val");
1811 }
1812 assert_up_down_counter!("test_guard", 0, "attr" = "val");
1814 }
1815 .with_metrics()
1816 .await;
1817 }
1818
1819 #[tokio::test]
1820 async fn test_i64_up_down_counter_guard_multiple() {
1821 async {
1822 let _guard1 = i64_up_down_counter!("test_multi", "test description", 1, "attr" = "val");
1824 assert_up_down_counter!("test_multi", 1, "attr" = "val");
1825
1826 let _guard2 = i64_up_down_counter!("test_multi", "test description", 1, "attr" = "val");
1827 assert_up_down_counter!("test_multi", 2, "attr" = "val");
1828
1829 let _guard3 = i64_up_down_counter!("test_multi", "test description", 1, "attr" = "val");
1830 assert_up_down_counter!("test_multi", 3, "attr" = "val");
1831
1832 drop(_guard2);
1833 assert_up_down_counter!("test_multi", 2, "attr" = "val");
1834
1835 drop(_guard1);
1836 assert_up_down_counter!("test_multi", 1, "attr" = "val");
1837
1838 drop(_guard3);
1839 assert_up_down_counter!("test_multi", 0, "attr" = "val");
1840 }
1841 .with_metrics()
1842 .await;
1843 }
1844
1845 #[tokio::test]
1846 async fn test_i64_up_down_counter_guard_different_attributes() {
1847 async {
1848 let _guard1 =
1850 i64_up_down_counter!("test_attrs", "test description", 1, "attr" = "val1");
1851 let _guard2 =
1852 i64_up_down_counter!("test_attrs", "test description", 1, "attr" = "val2");
1853
1854 assert_up_down_counter!("test_attrs", 1, "attr" = "val1");
1855 assert_up_down_counter!("test_attrs", 1, "attr" = "val2");
1856
1857 drop(_guard1);
1858 assert_up_down_counter!("test_attrs", 0, "attr" = "val1");
1859 assert_up_down_counter!("test_attrs", 1, "attr" = "val2");
1860
1861 drop(_guard2);
1862 assert_up_down_counter!("test_attrs", 0, "attr" = "val2");
1863 }
1864 .with_metrics()
1865 .await;
1866 }
1867
1868 #[tokio::test]
1869 async fn test_f64_up_down_counter_guard_auto_decrement() {
1870 async {
1871 {
1873 let _guard =
1874 f64_up_down_counter!("test_f64_guard", "test description", 2.5, "attr" = "val");
1875 assert_up_down_counter!("test_f64_guard", 2.5, "attr" = "val");
1876 }
1877 assert_up_down_counter!("test_f64_guard", 0.0, "attr" = "val");
1879 }
1880 .with_metrics()
1881 .await;
1882 }
1883
1884 #[tokio::test]
1885 async fn test_u64_histogram() {
1886 async {
1887 u64_histogram!("test", "test description", 1, "attr" = "val");
1888 assert_histogram_sum!("test", 1, "attr" = "val");
1889 }
1890 .with_metrics()
1891 .await;
1892 }
1893
1894 #[tokio::test]
1895 async fn test_f64_histogram() {
1896 async {
1897 f64_histogram!("test", "test description", 1.0, "attr" = "val");
1898 assert_histogram_sum!("test", 1, "attr" = "val");
1899 }
1900 .with_metrics()
1901 .await;
1902 }
1903
1904 #[tokio::test]
1905 #[should_panic]
1906 async fn test_type_histogram() {
1907 async {
1908 f64_histogram!("test", "test description", 1.0, "attr" = "val");
1909 assert_counter!("test", 1, "attr" = "val");
1910 }
1911 .with_metrics()
1912 .await;
1913 }
1914
1915 #[tokio::test]
1916 #[should_panic]
1917 async fn test_type_counter() {
1918 async {
1919 f64_counter!("test", "test description", 1.0, "attr" = "val");
1920 assert_histogram_sum!("test", 1, "attr" = "val");
1921 }
1922 .with_metrics()
1923 .await;
1924 }
1925
1926 #[tokio::test]
1927 #[should_panic]
1928 async fn test_type_up_down_counter() {
1929 async {
1930 let _ = f64_up_down_counter!("test", "test description", 1.0, "attr" = "val");
1931 assert_histogram_sum!("test", 1, "attr" = "val");
1932 }
1933 .with_metrics()
1934 .await;
1935 }
1936
1937 #[tokio::test]
1938 #[should_panic]
1939 async fn test_type_gauge() {
1940 async {
1941 let _gauge = meter_provider()
1942 .meter("test")
1943 .u64_observable_gauge("test")
1944 .with_callback(|m| m.observe(5, &[]))
1945 .init();
1946 assert_histogram_sum!("test", 1, "attr" = "val");
1947 }
1948 .with_metrics()
1949 .await;
1950 }
1951
1952 #[test]
1953 fn parse_attributes_should_handle_multiple_input_types() {
1954 let variable = 123;
1955 let parsed_idents = parse_attributes!(hello = "world", my.variable = variable);
1956 let parsed_literals = parse_attributes!("hello" = "world", "my.variable" = variable);
1957 let parsed_provided = parse_attributes!(vec![
1958 KeyValue::new("hello", "world"),
1959 KeyValue::new("my.variable", variable)
1960 ]);
1961
1962 assert_eq!(parsed_idents, parsed_literals);
1963 assert_eq!(parsed_idents.as_slice(), parsed_provided.as_slice());
1964 assert_eq!(parsed_literals.as_slice(), parsed_provided.as_slice());
1965 }
1966
1967 #[test]
1968 fn test_callsite_caching() {
1969 super::CACHE_CALLSITE.with(|cell| cell.store(true, std::sync::atomic::Ordering::SeqCst));
1973 fn test() {
1974 u64_counter!("test", "test description", 1, "attr" = "val");
1976 }
1977
1978 assert_eq!(meter_provider_internal().registered_instruments(), 0);
1980
1981 test();
1983 assert_counter!("test", 1, "attr" = "val");
1984 assert_eq!(meter_provider_internal().registered_instruments(), 1);
1985
1986 test();
1988 assert_counter!("test", 2, "attr" = "val");
1989 assert_eq!(meter_provider_internal().registered_instruments(), 1);
1990
1991 meter_provider_internal().invalidate();
1993 assert_eq!(meter_provider_internal().registered_instruments(), 0);
1994
1995 test();
1997 assert_eq!(meter_provider_internal().registered_instruments(), 1);
1998
1999 test();
2001 assert_eq!(meter_provider_internal().registered_instruments(), 1);
2002 }
2003
2004 #[tokio::test]
2005 async fn test_f64_histogram_with_unit() {
2006 async {
2007 f64_histogram_with_unit!("test", "test description", "m/s", 1.0, "attr" = "val");
2008 assert_histogram_sum!("test", 1, "attr" = "val");
2009 assert_unit("test", "m/s");
2010 }
2011 .with_metrics()
2012 .await;
2013 }
2014
2015 #[tokio::test]
2016 async fn test_u64_counter_with_unit() {
2017 async {
2018 u64_counter_with_unit!("test", "test description", "Hz", 1, attr = "val");
2019 assert_counter!("test", 1, "attr" = "val");
2020 assert_unit("test", "Hz");
2021 }
2022 .with_metrics()
2023 .await;
2024 }
2025
2026 #[tokio::test]
2027 async fn test_i64_up_down_counter_with_unit() {
2028 async {
2029 let _guard = i64_up_down_counter_with_unit!(
2030 "test",
2031 "test description",
2032 "{request}",
2033 1,
2034 attr = "val"
2035 );
2036 assert_up_down_counter!("test", 1, "attr" = "val");
2037 assert_unit("test", "{request}");
2038 }
2039 .with_metrics()
2040 .await;
2041 }
2042
2043 #[tokio::test]
2044 async fn test_f64_up_down_counter_with_unit() {
2045 async {
2046 let _guard = f64_up_down_counter_with_unit!(
2047 "test",
2048 "test description",
2049 "kg",
2050 1.5,
2051 "attr" = "val"
2052 );
2053 assert_up_down_counter!("test", 1.5, "attr" = "val");
2054 assert_unit("test", "kg");
2055 }
2056 .with_metrics()
2057 .await;
2058 }
2059
2060 #[tokio::test]
2061 async fn test_u64_histogram_with_unit() {
2062 async {
2063 u64_histogram_with_unit!("test", "test description", "{packet}", 1, "attr" = "val");
2064 assert_histogram_sum!("test", 1, "attr" = "val");
2065 assert_unit("test", "{packet}");
2066 }
2067 .with_metrics()
2068 .await;
2069 }
2070
2071 #[tokio::test]
2072 async fn test_f64_counter_with_unit() {
2073 async {
2074 f64_counter_with_unit!("test", "test description", "s", 1.5, "attr" = "val");
2075 assert_counter!("test", 1.5, "attr" = "val");
2076 assert_unit("test", "s");
2077 }
2078 .with_metrics()
2079 .await;
2080 }
2081
2082 #[tokio::test]
2083 async fn test_metrics_across_tasks() {
2084 async {
2085 u64_counter!("apollo.router.test", "metric", 1);
2087 assert_counter!("apollo.router.test", 1);
2088
2089 let handle = tokio::spawn(
2091 async move {
2092 u64_counter!("apollo.router.test", "metric", 2);
2093 }
2094 .with_current_meter_provider(),
2095 );
2096
2097 handle.await.unwrap();
2099
2100 assert_counter!("apollo.router.test", 3);
2102 }
2103 .with_metrics()
2104 .await;
2105 }
2106}