Skip to content

Commit 0438143

Browse files
author
Tom McCarthy
committed
fix: default dimension creation now happens when metrics are serialized instead of on metrics constructor (#74)
1 parent 31c2620 commit 0438143

File tree

3 files changed

+59
-17
lines changed

3 files changed

+59
-17
lines changed

Diff for: aws_lambda_powertools/metrics/base.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class MetricManager:
3636
---------------------
3737
POWERTOOLS_METRICS_NAMESPACE : str
3838
metric namespace to be set for all metrics
39+
POWERTOOLS_SERVICE_NAME : str
40+
service name used for default dimension
3941
4042
Raises
4143
------
@@ -49,10 +51,13 @@ class MetricManager:
4951
When metric object fails EMF schema validation
5052
"""
5153

52-
def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):
54+
def __init__(
55+
self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None, service: str = None
56+
):
5357
self.metric_set = metric_set if metric_set is not None else {}
5458
self.dimension_set = dimension_set if dimension_set is not None else {}
5559
self.namespace = namespace or os.getenv("POWERTOOLS_METRICS_NAMESPACE")
60+
self.service = service or os.environ.get("POWERTOOLS_SERVICE_NAME")
5661
self._metric_units = [unit.value for unit in MetricUnit]
5762
self._metric_unit_options = list(MetricUnit.__members__)
5863

@@ -158,6 +163,9 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) ->
158163
if dimensions is None: # pragma: no cover
159164
dimensions = self.dimension_set
160165

166+
if self.service and not self.dimension_set.get("service"):
167+
self.dimension_set["service"] = self.service
168+
161169
logger.debug("Serializing...", {"metrics": metrics, "dimensions": dimensions})
162170

163171
dimension_keys: List[str] = list(dimensions.keys())

Diff for: aws_lambda_powertools/metrics/metrics.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import functools
22
import json
33
import logging
4-
import os
54
import warnings
65
from typing import Any, Callable
76

@@ -72,11 +71,11 @@ def do_something():
7271
def __init__(self, service: str = None, namespace: str = None):
7372
self.metric_set = self._metrics
7473
self.dimension_set = self._dimensions
75-
self.service = service or os.environ.get("POWERTOOLS_SERVICE_NAME")
74+
self.service = service
7675
self.namespace = namespace
77-
if self.service:
78-
self.dimension_set["service"] = self.service
79-
super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set, namespace=self.namespace)
76+
super().__init__(
77+
metric_set=self.metric_set, dimension_set=self.dimension_set, namespace=self.namespace, service=self.service
78+
)
8079

8180
def clear_metrics(self):
8281
logger.debug("Clearing out existing metric set from memory")

Diff for: tests/functional/test_metrics.py

+46-11
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ def lambda_handler(evt, ctx):
466466

467467
output = json.loads(capsys.readouterr().out.strip())
468468

469-
dimensions.insert(0, {"name": "service", "value": "test_service"})
469+
dimensions.append({"name": "service", "value": "test_service"})
470470
expected = serialize_metrics(metrics=metrics, dimensions=dimensions, namespace=namespace)
471471

472472
remove_timestamp(metrics=[output, expected]) # Timestamp will always be different
@@ -503,33 +503,31 @@ def lambda_handler(evt, ctx):
503503
assert expected == output
504504

505505

506-
def test_log_metrics_with_renamed_service(capsys, metrics):
506+
def test_log_metrics_with_renamed_service(capsys, metrics, metric):
507507
# GIVEN Metrics is initialized with service specified
508508
my_metrics = Metrics(service="test_service", namespace="test_application")
509509
for metric in metrics:
510510
my_metrics.add_metric(**metric)
511511

512-
# WHEN we manually call add_dimension to change the value of the service dimension
513-
my_metrics.add_dimension(name="service", value="another_test_service")
514-
515512
@my_metrics.log_metrics
516513
def lambda_handler(evt, ctx):
514+
# WHEN we manually call add_dimension to change the value of the service dimension
515+
my_metrics.add_dimension(name="service", value="another_test_service")
516+
my_metrics.add_metric(**metric)
517517
return True
518518

519519
lambda_handler({}, {})
520520

521521
output = json.loads(capsys.readouterr().out.strip())
522+
lambda_handler({}, {})
523+
second_output = json.loads(capsys.readouterr().out.strip())
522524

523-
expected_dimensions = [{"name": "service", "value": "test_service"}]
524-
expected = serialize_metrics(
525-
metrics=metrics, dimensions=expected_dimensions, namespace={"name": "test_application"}
526-
)
527-
528-
remove_timestamp(metrics=[output, expected]) # Timestamp will always be different
525+
remove_timestamp(metrics=[output]) # Timestamp will always be different
529526

530527
# THEN we should have no exceptions and the dimensions should be set to the name provided in the
531528
# add_dimension call
532529
assert output["service"] == "another_test_service"
530+
assert second_output["service"] == "another_test_service"
533531

534532

535533
def test_log_metrics_with_namespace_overridden(capsys, metrics, dimensions):
@@ -649,3 +647,40 @@ def lambda_handler(evt, context):
649647
lambda_handler({}, {})
650648
assert len(w) == 1
651649
assert str(w[-1].message) == "No metrics to publish, skipping"
650+
651+
652+
def test_log_metrics_with_implicit_dimensions_called_twice(capsys, metrics):
653+
# GIVEN Metrics is initialized with service specified
654+
my_metrics = Metrics(service="test_service", namespace="test_application")
655+
656+
# WHEN we utilize log_metrics to serialize and don't explicitly add any dimensions,
657+
# and the lambda function is called more than once
658+
@my_metrics.log_metrics
659+
def lambda_handler(evt, ctx):
660+
for metric in metrics:
661+
my_metrics.add_metric(**metric)
662+
return True
663+
664+
lambda_handler({}, {})
665+
output = json.loads(capsys.readouterr().out.strip())
666+
667+
lambda_handler({}, {})
668+
second_output = json.loads(capsys.readouterr().out.strip())
669+
670+
expected_dimensions = [{"name": "service", "value": "test_service"}]
671+
expected = serialize_metrics(
672+
metrics=metrics, dimensions=expected_dimensions, namespace={"name": "test_application"}
673+
)
674+
675+
remove_timestamp(metrics=[output, expected, second_output]) # Timestamp will always be different
676+
677+
# THEN we should have no exceptions and the dimensions should be set to the name provided in the
678+
# service passed to Metrics constructor
679+
assert output["service"] == "test_service"
680+
assert second_output["service"] == "test_service"
681+
682+
for metric_record in output["_aws"]["CloudWatchMetrics"]:
683+
assert ["service"] in metric_record["Dimensions"]
684+
685+
for metric_record in second_output["_aws"]["CloudWatchMetrics"]:
686+
assert ["service"] in metric_record["Dimensions"]

0 commit comments

Comments
 (0)