@temporalio/core-bridge 1.12.0 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/Cargo.lock +64 -119
  2. package/Cargo.toml +1 -1
  3. package/index.js +3 -2
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.cargo/config.toml +1 -2
  11. package/sdk-core/.github/workflows/per-pr.yml +2 -0
  12. package/sdk-core/AGENTS.md +7 -0
  13. package/sdk-core/Cargo.toml +9 -5
  14. package/sdk-core/README.md +6 -5
  15. package/sdk-core/client/Cargo.toml +3 -2
  16. package/sdk-core/client/src/lib.rs +17 -8
  17. package/sdk-core/client/src/metrics.rs +57 -23
  18. package/sdk-core/client/src/raw.rs +33 -15
  19. package/sdk-core/core/Cargo.toml +11 -9
  20. package/sdk-core/core/benches/workflow_replay.rs +114 -15
  21. package/sdk-core/core/src/core_tests/activity_tasks.rs +18 -18
  22. package/sdk-core/core/src/core_tests/child_workflows.rs +4 -4
  23. package/sdk-core/core/src/core_tests/determinism.rs +6 -6
  24. package/sdk-core/core/src/core_tests/local_activities.rs +20 -20
  25. package/sdk-core/core/src/core_tests/mod.rs +40 -5
  26. package/sdk-core/core/src/core_tests/queries.rs +25 -16
  27. package/sdk-core/core/src/core_tests/replay_flag.rs +3 -3
  28. package/sdk-core/core/src/core_tests/updates.rs +3 -3
  29. package/sdk-core/core/src/core_tests/workers.rs +9 -7
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +40 -42
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +1 -19
  32. package/sdk-core/core/src/lib.rs +10 -1
  33. package/sdk-core/core/src/pollers/poll_buffer.rs +2 -2
  34. package/sdk-core/core/src/replay/mod.rs +3 -3
  35. package/sdk-core/core/src/telemetry/metrics.rs +306 -152
  36. package/sdk-core/core/src/telemetry/mod.rs +11 -4
  37. package/sdk-core/core/src/telemetry/otel.rs +134 -131
  38. package/sdk-core/core/src/telemetry/prometheus_meter.rs +885 -0
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +48 -28
  40. package/sdk-core/core/src/test_help/mod.rs +27 -12
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +7 -7
  42. package/sdk-core/core/src/worker/activities.rs +4 -4
  43. package/sdk-core/core/src/worker/client/mocks.rs +10 -3
  44. package/sdk-core/core/src/worker/client.rs +68 -5
  45. package/sdk-core/core/src/worker/heartbeat.rs +229 -0
  46. package/sdk-core/core/src/worker/mod.rs +35 -14
  47. package/sdk-core/core/src/worker/tuner/resource_based.rs +4 -4
  48. package/sdk-core/core/src/worker/workflow/history_update.rs +71 -19
  49. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -2
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -1
  51. package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +31 -48
  52. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -2
  53. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +3 -3
  54. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +4 -1
  55. package/sdk-core/core/src/worker/workflow/managed_run.rs +1 -1
  56. package/sdk-core/core/src/worker/workflow/mod.rs +15 -15
  57. package/sdk-core/core-api/Cargo.toml +2 -2
  58. package/sdk-core/core-api/src/envconfig.rs +204 -99
  59. package/sdk-core/core-api/src/lib.rs +9 -0
  60. package/sdk-core/core-api/src/telemetry/metrics.rs +548 -100
  61. package/sdk-core/core-api/src/worker.rs +11 -5
  62. package/sdk-core/core-c-bridge/Cargo.toml +49 -0
  63. package/sdk-core/core-c-bridge/build.rs +26 -0
  64. package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +817 -0
  65. package/sdk-core/core-c-bridge/src/client.rs +679 -0
  66. package/sdk-core/core-c-bridge/src/lib.rs +245 -0
  67. package/sdk-core/core-c-bridge/src/metric.rs +682 -0
  68. package/sdk-core/core-c-bridge/src/random.rs +61 -0
  69. package/sdk-core/core-c-bridge/src/runtime.rs +445 -0
  70. package/sdk-core/core-c-bridge/src/testing.rs +282 -0
  71. package/sdk-core/core-c-bridge/src/tests/context.rs +644 -0
  72. package/sdk-core/core-c-bridge/src/tests/mod.rs +178 -0
  73. package/sdk-core/core-c-bridge/src/tests/utils.rs +108 -0
  74. package/sdk-core/core-c-bridge/src/worker.rs +1069 -0
  75. package/sdk-core/etc/deps.svg +64 -64
  76. package/sdk-core/sdk/src/activity_context.rs +6 -4
  77. package/sdk-core/sdk/src/lib.rs +49 -27
  78. package/sdk-core/sdk/src/workflow_future.rs +18 -25
  79. package/sdk-core/sdk-core-protos/protos/api_upstream/README.md +4 -0
  80. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +0 -2
  81. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +630 -83
  82. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +632 -78
  83. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +4 -4
  84. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +6 -4
  85. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +2 -2
  86. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +32 -2
  87. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +10 -1
  88. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/deployment.proto +26 -0
  89. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  90. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
  91. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  92. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +47 -31
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -4
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +7 -1
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +134 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +14 -11
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +148 -37
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +21 -0
  99. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -4
  100. package/sdk-core/sdk-core-protos/src/history_builder.rs +9 -5
  101. package/sdk-core/sdk-core-protos/src/lib.rs +96 -6
  102. package/sdk-core/test-utils/src/lib.rs +11 -3
  103. package/sdk-core/tests/cloud_tests.rs +3 -3
  104. package/sdk-core/tests/heavy_tests.rs +11 -3
  105. package/sdk-core/tests/integ_tests/client_tests.rs +12 -13
  106. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +1 -1
  107. package/sdk-core/tests/integ_tests/metrics_tests.rs +188 -83
  108. package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
  109. package/sdk-core/tests/integ_tests/queries_tests.rs +56 -40
  110. package/sdk-core/tests/integ_tests/update_tests.rs +2 -7
  111. package/sdk-core/tests/integ_tests/worker_tests.rs +3 -4
  112. package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +3 -7
  113. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +3 -5
  114. package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +24 -17
  115. package/src/client.rs +6 -0
  116. package/src/metrics.rs +6 -6
