1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! RAII guards for recording metrics that span asynchronous operations.
//!
//! In async Rust, a future can be cancelled at any `.await` point — for example, when a request
//! times out or a client disconnects. Metrics recorded only on the success path are silently
//! dropped, leaving gaps in observability. These guards ensure metrics are always recorded when
//! a scope exits, regardless of how it exits.
//!
//! # Guards
//!
//! Four guards are available, each accessed via an extension trait on the corresponding
//! OpenTelemetry instrument type. Import the traits you need:
//!
//! ```
//! use apollo_opentelemetry::metrics::{CounterExt, FutureExt, HistogramExt, UpDownCounterExt};
//! ```
//!
//! - [`track`](UpDownCounterExt::track) — increments an `UpDownCounter` on entry and
//! decrements it on drop. Use this to track concurrent operations such as active connections
//! or in-flight requests.
//!
//! - [`record_duration_on_drop`](HistogramExt::record_duration_on_drop) — starts a timer on
//! entry and records elapsed seconds to a `Histogram<f64>` on drop. Use this for operation
//! latency.
//!
//! - [`record_on_drop`](HistogramExt::record_on_drop) — records a value to a `Histogram` on
//! drop. Use this for metrics whose value is only known at the end of an operation, such as
//! response body size.
//!
//! - [`add_on_drop`](CounterExt::add_on_drop) — adds a value to a `Counter` on drop. Use this
//! to count completed operations or bytes transferred.
//!
//! In addition, [`with_guard`](FutureExt::with_guard) is available on any `Future` and ties a
//! guard's lifetime to that future — the guard drops when the future resolves or is cancelled.
//!
//! # Example
//!
//! ```
//! use opentelemetry::KeyValue;
//! use opentelemetry::metrics::{Counter, Histogram, UpDownCounter};
//! use apollo_opentelemetry::metrics::{CounterExt, HistogramExt, UpDownCounterExt};
//! # async fn read_body() -> usize { 1024 }
//!
//! async fn handle_request(
//! active_requests: UpDownCounter<i64>,
//! request_duration: Histogram<f64>,
//! response_size: Histogram<f64>,
//! bytes_sent: Counter<u64>,
//! ) {
//! // Increments immediately; decrements when this guard is dropped.
//! let _active = active_requests.track([KeyValue::new("service", "api")]);
//!
//! // Timer starts now; elapsed time is recorded when this guard is dropped.
//! let mut timer = request_duration.record_duration_on_drop([KeyValue::new("http.method", "GET")]);
//!
//! // Value recorded on drop — body size is not yet known.
//! let mut size = response_size.record_on_drop(0.0, []);
//!
//! // Counter incremented on drop.
//! let bytes = bytes_sent.add_on_drop(0, []);
//!
//! let body_bytes = read_body().await; // future may be cancelled here — all guards still record on drop
//!
//! // Outcome attributes and final values set after the await.
//! timer.set(KeyValue::new("http.status", 200_i64));
//! size.value(body_bytes as f64);
//! drop(bytes); // already holds the right value
//! }
//! # fn main() {}
//! ```
//!
//! # Setting attributes
//!
//! Attributes known upfront — such as HTTP method or service name — are passed at construction.
//! Attributes that depend on the outcome — such as a status code or error type — are set
//! afterwards via [`set`](RecordDurationGuard::set). This ensures the metric is always recorded
//! with the correct attributes even if the future is cancelled before reaching the `set` call,
//! in which case the metric is recorded with only the attributes provided at construction.
//!
//! ```
//! # use opentelemetry::metrics::Histogram;
//! # use opentelemetry::KeyValue;
//! # use apollo_opentelemetry::metrics::HistogramExt;
//! # async fn process() -> Result<(), ()> { Ok(()) }
//! async fn handle(histogram: Histogram<f64>) {
//! let mut timer = histogram.record_duration_on_drop([KeyValue::new("http.method", "POST")]);
//!
//! let result = process().await; // may be cancelled here
//!
//! let status = if result.is_ok() { 200_i64 } else { 500_i64 };
//! timer.set(KeyValue::new("http.status", status));
//! }
//! # fn main() {}
//! ```
//!
//! # Cancellation
//!
//! Call [`cancel`](RecordDurationGuard::cancel) to suppress recording entirely. This is useful
//! when an error or early-exit path should not contribute to a metric, or when a more specific
//! guard takes over on the success path.
//!
//! ```
//! # use opentelemetry::metrics::Histogram;
//! # use apollo_opentelemetry::metrics::HistogramExt;
//! # async fn process() -> bool { true }
//! async fn handle(histogram: Histogram<f64>) {
//! let mut timer = histogram.record_duration_on_drop([]);
//!
//! let success = process().await; // may be cancelled here
//!
//! if !success {
//! timer.cancel(); // don't record failed attempts
//! }
//! }
//! # fn main() {}
//! ```
pub use AddValueGuard;
pub use RecordDurationGuard;
pub use ;
pub use ;
pub use RecordValueGuard;
/// Clock used to measure elapsed time in [`RecordDurationGuard`].
///
/// In tests, use [`Clock::mock`] to control time:
///
/// ```
/// # use std::time::Duration;
/// # use opentelemetry::global;
/// # use apollo_opentelemetry::metrics::{Clock, HistogramExt};
/// # let histogram = global::meter_provider().meter("example").f64_histogram("request.duration").build();
/// let (clock, mock) = Clock::mock();
/// let _guard = histogram.record_duration_on_drop_with_clock(clock, []);
/// mock.increment(Duration::from_millis(500));
/// // guard records 0.5s on drop
/// ```
pub use Clock;
pub use TrackGuard;