12
12
13
13
from ..shared import constants
14
14
from ..shared .functions import resolve_env_var_choice
15
- from .exceptions import MetricUnitError , MetricValueError , SchemaValidationError
15
+ from .exceptions import (
16
+ MetricResolutionError ,
17
+ MetricUnitError ,
18
+ MetricValueError ,
19
+ SchemaValidationError ,
20
+ )
21
+ from .types import MetricNameUnitResolution
16
22
17
23
logger = logging .getLogger (__name__ )
18
24
22
28
is_cold_start = True
23
29
24
30
31
+ class MetricResolution (Enum ):
32
+ Standard = 60
33
+ High = 1
34
+
35
+
25
36
class MetricUnit (Enum ):
26
37
Seconds = "Seconds"
27
38
Microseconds = "Microseconds"
@@ -72,7 +83,9 @@ class MetricManager:
72
83
Raises
73
84
------
74
85
MetricUnitError
75
- When metric metric isn't supported by CloudWatch
86
+ When metric unit isn't supported by CloudWatch
87
+ MetricResolutionError
88
+ When metric resolution isn't supported by CloudWatch
76
89
MetricValueError
77
90
When metric value isn't a number
78
91
SchemaValidationError
@@ -93,9 +106,16 @@ def __init__(
93
106
self .service = resolve_env_var_choice (choice = service , env = os .getenv (constants .SERVICE_NAME_ENV ))
94
107
self .metadata_set = metadata_set if metadata_set is not None else {}
95
108
self ._metric_units = [unit .value for unit in MetricUnit ]
96
- self ._metric_unit_options = list (MetricUnit .__members__ )
109
+ self ._metric_unit_valid_options = list (MetricUnit .__members__ )
110
+ self ._metric_resolutions = [resolution .value for resolution in MetricResolution ]
97
111
98
- def add_metric (self , name : str , unit : Union [MetricUnit , str ], value : float ) -> None :
112
+ def add_metric (
113
+ self ,
114
+ name : str ,
115
+ unit : Union [MetricUnit , str ],
116
+ value : float ,
117
+ resolution : Union [MetricResolution , int ] = 60 ,
118
+ ) -> None :
99
119
"""Adds given metric
100
120
101
121
Example
@@ -108,6 +128,10 @@ def add_metric(self, name: str, unit: Union[MetricUnit, str], value: float) -> N
108
128
109
129
metric.add_metric(name="BookingConfirmation", unit="Count", value=1)
110
130
131
+ **Add given metric with MetricResolution non default value**
132
+
133
+ metric.add_metric(name="BookingConfirmation", unit="Count", value=1, resolution=MetricResolution.High)
134
+
111
135
Parameters
112
136
----------
113
137
name : str
@@ -116,18 +140,24 @@ def add_metric(self, name: str, unit: Union[MetricUnit, str], value: float) -> N
116
140
`aws_lambda_powertools.helper.models.MetricUnit`
117
141
value : float
118
142
Metric value
143
+ resolution : Union[MetricResolution, int]
144
+ `aws_lambda_powertools.helper.models.MetricResolution`
119
145
120
146
Raises
121
147
------
122
148
MetricUnitError
123
149
When metric unit is not supported by CloudWatch
150
+ MetricResolutionError
151
+ When metric resolution is not supported by CloudWatch
124
152
"""
125
153
if not isinstance (value , numbers .Number ):
126
154
raise MetricValueError (f"{ value } is not a valid number" )
127
155
128
156
unit = self ._extract_metric_unit_value (unit = unit )
157
+ resolution = self ._extract_metric_resolution_value (resolution = resolution )
129
158
metric : Dict = self .metric_set .get (name , defaultdict (list ))
130
159
metric ["Unit" ] = unit
160
+ metric ["StorageResolution" ] = resolution
131
161
metric ["Value" ].append (float (value ))
132
162
logger .debug (f"Adding metric: { name } with { metric } " )
133
163
self .metric_set [name ] = metric
@@ -194,15 +224,28 @@ def serialize_metric_set(
194
224
195
225
logger .debug ({"details" : "Serializing metrics" , "metrics" : metrics , "dimensions" : dimensions })
196
226
197
- metric_names_and_units : List [Dict [str , str ]] = [] # [ { "Name": "metric_name", "Unit": "Count" } ]
227
+ # For standard resolution metrics, don't add StorageResolution field to avoid unnecessary ingestion of data into cloudwatch # noqa E501
228
+ # Example: [ { "Name": "metric_name", "Unit": "Count"} ] # noqa E800
229
+ #
230
+ # In case using high-resolution metrics, add StorageResolution field
231
+ # Example: [ { "Name": "metric_name", "Unit": "Count", "StorageResolution": 1 } ] # noqa E800
232
+ metric_definition : List [MetricNameUnitResolution ] = []
198
233
metric_names_and_values : Dict [str , float ] = {} # { "metric_name": 1.0 }
199
234
200
235
for metric_name in metrics :
201
236
metric : dict = metrics [metric_name ]
202
237
metric_value : int = metric .get ("Value" , 0 )
203
238
metric_unit : str = metric .get ("Unit" , "" )
239
+ metric_resolution : int = metric .get ("StorageResolution" , 60 )
240
+
241
+ metric_definition_data : MetricNameUnitResolution = {"Name" : metric_name , "Unit" : metric_unit }
242
+
243
+ # high-resolution metrics
244
+ if metric_resolution == 1 :
245
+ metric_definition_data ["StorageResolution" ] = metric_resolution
246
+
247
+ metric_definition .append (metric_definition_data )
204
248
205
- metric_names_and_units .append ({"Name" : metric_name , "Unit" : metric_unit })
206
249
metric_names_and_values .update ({metric_name : metric_value })
207
250
208
251
return {
@@ -212,7 +255,7 @@ def serialize_metric_set(
212
255
{
213
256
"Namespace" : self .namespace , # "test_namespace"
214
257
"Dimensions" : [list (dimensions .keys ())], # [ "service" ]
215
- "Metrics" : metric_names_and_units ,
258
+ "Metrics" : metric_definition ,
216
259
}
217
260
],
218
261
},
@@ -358,6 +401,34 @@ def decorate(event, context):
358
401
359
402
return decorate
360
403
404
+ def _extract_metric_resolution_value (self , resolution : Union [int , MetricResolution ]) -> int :
405
+ """Return metric value from metric unit whether that's str or MetricResolution enum
406
+
407
+ Parameters
408
+ ----------
409
+ unit : Union[int, MetricResolution]
410
+ Metric resolution
411
+
412
+ Returns
413
+ -------
414
+ int
415
+ Metric resolution value must be 1 or 60
416
+
417
+ Raises
418
+ ------
419
+ MetricResolutionError
420
+ When metric resolution is not supported by CloudWatch
421
+ """
422
+ if isinstance (resolution , MetricResolution ):
423
+ return resolution .value
424
+
425
+ if isinstance (resolution , int ) and resolution in self ._metric_resolutions :
426
+ return resolution
427
+
428
+ raise MetricResolutionError (
429
+ f"Invalid metric resolution '{ resolution } ', expected either option: { self ._metric_resolutions } " # noqa: E501
430
+ )
431
+
361
432
def _extract_metric_unit_value (self , unit : Union [str , MetricUnit ]) -> str :
362
433
"""Return metric value from metric unit whether that's str or MetricUnit enum
363
434
@@ -378,12 +449,12 @@ def _extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str:
378
449
"""
379
450
380
451
if isinstance (unit , str ):
381
- if unit in self ._metric_unit_options :
452
+ if unit in self ._metric_unit_valid_options :
382
453
unit = MetricUnit [unit ].value
383
454
384
455
if unit not in self ._metric_units :
385
456
raise MetricUnitError (
386
- f"Invalid metric unit '{ unit } ', expected either option: { self ._metric_unit_options } "
457
+ f"Invalid metric unit '{ unit } ', expected either option: { self ._metric_unit_valid_options } "
387
458
)
388
459
389
460
if isinstance (unit , MetricUnit ):
@@ -429,10 +500,10 @@ class SingleMetric(MetricManager):
429
500
**Creates cold start metric with function_version as dimension**
430
501
431
502
import json
432
- from aws_lambda_powertools.metrics import single_metric, MetricUnit
503
+ from aws_lambda_powertools.metrics import single_metric, MetricUnit, MetricResolution
433
504
metric = single_metric(namespace="ServerlessAirline")
434
505
435
- metric.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
506
+ metric.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1, resolution=MetricResolution.Standard )
436
507
metric.add_dimension(name="function_version", value=47)
437
508
438
509
print(json.dumps(metric.serialize_metric_set(), indent=4))
@@ -443,7 +514,13 @@ class SingleMetric(MetricManager):
443
514
Inherits from `aws_lambda_powertools.metrics.base.MetricManager`
444
515
"""
445
516
446
- def add_metric (self , name : str , unit : Union [MetricUnit , str ], value : float ) -> None :
517
+ def add_metric (
518
+ self ,
519
+ name : str ,
520
+ unit : Union [MetricUnit , str ],
521
+ value : float ,
522
+ resolution : Union [MetricResolution , int ] = 60 ,
523
+ ) -> None :
447
524
"""Method to prevent more than one metric being created
448
525
449
526
Parameters
@@ -454,18 +531,21 @@ def add_metric(self, name: str, unit: Union[MetricUnit, str], value: float) -> N
454
531
Metric unit (e.g. "Seconds", MetricUnit.Seconds)
455
532
value : float
456
533
Metric value
534
+ resolution : MetricResolution
535
+ Metric resolution (e.g. 60, MetricResolution.Standard)
457
536
"""
458
537
if len (self .metric_set ) > 0 :
459
538
logger .debug (f"Metric { name } already set, skipping..." )
460
539
return
461
- return super ().add_metric (name , unit , value )
540
+ return super ().add_metric (name , unit , value , resolution )
462
541
463
542
464
543
@contextmanager
465
544
def single_metric (
466
545
name : str ,
467
546
unit : MetricUnit ,
468
547
value : float ,
548
+ resolution : Union [MetricResolution , int ] = 60 ,
469
549
namespace : Optional [str ] = None ,
470
550
default_dimensions : Optional [Dict [str , str ]] = None ,
471
551
) -> Generator [SingleMetric , None , None ]:
@@ -477,8 +557,9 @@ def single_metric(
477
557
478
558
from aws_lambda_powertools import single_metric
479
559
from aws_lambda_powertools.metrics import MetricUnit
560
+ from aws_lambda_powertools.metrics import MetricResolution
480
561
481
- with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, namespace="ServerlessAirline") as metric:
562
+ with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, resolution=MetricResolution.Standard, namespace="ServerlessAirline") as metric: # noqa E501
482
563
metric.add_dimension(name="function_version", value="47")
483
564
484
565
**Same as above but set namespace using environment variable**
@@ -487,8 +568,9 @@ def single_metric(
487
568
488
569
from aws_lambda_powertools import single_metric
489
570
from aws_lambda_powertools.metrics import MetricUnit
571
+ from aws_lambda_powertools.metrics import MetricResolution
490
572
491
- with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1) as metric:
573
+ with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, resolution=MetricResolution.Standard ) as metric: # noqa E501
492
574
metric.add_dimension(name="function_version", value="47")
493
575
494
576
Parameters
@@ -497,6 +579,8 @@ def single_metric(
497
579
Metric name
498
580
unit : MetricUnit
499
581
`aws_lambda_powertools.helper.models.MetricUnit`
582
+ resolution : MetricResolution
583
+ `aws_lambda_powertools.helper.models.MetricResolution`
500
584
value : float
501
585
Metric value
502
586
namespace: str
@@ -511,6 +595,8 @@ def single_metric(
511
595
------
512
596
MetricUnitError
513
597
When metric metric isn't supported by CloudWatch
598
+ MetricResolutionError
599
+ When metric resolution isn't supported by CloudWatch
514
600
MetricValueError
515
601
When metric value isn't a number
516
602
SchemaValidationError
@@ -519,7 +605,7 @@ def single_metric(
519
605
metric_set : Optional [Dict ] = None
520
606
try :
521
607
metric : SingleMetric = SingleMetric (namespace = namespace )
522
- metric .add_metric (name = name , unit = unit , value = value )
608
+ metric .add_metric (name = name , unit = unit , value = value , resolution = resolution )
523
609
524
610
if default_dimensions :
525
611
for dim_name , dim_value in default_dimensions .items ():
0 commit comments