5
5
import os
6
6
import pathlib
7
7
from enum import Enum
8
- from typing import Dict , List , Union
8
+ from typing import Any , Dict , List , Union
9
9
10
10
import fastjsonschema
11
11
@@ -78,14 +78,20 @@ class MetricManager:
78
78
"""
79
79
80
80
def __init__ (
81
- self , metric_set : Dict [str , str ] = None , dimension_set : Dict = None , namespace : str = None , service : str = None
81
+ self ,
82
+ metric_set : Dict [str , str ] = None ,
83
+ dimension_set : Dict = None ,
84
+ namespace : str = None ,
85
+ metadata_set : Dict [str , Any ] = None ,
86
+ service : str = None ,
82
87
):
83
88
self .metric_set = metric_set if metric_set is not None else {}
84
89
self .dimension_set = dimension_set if dimension_set is not None else {}
85
90
self .namespace = namespace or os .getenv ("POWERTOOLS_METRICS_NAMESPACE" )
86
91
self .service = service or os .environ .get ("POWERTOOLS_SERVICE_NAME" )
87
92
self ._metric_units = [unit .value for unit in MetricUnit ]
88
93
self ._metric_unit_options = list (MetricUnit .__members__ )
94
+ self .metadata_set = self .metadata_set if metadata_set is not None else {}
89
95
90
96
def add_metric (self , name : str , unit : MetricUnit , value : Union [float , int ]):
91
97
"""Adds given metric
@@ -131,7 +137,7 @@ def add_metric(self, name: str, unit: MetricUnit, value: Union[float, int]):
131
137
# since we could have more than 100 metrics
132
138
self .metric_set .clear ()
133
139
134
- def serialize_metric_set (self , metrics : Dict = None , dimensions : Dict = None ) -> Dict :
140
+ def serialize_metric_set (self , metrics : Dict = None , dimensions : Dict = None , metadata : Dict = None ) -> Dict :
135
141
"""Serializes metric and dimensions set
136
142
137
143
Parameters
@@ -165,6 +171,9 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) ->
165
171
if dimensions is None : # pragma: no cover
166
172
dimensions = self .dimension_set
167
173
174
+ if metadata is None : # pragma: no cover
175
+ metadata = self .metadata_set
176
+
168
177
if self .service and not self .dimension_set .get ("service" ):
169
178
self .dimension_set ["service" ] = self .service
170
179
@@ -190,6 +199,7 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) ->
190
199
metrics_timestamp = {"Timestamp" : int (datetime .datetime .now ().timestamp () * 1000 )}
191
200
metric_set ["_aws" ] = {** metrics_timestamp , ** metrics_definition }
192
201
metric_set .update (** dimensions )
202
+ metric_set .update (** metadata )
193
203
194
204
try :
195
205
logger .debug ("Validating serialized metrics against CloudWatch EMF schema" , metric_set )
@@ -225,6 +235,38 @@ def add_dimension(self, name: str, value: str):
225
235
else :
226
236
self .dimension_set [name ] = str (value )
227
237
238
+ def add_metadata (self , key : str , value : Any ):
239
+ """Adds high cardinal metadata for metrics object
240
+
241
+ This will not be available during metrics visualization.
242
+ Instead, this will be searchable through logs.
243
+
244
+ If you're looking to add metadata to filter metrics, then
245
+ use add_dimensions method.
246
+
247
+ Example
248
+ -------
249
+ **Add metrics metadata**
250
+
251
+ metric.add_metadata(key="booking_id", value="booking_id")
252
+
253
+ Parameters
254
+ ----------
255
+ name : str
256
+ Metadata key
257
+ value : any
258
+ Metadata value
259
+ """
260
+ logger .debug (f"Adding metadata: { key } :{ value } " )
261
+
262
+ # Cast key to str according to EMF spec
263
+ # Majority of keys are expected to be string already, so
264
+ # checking before casting improves performance in most cases
265
+ if isinstance (key , str ):
266
+ self .metadata_set [key ] = value
267
+ else :
268
+ self .metadata_set [str (key )] = value
269
+
228
270
def __extract_metric_unit_value (self , unit : Union [str , MetricUnit ]) -> str :
229
271
"""Return metric value from metric unit whether that's str or MetricUnit enum
230
272
0 commit comments