diff --git a/aws_lambda_powertools/metrics/base.py b/aws_lambda_powertools/metrics/base.py index 83949ad874d..9d2f07b677f 100644 --- a/aws_lambda_powertools/metrics/base.py +++ b/aws_lambda_powertools/metrics/base.py @@ -203,7 +203,14 @@ def add_dimension(self, name: str, value: str): Dimension value """ logger.debug(f"Adding dimension: {name}:{value}") - self.dimension_set[name] = value + + # Cast value to str according to EMF spec + # Majority of values are expected to be string already, so + # checking before casting improves performance in most cases + if isinstance(value, str): + self.dimension_set[name] = value + else: + self.dimension_set[name] = str(value) def __extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str: """Return metric value from metric unit whether that's str or MetricUnit enum diff --git a/tests/functional/test_metrics.py b/tests/functional/test_metrics.py index 71610bc0f19..b5c1a3232de 100644 --- a/tests/functional/test_metrics.py +++ b/tests/functional/test_metrics.py @@ -1,5 +1,5 @@ import json -from typing import Dict, List +from typing import Any, Dict, List import pytest @@ -48,6 +48,14 @@ def dimensions() -> List[Dict[str, str]]: ] +@pytest.fixture +def non_str_dimensions() -> List[Dict[str, Any]]: + return [ + {"name": "test_dimension", "value": True}, + {"name": "test_dimension_2", "value": 3}, + ] + + @pytest.fixture def namespace() -> Dict[str, str]: return {"name": "test_namespace"} @@ -380,3 +388,27 @@ def lambda_handler(evt, context): # THEN metric set should be empty after function has been run assert my_metrics.metric_set == {} + + +def test_log_metrics_non_string_dimension_values(capsys, metrics, non_str_dimensions, namespace): + # GIVEN Metrics is initialized and dimensions with non-string values are added + my_metrics = Metrics() + my_metrics.add_namespace(**namespace) + for metric in metrics: + my_metrics.add_metric(**metric) + for dimension in non_str_dimensions: + my_metrics.add_dimension(**dimension) + + # WHEN we utilize log_metrics to serialize + # and flush all metrics at the end of a function execution + @my_metrics.log_metrics + def lambda_handler(evt, ctx): + return True + + lambda_handler({}, {}) + output = json.loads(capsys.readouterr().out.strip()) + + # THEN we should have no exceptions + # and dimension values hould be serialized as strings + for dimension in non_str_dimensions: + assert isinstance(output[dimension["name"]], str)