@@ -1,7 +1,9 @@
1
+ use crate::dbg_panic;
1
2
  use std::{
2
3
  any::Any,
3
4
  borrow::Cow,
4
- fmt::Debug,
5
+ collections::{BTreeMap, HashMap},
6
+ fmt::{Debug, Display},
5
7
  ops::Deref,
6
8
  sync::{Arc, OnceLock},
7
9
  time::Duration,
@@ -23,16 +25,16 @@ pub trait CoreMeter: Send + Sync + Debug {
23
25
  existing: MetricAttributes,
24
26
  attribs: NewAttributes,
25
27
  ) -> MetricAttributes;
26
- fn counter(&self, params: MetricParameters) -> Arc<dyn Counter>;
27
- fn histogram(&self, params: MetricParameters) -> Arc<dyn Histogram>;
28
- fn histogram_f64(&self, params: MetricParameters) -> Arc<dyn HistogramF64>;
28
+ fn counter(&self, params: MetricParameters) -> Counter;
29
+ fn histogram(&self, params: MetricParameters) -> Histogram;
30
+ fn histogram_f64(&self, params: MetricParameters) -> HistogramF64;
29
31
  /// Create a histogram which records Durations. Implementations should choose to emit in
30
32
  /// either milliseconds or seconds depending on how they have been configured.
31
33
  /// [MetricParameters::unit] should be overwritten by implementations to be `ms` or `s`
32
34
  /// accordingly.
33
- fn histogram_duration(&self, params: MetricParameters) -> Arc<dyn HistogramDuration>;
34
- fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge>;
35
- fn gauge_f64(&self, params: MetricParameters) -> Arc<dyn GaugeF64>;
35
+ fn histogram_duration(&self, params: MetricParameters) -> HistogramDuration;
36
+ fn gauge(&self, params: MetricParameters) -> Gauge;
37
+ fn gauge_f64(&self, params: MetricParameters) -> GaugeF64;
36
38
  }
37
39
 
38
40
  #[derive(Debug, Clone, derive_builder::Builder)]
@@ -84,26 +86,26 @@ impl CoreMeter for Arc<dyn CoreMeter> {
84
86
  self.as_ref().extend_attributes(existing, attribs)
85
87
  }
86
88
 
