1
+ use std:: any:: Any ;
1
2
use std:: { collections:: HashMap , sync:: Arc } ;
2
3
3
- use pyo3:: exceptions:: PyTypeError ;
4
4
use pyo3:: prelude:: * ;
5
- use temporal_sdk_core_api:: telemetry:: metrics;
5
+ use pyo3:: { exceptions:: PyTypeError , types:: PyDict } ;
6
+ use temporal_sdk_core_api:: telemetry:: metrics:: {
7
+ self , BufferInstrumentRef , CustomMetricAttributes , MetricEvent , NewAttributes ,
8
+ } ;
6
9
7
10
use crate :: runtime;
8
11
@@ -139,14 +142,17 @@ impl MetricAttributesRef {
139
142
fn with_additional_attributes < ' p > (
140
143
& self ,
141
144
py : Python < ' p > ,
145
+ meter : & MetricMeterRef ,
142
146
new_attrs : HashMap < String , PyObject > ,
143
147
) -> PyResult < Self > {
144
- let mut attrs = self . attrs . clone ( ) ;
145
- attrs. add_new_attrs (
146
- new_attrs
147
- . into_iter ( )
148
- . map ( |( k, obj) | metric_key_value_from_py ( py, k, obj) )
149
- . collect :: < PyResult < Vec < metrics:: MetricKeyValue > > > ( ) ?,
148
+ let attrs = meter. meter . inner . extend_attributes (
149
+ self . attrs . clone ( ) ,
150
+ NewAttributes {
151
+ attributes : new_attrs
152
+ . into_iter ( )
153
+ . map ( |( k, obj) | metric_key_value_from_py ( py, k, obj) )
154
+ . collect :: < PyResult < Vec < metrics:: MetricKeyValue > > > ( ) ?,
155
+ } ,
150
156
) ;
151
157
Ok ( MetricAttributesRef { attrs } )
152
158
}
@@ -173,3 +179,146 @@ fn metric_key_value_from_py<'p>(
173
179
} ;
174
180
Ok ( metrics:: MetricKeyValue :: new ( k, val) )
175
181
}
182
+
183
+ // WARNING: This must match temporalio.runtime.BufferedMetricUpdate protocol
184
+ #[ pyclass]
185
+ pub struct BufferedMetricUpdate {
186
+ #[ pyo3( get) ]
187
+ pub metric : Py < BufferedMetric > ,
188
+ #[ pyo3( get) ]
189
+ pub value : u64 ,
190
+ #[ pyo3( get) ]
191
+ pub attributes : Py < PyDict > ,
192
+ }
193
+
194
+ // WARNING: This must match temporalio.runtime.BufferedMetric protocol
195
+ #[ pyclass]
196
+ pub struct BufferedMetric {
197
+ #[ pyo3( get) ]
198
+ pub name : String ,
199
+ #[ pyo3( get) ]
200
+ pub description : Option < String > ,
201
+ #[ pyo3( get) ]
202
+ pub unit : Option < String > ,
203
+ #[ pyo3( get) ]
204
+ pub kind : u8 , // 0 - counter, 1 - gauge, 2 - histogram
205
+ }
206
+
207
+ #[ derive( Debug ) ]
208
+ struct BufferedMetricAttributes ( Py < PyDict > ) ;
209
+
210
+ #[ derive( Clone , Debug ) ]
211
+ pub struct BufferedMetricRef ( Py < BufferedMetric > ) ;
212
+
213
+ impl BufferInstrumentRef for BufferedMetricRef { }
214
+
215
+ impl CustomMetricAttributes for BufferedMetricAttributes {
216
+ fn as_any ( self : Arc < Self > ) -> Arc < dyn Any + Send + Sync > {
217
+ self as Arc < dyn Any + Send + Sync >
218
+ }
219
+ }
220
+
221
+ pub fn convert_metric_events < ' p > (
222
+ py : Python < ' p > ,
223
+ events : Vec < MetricEvent < BufferedMetricRef > > ,
224
+ ) -> Vec < BufferedMetricUpdate > {
225
+ events
226
+ . into_iter ( )
227
+ . filter_map ( |e| convert_metric_event ( py, e) )
228
+ . collect ( )
229
+ }
230
+
231
+ fn convert_metric_event < ' p > (
232
+ py : Python < ' p > ,
233
+ event : MetricEvent < BufferedMetricRef > ,
234
+ ) -> Option < BufferedMetricUpdate > {
235
+ match event {
236
+ // Create the metric and put it on the lazy ref
237
+ MetricEvent :: Create {
238
+ params,
239
+ populate_into,
240
+ kind,
241
+ } => {
242
+ let buffered_ref = BufferedMetricRef (
243
+ Py :: new (
244
+ py,
245
+ BufferedMetric {
246
+ name : params. name . to_string ( ) ,
247
+ description : Some ( params. description )
248
+ . filter ( |s| !s. is_empty ( ) )
249
+ . map ( |s| s. to_string ( ) ) ,
250
+ unit : Some ( params. unit )
251
+ . filter ( |s| !s. is_empty ( ) )
252
+ . map ( |s| s. to_string ( ) ) ,
253
+ kind : match kind {
254
+ metrics:: MetricKind :: Counter => 0 ,
255
+ metrics:: MetricKind :: Gauge => 1 ,
256
+ metrics:: MetricKind :: Histogram => 2 ,
257
+ } ,
258
+ } ,
259
+ )
260
+ . expect ( "Unable to create buffered metric" ) ,
261
+ ) ;
262
+ populate_into. set ( Arc :: new ( buffered_ref) ) . unwrap ( ) ;
263
+ None
264
+ }
265
+ // Create the attributes and put it on the lazy ref
266
+ MetricEvent :: CreateAttributes {
267
+ populate_into,
268
+ append_from,
269
+ attributes,
270
+ } => {
271
+ // Create the dictionary (as copy from existing if needed)
272
+ let new_attrs_ref: Py < PyDict > = match append_from {
273
+ Some ( existing) => existing
274
+ . get ( )
275
+ . clone ( )
276
+ . as_any ( )
277
+ . downcast :: < BufferedMetricAttributes > ( )
278
+ . expect ( "Unable to downcast to expected buffered metric attributes" )
279
+ . 0
280
+ . as_ref ( py)
281
+ . copy ( )
282
+ . expect ( "Failed to copy metric attribute dictionary" )
283
+ . into ( ) ,
284
+ None => PyDict :: new ( py) . into ( ) ,
285
+ } ;
286
+ // Add attributes
287
+ let new_attrs = new_attrs_ref. as_ref ( py) ;
288
+ for kv in attributes. into_iter ( ) {
289
+ match kv. value {
290
+ metrics:: MetricValue :: String ( v) => new_attrs. set_item ( kv. key , v) ,
291
+ metrics:: MetricValue :: Int ( v) => new_attrs. set_item ( kv. key , v) ,
292
+ metrics:: MetricValue :: Float ( v) => new_attrs. set_item ( kv. key , v) ,
293
+ metrics:: MetricValue :: Bool ( v) => new_attrs. set_item ( kv. key , v) ,
294
+ }
295
+ . expect ( "Unable to set metric key/value on dictionary" ) ;
296
+ }
297
+ // Put on lazy ref
298
+ populate_into
299
+ . set ( Arc :: new ( BufferedMetricAttributes ( new_attrs_ref) ) )
300
+ . expect ( "Unable to set buffered metric attributes on reference" ) ;
301
+ None
302
+ }
303
+ // Convert to Python metric event
304
+ MetricEvent :: Update {
305
+ instrument,
306
+ attributes,
307
+ update,
308
+ } => Some ( BufferedMetricUpdate {
309
+ metric : instrument. get ( ) . clone ( ) . 0 . clone ( ) ,
310
+ value : match update {
311
+ metrics:: MetricUpdateVal :: Delta ( v) => v,
312
+ metrics:: MetricUpdateVal :: Value ( v) => v,
313
+ } ,
314
+ attributes : attributes
315
+ . get ( )
316
+ . clone ( )
317
+ . as_any ( )
318
+ . downcast :: < BufferedMetricAttributes > ( )
319
+ . expect ( "Unable to downcast to expected buffered metric attributes" )
320
+ . 0
321
+ . clone ( ) ,
322
+ } ) ,
323
+ }
324
+ }
0 commit comments