Skip to content

Commit cdf9084

Browse files
roger-zhanggleandrodamascenaCavalcante Damascena
authored
feat(metrics): support to bring your own metrics provider (#2194)
* Use a different Metric class for different provider * fix static checking error * fix static checking error * optimize docstring * add alias CloudWatchEMF to original Metrics class * add alias CloudWatchEMF to original Metrics class * Move Metrics to Provider * add sample document * reformat code block * add OTel provider draft for poc * rebasing from upstream * rebasing from upstream * add test to metrics providers * docstring + code coverage * python annotations + imports * fix docstring polish datadog_provider add flush_to_log parameter remove OTEL provider draft * add tests for datadog provider * add tests for datadog provider * migrate from ABC to protocol, support convert kwargs to tags, add test * migrate from ABC to protocol, support convert kwargs to tags, add test * migrate from ABC to protocol, support convert kwargs to tags, add test * remove parent class, fix example * base: fix small problems * refactoring: removing Datadog provider * refactoring: importing from typing_extensions * refactoring EMF provider * refactoring cloudwatchemf provider and cleaning code * fix mypy error * fix mypy error * fix metric tests * fix documentation * adding test --------- Signed-off-by: Leandro Damascena <[email protected]> Co-authored-by: Leandro Damascena <[email protected]> Co-authored-by: Leandro Damascena <[email protected]> Co-authored-by: Cavalcante Damascena <[email protected]>
1 parent 0916bc5 commit cdf9084

18 files changed

+1317
-206
lines changed

Diff for: .gitleaks.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Title for the gitleaks configuration file.
2+
title = "Gitleaks"
3+
4+
[extend]
5+
# useDefault will extend the base configuration with the default gitleaks config:
6+
# https://github.com/zricethezav/gitleaks/blob/master/config/gitleaks.toml
7+
useDefault = true
8+
9+
[allowlist]
10+
description = "Allow list false positive"
11+
12+
# Allow list paths to ignore due to false positives.
13+
paths = [
14+
'''tests/unit/parser/test_kinesis_firehose\.py''',
15+
]

Diff for: aws_lambda_powertools/metrics/__init__.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
"""CloudWatch Embedded Metric Format utility
22
"""
3-
from .base import MetricResolution, MetricUnit
4-
from .exceptions import (
3+
from aws_lambda_powertools.metrics.base import MetricResolution, MetricUnit, single_metric
4+
from aws_lambda_powertools.metrics.exceptions import (
55
MetricResolutionError,
66
MetricUnitError,
77
MetricValueError,
88
SchemaValidationError,
99
)
10-
from .metric import single_metric
11-
from .metrics import EphemeralMetrics, Metrics
10+
from aws_lambda_powertools.metrics.metrics import EphemeralMetrics, Metrics
1211

1312
__all__ = [
14-
"Metrics",
15-
"EphemeralMetrics",
1613
"single_metric",
17-
"MetricUnit",
1814
"MetricUnitError",
19-
"MetricResolution",
2015
"MetricResolutionError",
2116
"SchemaValidationError",
2217
"MetricValueError",
18+
"Metrics",
19+
"EphemeralMetrics",
20+
"MetricResolution",
21+
"MetricUnit",
2322
]

Diff for: aws_lambda_powertools/metrics/base.py

+32-67
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import datetime
24
import functools
35
import json
@@ -7,59 +9,28 @@
79
import warnings
810
from collections import defaultdict
911
from contextlib import contextmanager
10-
from enum import Enum
1112
from typing import Any, Callable, Dict, Generator, List, Optional, Union
1213

13-
from ..shared import constants
14-
from ..shared.functions import resolve_env_var_choice
15-
from .exceptions import (
14+
from aws_lambda_powertools.metrics.exceptions import (
1615
MetricResolutionError,
1716
MetricUnitError,
1817
MetricValueError,
1918
SchemaValidationError,
2019
)
21-
from .types import MetricNameUnitResolution
20+
from aws_lambda_powertools.metrics.provider.cloudwatch_emf import cold_start
21+
from aws_lambda_powertools.metrics.provider.cloudwatch_emf.cold_start import (
22+
reset_cold_start_flag, # noqa: F401 # backwards compatibility
23+
)
24+
from aws_lambda_powertools.metrics.provider.cloudwatch_emf.constants import MAX_DIMENSIONS, MAX_METRICS
25+
from aws_lambda_powertools.metrics.provider.cloudwatch_emf.metric_properties import MetricResolution, MetricUnit
26+
from aws_lambda_powertools.metrics.types import MetricNameUnitResolution
27+
from aws_lambda_powertools.shared import constants
28+
from aws_lambda_powertools.shared.functions import resolve_env_var_choice
2229

2330
logger = logging.getLogger(__name__)
2431

25-
MAX_METRICS = 100
26-
MAX_DIMENSIONS = 29
27-
28-
is_cold_start = True
29-
30-
31-
class MetricResolution(Enum):
32-
Standard = 60
33-
High = 1
34-
35-
36-
class MetricUnit(Enum):
37-
Seconds = "Seconds"
38-
Microseconds = "Microseconds"
39-
Milliseconds = "Milliseconds"
40-
Bytes = "Bytes"
41-
Kilobytes = "Kilobytes"
42-
Megabytes = "Megabytes"
43-
Gigabytes = "Gigabytes"
44-
Terabytes = "Terabytes"
45-
Bits = "Bits"
46-
Kilobits = "Kilobits"
47-
Megabits = "Megabits"
48-
Gigabits = "Gigabits"
49-
Terabits = "Terabits"
50-
Percent = "Percent"
51-
Count = "Count"
52-
BytesPerSecond = "Bytes/Second"
53-
KilobytesPerSecond = "Kilobytes/Second"
54-
MegabytesPerSecond = "Megabytes/Second"
55-
GigabytesPerSecond = "Gigabytes/Second"
56-
TerabytesPerSecond = "Terabytes/Second"
57-
BitsPerSecond = "Bits/Second"
58-
KilobitsPerSecond = "Kilobits/Second"
59-
MegabitsPerSecond = "Megabits/Second"
60-
GigabitsPerSecond = "Gigabits/Second"
61-
TerabitsPerSecond = "Terabits/Second"
62-
CountPerSecond = "Count/Second"
32+
# Maintenance: alias due to Hyrum's law
33+
is_cold_start = cold_start.is_cold_start
6334

6435

6536
class MetricManager:
@@ -94,11 +65,11 @@ class MetricManager:
9465

9566
def __init__(
9667
self,
97-
metric_set: Optional[Dict[str, Any]] = None,
98-
dimension_set: Optional[Dict] = None,
99-
namespace: Optional[str] = None,
100-
metadata_set: Optional[Dict[str, Any]] = None,
101-
service: Optional[str] = None,
68+
metric_set: Dict[str, Any] | None = None,
69+
dimension_set: Dict | None = None,
70+
namespace: str | None = None,
71+
metadata_set: Dict[str, Any] | None = None,
72+
service: str | None = None,
10273
):
10374
self.metric_set = metric_set if metric_set is not None else {}
10475
self.dimension_set = dimension_set if dimension_set is not None else {}
@@ -112,9 +83,9 @@ def __init__(
11283
def add_metric(
11384
self,
11485
name: str,
115-
unit: Union[MetricUnit, str],
86+
unit: MetricUnit | str,
11687
value: float,
117-
resolution: Union[MetricResolution, int] = 60,
88+
resolution: MetricResolution | int = 60,
11889
) -> None:
11990
"""Adds given metric
12091
@@ -173,9 +144,9 @@ def add_metric(
173144

174145
def serialize_metric_set(
175146
self,
176-
metrics: Optional[Dict] = None,
177-
dimensions: Optional[Dict] = None,
178-
metadata: Optional[Dict] = None,
147+
metrics: Dict | None = None,
148+
dimensions: Dict | None = None,
149+
metadata: Dict | None = None,
179150
) -> Dict:
180151
"""Serializes metric and dimensions set
181152
@@ -355,10 +326,10 @@ def flush_metrics(self, raise_on_empty_metrics: bool = False) -> None:
355326

356327
def log_metrics(
357328
self,
358-
lambda_handler: Union[Callable[[Dict, Any], Any], Optional[Callable[[Dict, Any, Optional[Dict]], Any]]] = None,
329+
lambda_handler: Callable[[Dict, Any], Any] | Optional[Callable[[Dict, Any, Optional[Dict]], Any]] = None,
359330
capture_cold_start_metric: bool = False,
360331
raise_on_empty_metrics: bool = False,
361-
default_dimensions: Optional[Dict[str, str]] = None,
332+
default_dimensions: Dict[str, str] | None = None,
362333
):
363334
"""Decorator to serialize and publish metrics at the end of a function execution.
364335
@@ -537,9 +508,9 @@ class SingleMetric(MetricManager):
537508
def add_metric(
538509
self,
539510
name: str,
540-
unit: Union[MetricUnit, str],
511+
unit: MetricUnit | str,
541512
value: float,
542-
resolution: Union[MetricResolution, int] = 60,
513+
resolution: MetricResolution | int = 60,
543514
) -> None:
544515
"""Method to prevent more than one metric being created
545516
@@ -565,9 +536,9 @@ def single_metric(
565536
name: str,
566537
unit: MetricUnit,
567538
value: float,
568-
resolution: Union[MetricResolution, int] = 60,
569-
namespace: Optional[str] = None,
570-
default_dimensions: Optional[Dict[str, str]] = None,
539+
resolution: MetricResolution | int = 60,
540+
namespace: str | None = None,
541+
default_dimensions: Dict[str, str] | None = None,
571542
) -> Generator[SingleMetric, None, None]:
572543
"""Context manager to simplify creation of a single metric
573544
@@ -622,7 +593,7 @@ def single_metric(
622593
SchemaValidationError
623594
When metric object fails EMF schema validation
624595
""" # noqa: E501
625-
metric_set: Optional[Dict] = None
596+
metric_set: Dict | None = None
626597
try:
627598
metric: SingleMetric = SingleMetric(namespace=namespace)
628599
metric.add_metric(name=name, unit=unit, value=value, resolution=resolution)
@@ -635,9 +606,3 @@ def single_metric(
635606
metric_set = metric.serialize_metric_set()
636607
finally:
637608
print(json.dumps(metric_set, separators=(",", ":")))
638-
639-
640-
def reset_cold_start_flag():
641-
global is_cold_start
642-
if not is_cold_start:
643-
is_cold_start = True

Diff for: aws_lambda_powertools/metrics/exceptions.py

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
class MetricUnitError(Exception):
2-
"""When metric unit is not supported by CloudWatch"""
3-
4-
pass
5-
6-
7-
class MetricResolutionError(Exception):
8-
"""When metric resolution is not supported by CloudWatch"""
9-
10-
pass
1+
from aws_lambda_powertools.metrics.provider.cloudwatch_emf.exceptions import MetricResolutionError, MetricUnitError
112

123

134
class SchemaValidationError(Exception):
@@ -20,3 +11,6 @@ class MetricValueError(Exception):
2011
"""When metric value isn't a valid number"""
2112

2213
pass
14+
15+
16+
__all__ = ["MetricUnitError", "MetricResolutionError", "SchemaValidationError", "MetricValueError"]

Diff for: aws_lambda_powertools/metrics/metric.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# NOTE: prevents circular inheritance import
2-
from .base import SingleMetric, single_metric
2+
from aws_lambda_powertools.metrics.base import SingleMetric, single_metric
33

44
__all__ = ["SingleMetric", "single_metric"]

0 commit comments

Comments
 (0)