Skip to content

Commit 4840a9c

Browse files
authored
Allow emitting duration metrics in seconds (#706)
* Add f64 metric types * Add Duration histogram
1 parent 9d8d9f8 commit 4840a9c

File tree

7 files changed

+480
-180
lines changed

7 files changed

+480
-180
lines changed

client/src/metrics.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
time::{Duration, Instant},
77
};
88
use temporal_sdk_core_api::telemetry::metrics::{
9-
CoreMeter, Counter, Histogram, MetricAttributes, MetricKeyValue, MetricParameters,
9+
CoreMeter, Counter, HistogramDuration, MetricAttributes, MetricKeyValue, MetricParameters,
1010
TemporalMeter,
1111
};
1212
use tonic::{body::BoxBody, transport::Channel};
@@ -27,8 +27,8 @@ pub struct MetricsContext {
2727
long_svc_request: Arc<dyn Counter>,
2828
long_svc_request_failed: Arc<dyn Counter>,
2929

30-
svc_request_latency: Arc<dyn Histogram>,
31-
long_svc_request_latency: Arc<dyn Histogram>,
30+
svc_request_latency: Arc<dyn HistogramDuration>,
31+
long_svc_request_latency: Arc<dyn HistogramDuration>,
3232
}
3333

3434
impl MetricsContext {
@@ -57,14 +57,14 @@ impl MetricsContext {
5757
description: "Count of long-poll request failures by rpc name".into(),
5858
unit: "".into(),
5959
}),
60-
svc_request_latency: meter.histogram(MetricParameters {
60+
svc_request_latency: meter.histogram_duration(MetricParameters {
6161
name: "request_latency".into(),
62-
unit: "ms".into(),
62+
unit: "duration".into(),
6363
description: "Histogram of client request latencies".into(),
6464
}),
65-
long_svc_request_latency: meter.histogram(MetricParameters {
65+
long_svc_request_latency: meter.histogram_duration(MetricParameters {
6666
name: "long_request_latency".into(),
67-
unit: "ms".into(),
67+
unit: "duration".into(),
6868
description: "Histogram of client long-poll request latencies".into(),
6969
}),
7070
meter,
@@ -103,11 +103,9 @@ impl MetricsContext {
103103
/// Record service request latency
104104
pub(crate) fn record_svc_req_latency(&self, dur: Duration) {
105105
if self.poll_is_long {
106-
self.long_svc_request_latency
107-
.record(dur.as_millis() as u64, &self.kvs);
106+
self.long_svc_request_latency.record(dur, &self.kvs);
108107
} else {
109-
self.svc_request_latency
110-
.record(dur.as_millis() as u64, &self.kvs);
108+
self.svc_request_latency.record(dur, &self.kvs);
111109
}
112110
}
113111
}

core-api/src/telemetry.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ pub struct OtelCollectorOptions {
6161
/// Specifies the aggregation temporality for metric export. Defaults to cumulative.
6262
#[builder(default = "MetricTemporality::Cumulative")]
6363
pub metric_temporality: MetricTemporality,
64-
// A map of tags to be applied to all metrics
64+
/// A map of tags to be applied to all metrics
6565
#[builder(default)]
6666
pub global_tags: HashMap<String, String>,
67+
/// If set to true, use f64 seconds for durations instead of u64 milliseconds
68+
#[builder(default)]
69+
pub use_seconds_for_durations: bool,
6770
}
6871

6972
/// Options for exporting metrics to Prometheus
@@ -80,6 +83,9 @@ pub struct PrometheusExporterOptions {
8083
/// Ex: "_milliseconds".
8184
#[builder(default = "false")]
8285
pub unit_suffix: bool,
86+
/// If set to true, use f64 seconds for durations instead of u64 milliseconds
87+
#[builder(default)]
88+
pub use_seconds_for_durations: bool,
8389
}
8490

8591
/// Control where logs go

core-api/src/telemetry/metrics.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
borrow::Cow,
44
fmt::Debug,
55
sync::{Arc, OnceLock},
6+
time::Duration,
67
};
78

89
/// Implementors of this trait are expected to be defined in each language's bridge.
@@ -23,7 +24,14 @@ pub trait CoreMeter: Send + Sync + Debug {
2324
) -> MetricAttributes;
2425
fn counter(&self, params: MetricParameters) -> Arc<dyn Counter>;
2526
fn histogram(&self, params: MetricParameters) -> Arc<dyn Histogram>;
27+
fn histogram_f64(&self, params: MetricParameters) -> Arc<dyn HistogramF64>;
28+
/// Create a histogram which records Durations. Implementations should choose to emit in
29+
/// either milliseconds or seconds depending on how they have been configured.
30+
/// [MetricParameters::unit] should be overwritten by implementations to be `ms` or `s`
31+
/// accordingly.
32+
fn histogram_duration(&self, params: MetricParameters) -> Arc<dyn HistogramDuration>;
2633
fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge>;
34+
fn gauge_f64(&self, params: MetricParameters) -> Arc<dyn GaugeF64>;
2735
}
2836

2937
#[derive(Debug, Clone, derive_builder::Builder)]
@@ -74,9 +82,22 @@ impl CoreMeter for Arc<dyn CoreMeter> {
7482
fn histogram(&self, params: MetricParameters) -> Arc<dyn Histogram> {
7583
self.as_ref().histogram(params)
7684
}
85+
86+
fn histogram_f64(&self, params: MetricParameters) -> Arc<dyn HistogramF64> {
87+
self.as_ref().histogram_f64(params)
88+
}
89+
90+
fn histogram_duration(&self, params: MetricParameters) -> Arc<dyn HistogramDuration> {
91+
self.as_ref().histogram_duration(params)
92+
}
93+
7794
fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge> {
7895
self.as_ref().gauge(params)
7996
}
97+
98+
fn gauge_f64(&self, params: MetricParameters) -> Arc<dyn GaugeF64> {
99+
self.as_ref().gauge_f64(params)
100+
}
80101
}
81102

82103
/// Attributes which are provided every time a call to record a specific metric is made.
@@ -158,22 +179,34 @@ pub trait Histogram: Send + Sync {
158179
// When referring to durations, this value is in millis
159180
fn record(&self, value: u64, attributes: &MetricAttributes);
160181
}
182+
pub trait HistogramF64: Send + Sync {
183+
// When referring to durations, this value is in seconds
184+
fn record(&self, value: f64, attributes: &MetricAttributes);
185+
}
186+
pub trait HistogramDuration: Send + Sync {
187+
fn record(&self, value: Duration, attributes: &MetricAttributes);
188+
}
161189

162190
pub trait Gauge: Send + Sync {
163191
// When referring to durations, this value is in millis
164192
fn record(&self, value: u64, attributes: &MetricAttributes);
165193
}
194+
pub trait GaugeF64: Send + Sync {
195+
// When referring to durations, this value is in seconds
196+
fn record(&self, value: f64, attributes: &MetricAttributes);
197+
}
166198

167199
#[derive(Debug, Clone)]
168200
pub enum MetricEvent<I: BufferInstrumentRef> {
169201
Create {
170202
params: MetricParameters,
171-
/// One you receive this event, call `set` on this with the initialized instrument reference
203+
/// Once you receive this event, call `set` on this with the initialized instrument
204+
/// reference
172205
populate_into: LazyBufferInstrument<I>,
173206
kind: MetricKind,
174207
},
175208
CreateAttributes {
176-
/// One you receive this event, call `set` on this with the initialized attributes
209+
/// Once you receive this event, call `set` on this with the initialized attributes
177210
populate_into: BufferAttributes,
178211
/// If not `None`, use these already-initialized attributes as the base (extended with
179212
/// `attributes`) for the ones you are about to initialize.
@@ -190,14 +223,18 @@ pub enum MetricEvent<I: BufferInstrumentRef> {
190223
pub enum MetricKind {
191224
Counter,
192225
Gauge,
226+
GaugeF64,
193227
Histogram,
228+
HistogramF64,
229+
HistogramDuration,
194230
}
195231
#[derive(Debug, Clone, Copy)]
196232
pub enum MetricUpdateVal {
197-
// Currently all deltas are natural numbers
198233
Delta(u64),
199-
// Currently all values are natural numbers
234+
DeltaF64(f64),
200235
Value(u64),
236+
ValueF64(f64),
237+
Duration(Duration),
201238
}
202239

203240
pub trait MetricCallBufferer<I: BufferInstrumentRef>: Send + Sync {
@@ -260,9 +297,21 @@ impl CoreMeter for NoOpCoreMeter {
260297
Arc::new(NoOpInstrument)
261298
}
262299

300+
fn histogram_f64(&self, _: MetricParameters) -> Arc<dyn HistogramF64> {
301+
Arc::new(NoOpInstrument)
302+
}
303+
304+
fn histogram_duration(&self, _: MetricParameters) -> Arc<dyn HistogramDuration> {
305+
Arc::new(NoOpInstrument)
306+
}
307+
263308
fn gauge(&self, _: MetricParameters) -> Arc<dyn Gauge> {
264309
Arc::new(NoOpInstrument)
265310
}
311+
312+
fn gauge_f64(&self, _: MetricParameters) -> Arc<dyn GaugeF64> {
313+
Arc::new(NoOpInstrument)
314+
}
266315
}
267316

268317
pub struct NoOpInstrument;
@@ -272,9 +321,18 @@ impl Counter for NoOpInstrument {
272321
impl Histogram for NoOpInstrument {
273322
fn record(&self, _: u64, _: &MetricAttributes) {}
274323
}
324+
impl HistogramF64 for NoOpInstrument {
325+
fn record(&self, _: f64, _: &MetricAttributes) {}
326+
}
327+
impl HistogramDuration for NoOpInstrument {
328+
fn record(&self, _: Duration, _: &MetricAttributes) {}
329+
}
275330
impl Gauge for NoOpInstrument {
276331
fn record(&self, _: u64, _: &MetricAttributes) {}
277332
}
333+
impl GaugeF64 for NoOpInstrument {
334+
fn record(&self, _: f64, _: &MetricAttributes) {}
335+
}
278336

279337
#[derive(Debug, Clone)]
280338
pub struct NoOpAttributes;
@@ -331,4 +389,17 @@ mod otel_impls {
331389
}
332390
}
333391
}
392+
393+
impl HistogramF64 for metrics::Histogram<f64> {
394+
fn record(&self, value: f64, attributes: &MetricAttributes) {
395+
if let MetricAttributes::OTel { kvs } = attributes {
396+
self.record(value, kvs);
397+
} else {
398+
debug_assert!(
399+
false,
400+
"Must use OTel attributes with an OTel metric implementation"
401+
);
402+
}
403+
}
404+
}
334405
}

0 commit comments

Comments
 (0)