87
- fn counter(&self, params: MetricParameters) -> Arc<dyn Counter> {
89
+ fn counter(&self, params: MetricParameters) -> Counter {
88
90
  self.as_ref().counter(params)
89
91
  }
90
- fn histogram(&self, params: MetricParameters) -> Arc<dyn Histogram> {
92
+ fn histogram(&self, params: MetricParameters) -> Histogram {
91
93
  self.as_ref().histogram(params)
92
94
  }
93
95
 
94
- fn histogram_f64(&self, params: MetricParameters) -> Arc<dyn HistogramF64> {
96
+ fn histogram_f64(&self, params: MetricParameters) -> HistogramF64 {
95
97
  self.as_ref().histogram_f64(params)
96
98
  }
97
99
 
98
- fn histogram_duration(&self, params: MetricParameters) -> Arc<dyn HistogramDuration> {
100
+ fn histogram_duration(&self, params: MetricParameters) -> HistogramDuration {
99
101
  self.as_ref().histogram_duration(params)
100
102
  }
101
103
 
102
- fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge> {
104
+ fn gauge(&self, params: MetricParameters) -> Gauge {
103
105
  self.as_ref().gauge(params)
104
106
  }
105
107
 
106
- fn gauge_f64(&self, params: MetricParameters) -> Arc<dyn GaugeF64> {
108
+ fn gauge_f64(&self, params: MetricParameters) -> GaugeF64 {
107
109
  self.as_ref().gauge_f64(params)
108
110
  }
109
111
  }
@@ -117,8 +119,12 @@ pub enum MetricAttributes {
117
119
  OTel {
118
120
  kvs: Arc<Vec<opentelemetry::KeyValue>>,
119
121
  },
122
+ Prometheus {
123
+ labels: Arc<OrderedPromLabelSet>,
124
+ },
120
125
  Buffer(BufferAttributes),
121
126
  Dynamic(Arc<dyn CustomMetricAttributes>),
127
+ Empty,
122
128
  }
123
129
 
124
130
  /// A reference to some attributes created lang side.
@@ -150,7 +156,7 @@ where
150
156
  }
151
157
 
152
158
  /// A K/V pair that can be used to label a specific recording of a metric
153
- #[derive(Clone, Debug)]
159
+ #[derive(Clone, Debug, PartialEq)]
154
160
  pub struct MetricKeyValue {
155
161
  pub key: String,
156
162
  pub value: MetricValue,
@@ -165,7 +171,7 @@ impl MetricKeyValue {
165
171
  }
166
172
 
167
173
  /// Values metric labels may assume
168
- #[derive(Clone, Debug, derive_more::From)]
174
+ #[derive(Clone, Debug, PartialEq, derive_more::From)]
169
175
  pub enum MetricValue {
170
176
  String(String),
171
177
  Int(i64),
@@ -178,30 +184,368 @@ impl From<&'static str> for MetricValue {
178
184
  MetricValue::String(value.to_string())
179
185
  }
180
186
  }
187
+ impl Display for MetricValue {
188
+ fn fmt(&self, f1: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189
+ match self {
190
+ MetricValue::String(s) => write!(f1, "{s}"),
191
+ MetricValue::Int(i) => write!(f1, "{i}"),
192
+ MetricValue::Float(f) => write!(f1, "{f}"),
193
+ MetricValue::Bool(b) => write!(f1, "{b}"),
194
+ }
195
+ }
196
+ }
197
+
198
+ pub trait MetricAttributable<Base> {
199
+ /// Replace any existing attributes on this metric with new ones, and return a new copy
200
+ /// of the metric, or a base version, which can be used to record values.
201
+ ///
202
+ /// Note that this operation is relatively expensive compared to simply recording a value
203
+ /// without any additional attributes, so users should prefer to save the metric instance
204
+ /// after calling this, and use the value-only methods afterward.
205
+ ///
206
+ /// This operation may fail if the underlying metrics implementation disallows the registration
207
+ /// of a new metric, or encounters any other issue.
208
+ fn with_attributes(
209
+ &self,
210
+ attributes: &MetricAttributes,
211
+ ) -> Result<Base, Box<dyn std::error::Error>>;
212
+ }
213
+
214
+ #[derive(Clone)]
215
+ pub struct LazyBoundMetric<T, B> {
216
+ metric: T,
217
+ attributes: MetricAttributes,
218
+ bound_cache: OnceLock<B>,
219
+ }
220
+ impl<T, B> LazyBoundMetric<T, B> {
221
+ pub fn update_attributes(&mut self, new_attributes: MetricAttributes) {
222
+ self.attributes = new_attributes;
223
+ self.bound_cache = OnceLock::new();
224
+ }
225
+ }
226
+
227
+ pub trait CounterBase: Send + Sync {
228
+ fn adds(&self, value: u64);
229
+ }
230
+ pub type Counter = LazyBoundMetric<
231
+ Arc<dyn MetricAttributable<Box<dyn CounterBase>> + Send + Sync>,
232
+ Arc<dyn CounterBase>,
233
+ >;
234
+ impl Counter {
235
+ pub fn new(inner: Arc<dyn MetricAttributable<Box<dyn CounterBase>> + Send + Sync>) -> Self {
236
+ Self {
237
+ metric: inner,
238
+ attributes: MetricAttributes::Empty,
239
+ bound_cache: OnceLock::new(),
240
+ }
241
+ }
242
+ pub fn add(&self, value: u64, attributes: &MetricAttributes) {
243
+ match self.metric.with_attributes(attributes) {
244
+ Ok(base) => {
245
+ base.adds(value);
246
+ }
247
+ Err(e) => {
248
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
249
+ }
250
+ }
251
+ }
252
+ }
253
+ impl CounterBase for Counter {
254
+ fn adds(&self, value: u64) {
255
+ // TODO: Replace all of these with below when stable
256
+ // https://doc.rust-lang.org/std/sync/struct.OnceLock.html#method.get_or_try_init
257
+ let bound = self.bound_cache.get_or_init(|| {
258
+ self.metric
259
+ .with_attributes(&self.attributes)
260
+ .map(Into::into)
261
+ .unwrap_or_else(|e| {
262
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
263
+ Arc::new(NoOpInstrument) as Arc<dyn CounterBase>
264
+ })
265
+ });
266
+ bound.adds(value);
267
+ }
268
+ }
269
+ impl MetricAttributable<Counter> for Counter {
270
+ fn with_attributes(
271
+ &self,
272
+ attributes: &MetricAttributes,
273
+ ) -> Result<Counter, Box<dyn std::error::Error>> {
274
+ Ok(Self {
275
+ metric: self.metric.clone(),
276
+ attributes: attributes.clone(),
277
+ bound_cache: OnceLock::new(),
278
+ })
279
+ }
280
+ }
181
281
 
182
- pub trait Counter: Send + Sync {
183
- fn add(&self, value: u64, attributes: &MetricAttributes);
282
+ pub trait HistogramBase: Send + Sync {
283
+ fn records(&self, value: u64);
284
+ }
285
+ pub type Histogram = LazyBoundMetric<
286
+ Arc<dyn MetricAttributable<Box<dyn HistogramBase>> + Send + Sync>,
287
+ Arc<dyn HistogramBase>,
288
+ >;
289
+ impl Histogram {
290
+ pub fn new(inner: Arc<dyn MetricAttributable<Box<dyn HistogramBase>> + Send + Sync>) -> Self {
291
+ Self {
292
+ metric: inner,
293
+ attributes: MetricAttributes::Empty,
294
+ bound_cache: OnceLock::new(),
295
+ }
296
+ }
297
+ pub fn record(&self, value: u64, attributes: &MetricAttributes) {
298
+ match self.metric.with_attributes(attributes) {
299
+ Ok(base) => {
300
+ base.records(value);
301
+ }
302
+ Err(e) => {
303
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
304
+ }
305
+ }
306
+ }
307
+ }
308
+ impl HistogramBase for Histogram {
309
+ fn records(&self, value: u64) {
310
+ let bound = self.bound_cache.get_or_init(|| {
311
+ self.metric
312
+ .with_attributes(&self.attributes)
313
+ .map(Into::into)
314
+ .unwrap_or_else(|e| {
315
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
316
+ Arc::new(NoOpInstrument) as Arc<dyn HistogramBase>
317
+ })
318
+ });
319
+ bound.records(value);
320
+ }
321
+ }
322
+ impl MetricAttributable<Histogram> for Histogram {
323
+ fn with_attributes(
324
+ &self,
325
+ attributes: &MetricAttributes,
326
+ ) -> Result<Histogram, Box<dyn std::error::Error>> {
327
+ Ok(Self {
328
+ metric: self.metric.clone(),
329
+ attributes: attributes.clone(),
330
+ bound_cache: OnceLock::new(),
331
+ })
332
+ }
333
+ }
334
+
335
+ pub trait HistogramF64Base: Send + Sync {
336
+ fn records(&self, value: f64);
337
+ }
338
+ pub type HistogramF64 = LazyBoundMetric<
339
+ Arc<dyn MetricAttributable<Box<dyn HistogramF64Base>> + Send + Sync>,
340
+ Arc<dyn HistogramF64Base>,
341
+ >;
342
+ impl HistogramF64 {
343
+ pub fn new(
344
+ inner: Arc<dyn MetricAttributable<Box<dyn HistogramF64Base>> + Send + Sync>,
345
+ ) -> Self {
346
+ Self {
347
+ metric: inner,
348
+ attributes: MetricAttributes::Empty,
349
+ bound_cache: OnceLock::new(),
350
+ }
351
+ }
352
+ pub fn record(&self, value: f64, attributes: &MetricAttributes) {
353
+ match self.metric.with_attributes(attributes) {
354
+ Ok(base) => {
355
+ base.records(value);
356
+ }
357
+ Err(e) => {
358
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
359
+ }
360
+ }
361
+ }
362
+ }
363
+ impl HistogramF64Base for HistogramF64 {
364
+ fn records(&self, value: f64) {
365
+ let bound = self.bound_cache.get_or_init(|| {
366
+ self.metric
367
+ .with_attributes(&self.attributes)
368
+ .map(Into::into)
369
+ .unwrap_or_else(|e| {
370
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
371
+ Arc::new(NoOpInstrument) as Arc<dyn HistogramF64Base>
372
+ })
373
+ });
374
+ bound.records(value);
375
+ }
376
+ }
377
+ impl MetricAttributable<HistogramF64> for HistogramF64 {
378
+ fn with_attributes(
379
+ &self,
380
+ attributes: &MetricAttributes,
381
+ ) -> Result<HistogramF64, Box<dyn std::error::Error>> {
382
+ Ok(Self {
383
+ metric: self.metric.clone(),
384
+ attributes: attributes.clone(),
385
+ bound_cache: OnceLock::new(),
386
+ })
387
+ }
388
+ }
389
+
390
+ pub trait HistogramDurationBase: Send + Sync {
391
+ fn records(&self, value: Duration);
392
+ }
393
+ pub type HistogramDuration = LazyBoundMetric<
394
+ Arc<dyn MetricAttributable<Box<dyn HistogramDurationBase>> + Send + Sync>,
395
+ Arc<dyn HistogramDurationBase>,
396
+ >;
397
+ impl HistogramDuration {
398
+ pub fn new(
399
+ inner: Arc<dyn MetricAttributable<Box<dyn HistogramDurationBase>> + Send + Sync>,
400
+ ) -> Self {
401
+ Self {
402
+ metric: inner,
403
+ attributes: MetricAttributes::Empty,
404
+ bound_cache: OnceLock::new(),
405
+ }
406
+ }
407
+ pub fn record(&self, value: Duration, attributes: &MetricAttributes) {
408
+ match self.metric.with_attributes(attributes) {
409
+ Ok(base) => {
410
+ base.records(value);
411
+ }
412
+ Err(e) => {
413
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
414
+ }
415
+ }
416
+ }
417
+ }
418
+ impl HistogramDurationBase for HistogramDuration {
419
+ fn records(&self, value: Duration) {
420
+ let bound = self.bound_cache.get_or_init(|| {
421
+ self.metric
422
+ .with_attributes(&self.attributes)
423
+ .map(Into::into)
424
+ .unwrap_or_else(|e| {
425
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
426
+ Arc::new(NoOpInstrument) as Arc<dyn HistogramDurationBase>
427
+ })
428
+ });
429
+ bound.records(value);
430
+ }
431
+ }
432
+ impl MetricAttributable<HistogramDuration> for HistogramDuration {
433
+ fn with_attributes(
434
+ &self,
435
+ attributes: &MetricAttributes,
436
+ ) -> Result<HistogramDuration, Box<dyn std::error::Error>> {
437
+ Ok(Self {
438
+ metric: self.metric.clone(),
439
+ attributes: attributes.clone(),
440
+ bound_cache: OnceLock::new(),
441
+ })
442
+ }
184
443
  }
185
444
 
186
- pub trait Histogram: Send + Sync {
187
- // When referring to durations, this value is in millis
188
- fn record(&self, value: u64, attributes: &MetricAttributes);
445
+ pub trait GaugeBase: Send + Sync {
446
+ fn records(&self, value: u64);
447
+ }
448
+ pub type Gauge = LazyBoundMetric<
449
+ Arc<dyn MetricAttributable<Box<dyn GaugeBase>> + Send + Sync>,
450
+ Arc<dyn GaugeBase>,
451
+ >;
452
+ impl Gauge {
453
+ pub fn new(inner: Arc<dyn MetricAttributable<Box<dyn GaugeBase>> + Send + Sync>) -> Self {
454
+ Self {
455
+ metric: inner,
456
+ attributes: MetricAttributes::Empty,
457
+ bound_cache: OnceLock::new(),
458
+ }
459
+ }
460
+ pub fn record(&self, value: u64, attributes: &MetricAttributes) {
461
+ match self.metric.with_attributes(attributes) {
462
+ Ok(base) => {
463
+ base.records(value);
464
+ }
465
+ Err(e) => {
466
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
467
+ }
468
+ }
469
+ }
189
470
  }
190
- pub trait HistogramF64: Send + Sync {
191
- // When referring to durations, this value is in seconds
192
- fn record(&self, value: f64, attributes: &MetricAttributes);
471
+ impl GaugeBase for Gauge {
472
+ fn records(&self, value: u64) {
473
+ let bound = self.bound_cache.get_or_init(|| {
474
+ self.metric
475
+ .with_attributes(&self.attributes)
476
+ .map(Into::into)
477
+ .unwrap_or_else(|e| {
478
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
479
+ Arc::new(NoOpInstrument) as Arc<dyn GaugeBase>
480
+ })
481
+ });
482
+ bound.records(value);
483
+ }
193
484
  }
194
- pub trait HistogramDuration: Send + Sync {
195
- fn record(&self, value: Duration, attributes: &MetricAttributes);
485
+ impl MetricAttributable<Gauge> for Gauge {
486
+ fn with_attributes(
487
+ &self,
488
+ attributes: &MetricAttributes,
489
+ ) -> Result<Gauge, Box<dyn std::error::Error>> {
490
+ Ok(Self {
491
+ metric: self.metric.clone(),
492
+ attributes: attributes.clone(),
493
+ bound_cache: OnceLock::new(),
494
+ })
495
+ }
196
496
  }
197
497
 
198
- pub trait Gauge: Send + Sync {
199
- // When referring to durations, this value is in millis
200
- fn record(&self, value: u64, attributes: &MetricAttributes);
498
+ pub trait GaugeF64Base: Send + Sync {
499
+ fn records(&self, value: f64);
500
+ }
501
+ pub type GaugeF64 = LazyBoundMetric<
502
+ Arc<dyn MetricAttributable<Box<dyn GaugeF64Base>> + Send + Sync>,
503
+ Arc<dyn GaugeF64Base>,
504
+ >;
505
+ impl GaugeF64 {
506
+ pub fn new(inner: Arc<dyn MetricAttributable<Box<dyn GaugeF64Base>> + Send + Sync>) -> Self {
507
+ Self {
508
+ metric: inner,
509
+ attributes: MetricAttributes::Empty,
510
+ bound_cache: OnceLock::new(),
511
+ }
512
+ }
513
+ pub fn record(&self, value: f64, attributes: &MetricAttributes) {
514
+ match self.metric.with_attributes(attributes) {
515
+ Ok(base) => {
516
+ base.records(value);
517
+ }
518
+ Err(e) => {
519
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}",);
520
+ }
521
+ }
522
+ }
523
+ }
524
+ impl GaugeF64Base for GaugeF64 {
525
+ fn records(&self, value: f64) {
526
+ let bound = self.bound_cache.get_or_init(|| {
527
+ self.metric
528
+ .with_attributes(&self.attributes)
529
+ .map(Into::into)
530
+ .unwrap_or_else(|e| {
531
+ dbg_panic!("Failed to initialize metric, will drop values: {e:?}");
532
+ Arc::new(NoOpInstrument) as Arc<dyn GaugeF64Base>
533
+ })
534
+ });
535
+ bound.records(value);
536
+ }
201
537
  }
202
- pub trait GaugeF64: Send + Sync {
203
- // When referring to durations, this value is in seconds
204
- fn record(&self, value: f64, attributes: &MetricAttributes);
538
+ impl MetricAttributable<GaugeF64> for GaugeF64 {
539
+ fn with_attributes(
540
+ &self,
541
+ attributes: &MetricAttributes,
542
+ ) -> Result<GaugeF64, Box<dyn std::error::Error>> {
543
+ Ok(Self {
544
+ metric: self.metric.clone(),
545
+ attributes: attributes.clone(),
546
+ bound_cache: OnceLock::new(),
547
+ })
548
+ }
205
549
  }
206
550
 
207
551
  #[derive(Debug, Clone)]
@@ -297,50 +641,65 @@ impl CoreMeter for NoOpCoreMeter {
297
641
  existing
298
642
  }
299
643
 
300
- fn counter(&self, _: MetricParameters) -> Arc<dyn Counter> {
301
- Arc::new(NoOpInstrument)
644
+ fn counter(&self, _: MetricParameters) -> Counter {
645
+ Counter::new(Arc::new(NoOpInstrument))
302
646
  }
303
647
 
304
- fn histogram(&self, _: MetricParameters) -> Arc<dyn Histogram> {
305
- Arc::new(NoOpInstrument)
648
+ fn histogram(&self, _: MetricParameters) -> Histogram {
649
+ Histogram::new(Arc::new(NoOpInstrument))
306
650
  }
307
651
 
308
- fn histogram_f64(&self, _: MetricParameters) -> Arc<dyn HistogramF64> {
309
- Arc::new(NoOpInstrument)
652
+ fn histogram_f64(&self, _: MetricParameters) -> HistogramF64 {
653
+ HistogramF64::new(Arc::new(NoOpInstrument))
310
654
  }
311
655
 
312
- fn histogram_duration(&self, _: MetricParameters) -> Arc<dyn HistogramDuration> {
313
- Arc::new(NoOpInstrument)
656
+ fn histogram_duration(&self, _: MetricParameters) -> HistogramDuration {
657
+ HistogramDuration::new(Arc::new(NoOpInstrument))
314
658
  }
315
659
 
316
- fn gauge(&self, _: MetricParameters) -> Arc<dyn Gauge> {
317
- Arc::new(NoOpInstrument)
660
+ fn gauge(&self, _: MetricParameters) -> Gauge {
661
+ Gauge::new(Arc::new(NoOpInstrument))
318
662
  }
319
663
 
320
- fn gauge_f64(&self, _: MetricParameters) -> Arc<dyn GaugeF64> {
321
- Arc::new(NoOpInstrument)
664
+ fn gauge_f64(&self, _: MetricParameters) -> GaugeF64 {
665
+ GaugeF64::new(Arc::new(NoOpInstrument))
322
666
  }
323
667
  }
324
668
 
325
- pub struct NoOpInstrument;
326
- impl Counter for NoOpInstrument {
327
- fn add(&self, _: u64, _: &MetricAttributes) {}
328
- }
329
- impl Histogram for NoOpInstrument {
330
- fn record(&self, _: u64, _: &MetricAttributes) {}
331
- }
332
- impl HistogramF64 for NoOpInstrument {
333
- fn record(&self, _: f64, _: &MetricAttributes) {}
334
- }
335
- impl HistogramDuration for NoOpInstrument {
336
- fn record(&self, _: Duration, _: &MetricAttributes) {}
337
- }
338
- impl Gauge for NoOpInstrument {
339
- fn record(&self, _: u64, _: &MetricAttributes) {}
669
+ macro_rules! impl_metric_attributable {
670
+ ($base_trait:ident, $rt:ty, $init:expr) => {
671
+ impl MetricAttributable<Box<dyn $base_trait>> for $rt {
672
+ fn with_attributes(
673
+ &self,
674
+ _: &MetricAttributes,
675
+ ) -> Result<Box<dyn $base_trait>, Box<dyn std::error::Error>> {
676
+ Ok(Box::new($init))
677
+ }
678
+ }
679
+ };
340
680
  }
341
- impl GaugeF64 for NoOpInstrument {
342
- fn record(&self, _: f64, _: &MetricAttributes) {}
681
+
682
+ pub struct NoOpInstrument;
683
+ macro_rules! impl_no_op {
684
+ ($base_trait:ident, $value_type:ty) => {
685
+ impl_metric_attributable!($base_trait, NoOpInstrument, NoOpInstrument);
686
+ impl $base_trait for NoOpInstrument {
687
+ fn records(&self, _: $value_type) {}
688
+ }
689
+ };
690
+ ($base_trait:ident) => {
691
+ impl_metric_attributable!($base_trait, NoOpInstrument, NoOpInstrument);
692
+ impl $base_trait for NoOpInstrument {
693
+ fn adds(&self, _: u64) {}
694
+ }
695
+ };
343
696
  }
697
+ impl_no_op!(CounterBase);
698
+ impl_no_op!(HistogramBase, u64);
699
+ impl_no_op!(HistogramF64Base, f64);
700
+ impl_no_op!(HistogramDurationBase, Duration);
701
+ impl_no_op!(GaugeBase, u64);
702
+ impl_no_op!(GaugeF64Base, f64);
344
703
 
345
704
  #[derive(Debug, Clone)]
346
705
  pub struct NoOpAttributes;
@@ -355,6 +714,12 @@ mod otel_impls {
355
714
  use super::*;
356
715
  use opentelemetry::{KeyValue, metrics};
357
716
 
717
+ #[derive(Clone)]
718
+ struct InstrumentWithAttributes<I> {
719
+ inner: I,
720
+ attributes: MetricAttributes,
721
+ }
722
+
358
723
  impl From<MetricKeyValue> for KeyValue {
359
724
  fn from(kv: MetricKeyValue) -> Self {
360
725
  KeyValue::new(kv.key, kv.value)
@@ -372,68 +737,151 @@ mod otel_impls {
372
737
  }
373
738
  }
374
739
 
375
- impl Counter for metrics::Counter<u64> {
376
- fn add(&self, value: u64, attributes: &MetricAttributes) {
377
- if let MetricAttributes::OTel { kvs } = attributes {
378
- self.add(value, kvs);
740
+ impl MetricAttributable<Box<dyn CounterBase>> for metrics::Counter<u64> {
741
+ fn with_attributes(
742
+ &self,
743
+ attributes: &MetricAttributes,
744
+ ) -> Result<Box<dyn CounterBase>, Box<dyn std::error::Error>> {
745
+ Ok(Box::new(InstrumentWithAttributes {
746
+ inner: self.clone(),
747
+ attributes: attributes.clone(),
748
+ }))
749
+ }
750
+ }
751
+
752
+ impl CounterBase for InstrumentWithAttributes<metrics::Counter<u64>> {
753
+ fn adds(&self, value: u64) {
754
+ if let MetricAttributes::OTel { kvs } = &self.attributes {
755
+ self.inner.add(value, kvs);
379
756
  } else {
380
- debug_assert!(
381
- false,
382
- "Must use OTel attributes with an OTel metric implementation"
383
- );
757
+ dbg_panic!("Must use OTel attributes with an OTel metric implementation");
384
758
  }
385
759
  }
386
760
  }
387
761
 
388
- impl Gauge for metrics::Gauge<u64> {
389
- fn record(&self, value: u64, attributes: &MetricAttributes) {
390
- if let MetricAttributes::OTel { kvs } = attributes {
391
- self.record(value, kvs);
762
+ impl MetricAttributable<Box<dyn GaugeBase>> for metrics::Gauge<u64> {
763
+ fn with_attributes(
764
+ &self,
765
+ attributes: &MetricAttributes,
766
+ ) -> Result<Box<dyn GaugeBase>, Box<dyn std::error::Error>> {
767
+ Ok(Box::new(InstrumentWithAttributes {
768
+ inner: self.clone(),
769
+ attributes: attributes.clone(),
770
+ }))
771
+ }
772
+ }
773
+
774
+ impl GaugeBase for InstrumentWithAttributes<metrics::Gauge<u64>> {
775
+ fn records(&self, value: u64) {
776
+ if let MetricAttributes::OTel { kvs } = &self.attributes {
777
+ self.inner.record(value, kvs);
392
778
  } else {
393
- debug_assert!(
394
- false,
395
- "Must use OTel attributes with an OTel metric implementation"
396
- );
779
+ dbg_panic!("Must use OTel attributes with an OTel metric implementation");
397
780
  }
398
781
  }
399
782
  }
400
783
 
401
- impl GaugeF64 for metrics::Gauge<f64> {
402
- fn record(&self, value: f64, attributes: &MetricAttributes) {
403
- if let MetricAttributes::OTel { kvs } = attributes {
404
- self.record(value, kvs);
784
+ impl MetricAttributable<Box<dyn GaugeF64Base>> for metrics::Gauge<f64> {
785
+ fn with_attributes(
786
+ &self,
787
+ attributes: &MetricAttributes,
788
+ ) -> Result<Box<dyn GaugeF64Base>, Box<dyn std::error::Error>> {
789
+ Ok(Box::new(InstrumentWithAttributes {
790
+ inner: self.clone(),
791
+ attributes: attributes.clone(),
792
+ }))
793
+ }
794
+ }
795
+
796
+ impl GaugeF64Base for InstrumentWithAttributes<metrics::Gauge<f64>> {
797
+ fn records(&self, value: f64) {
798
+ if let MetricAttributes::OTel { kvs } = &self.attributes {
799
+ self.inner.record(value, kvs);
405
800
  } else {
406
- debug_assert!(
407
- false,
408
- "Must use OTel attributes with an OTel metric implementation"
409
- );
801
+ dbg_panic!("Must use OTel attributes with an OTel metric implementation");
410
802
  }
411
803
  }
412
804
  }
413
805
 
414
- impl Histogram for metrics::Histogram<u64> {
415
- fn record(&self, value: u64, attributes: &MetricAttributes) {
416
- if let MetricAttributes::OTel { kvs } = attributes {
417
- self.record(value, kvs);
806
+ impl MetricAttributable<Box<dyn HistogramBase>> for metrics::Histogram<u64> {
807
+ fn with_attributes(
808
+ &self,
809
+ attributes: &MetricAttributes,
810
+ ) -> Result<Box<dyn HistogramBase>, Box<dyn std::error::Error>> {
811
+ Ok(Box::new(InstrumentWithAttributes {
812
+ inner: self.clone(),
813
+ attributes: attributes.clone(),
814
+ }))
815
+ }
816
+ }
817
+
818
+ impl HistogramBase for InstrumentWithAttributes<metrics::Histogram<u64>> {
819
+ fn records(&self, value: u64) {
820
+ if let MetricAttributes::OTel { kvs } = &self.attributes {
821
+ self.inner.record(value, kvs);
418
822
  } else {
419
- debug_assert!(
420
- false,
421
- "Must use OTel attributes with an OTel metric implementation"
422
- );
823
+ dbg_panic!("Must use OTel attributes with an OTel metric implementation");
423
824
  }
424
825
  }
425
826
  }
426
827
 
427
- impl HistogramF64 for metrics::Histogram<f64> {
428
- fn record(&self, value: f64, attributes: &MetricAttributes) {
429
- if let MetricAttributes::OTel { kvs } = attributes {
430
- self.record(value, kvs);
828
+ impl MetricAttributable<Box<dyn HistogramF64Base>> for metrics::Histogram<f64> {
829
+ fn with_attributes(
830
+ &self,
831
+ attributes: &MetricAttributes,
832
+ ) -> Result<Box<dyn HistogramF64Base>, Box<dyn std::error::Error>> {
833
+ Ok(Box::new(InstrumentWithAttributes {
834
+ inner: self.clone(),
835
+ attributes: attributes.clone(),
836
+ }))
837
+ }
838
+ }
839
+
840
+ impl HistogramF64Base for InstrumentWithAttributes<metrics::Histogram<f64>> {
841
+ fn records(&self, value: f64) {
842
+ if let MetricAttributes::OTel { kvs } = &self.attributes {
843
+ self.inner.record(value, kvs);
431
844
  } else {
432
- debug_assert!(
433
- false,
434
- "Must use OTel attributes with an OTel metric implementation"
435
- );
845
+ dbg_panic!("Must use OTel attributes with an OTel metric implementation");
436
846
  }
437
847
  }
438
848
  }
439
849
  }
850
+
851
+ /// Maintains a mapping of metric labels->values with a defined ordering, used for Prometheus labels
852
+ #[derive(Debug, Clone, PartialEq, Default)]
853
+ pub struct OrderedPromLabelSet {
854
+ attributes: BTreeMap<String, MetricValue>,
855
+ }
856
+
857
+ impl OrderedPromLabelSet {
858
+ pub const fn new() -> Self {
859
+ Self {
860
+ attributes: BTreeMap::new(),
861
+ }
862
+ }
863
+ pub fn keys_ordered(&self) -> impl Iterator<Item = &str> {
864
+ self.attributes.keys().map(|s| s.as_str())
865
+ }
866
+ pub fn as_prom_labels(&self) -> HashMap<&str, String> {
867
+ let mut labels = HashMap::new();
868
+ for (k, v) in self.attributes.iter() {
869
+ labels.insert(k.as_str(), v.to_string());
870
+ }
871
+ labels
872
+ }
873
+ pub fn add_kv(&mut self, kv: MetricKeyValue) {
874
+ // Replace '-' with '_' per Prom naming requirements
875
+ self.attributes.insert(kv.key.replace('-', "_"), kv.value);
876
+ }
877
+ }
878
+
879
+ impl From<NewAttributes> for OrderedPromLabelSet {
880
+ fn from(n: NewAttributes) -> Self {
881
+ let mut me = Self::default();
882
+ for kv in n.attributes {
883
+ me.add_kv(kv);
884
+ }
885
+ me
886
+ }
887
+ }