From 2a70f9a56411ff7154abb3d7523dcfcd19d5f0ce Mon Sep 17 00:00:00 2001 From: Vishwak Thatikonda Date: Wed, 26 Aug 2026 00:04:05 -0700 Subject: [PATCH] fix(metrics): stop spurious overwrite warnings from set_default_dimensions Metrics.set_default_dimensions called provider.set_default_dimensions and then re-added every dimension through add_dimension, so the second pass always found the keys already registered and warned even on the first call. Remove the redundant loop and delegate to the provider. The provider also re-registers default dimensions internally, in clear_metrics after every flush and on repeated set_default_dimensions calls, which triggered the same warning on every warm invocation. Warn only when a dimension is overwritten with a different value, matching the warning message and the intent of #5653. Closes #8402 --- aws_lambda_powertools/metrics/metrics.py | 5 +- .../provider/cloudwatch_emf/cloudwatch.py | 2 +- .../test_metrics_cloudwatch_emf.py | 74 +++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/metrics/metrics.py b/aws_lambda_powertools/metrics/metrics.py index 2ed7b43c35e..c7a73adc88d 100644 --- a/aws_lambda_powertools/metrics/metrics.py +++ b/aws_lambda_powertools/metrics/metrics.py @@ -174,7 +174,6 @@ def log_metrics( ) def set_default_dimensions(self, **dimensions) -> None: - self.provider.set_default_dimensions(**dimensions) """Persist dimensions across Lambda invocations Parameters @@ -195,9 +194,7 @@ def set_default_dimensions(self, **dimensions) -> None: def lambda_handler(): return True """ - for name, value in dimensions.items(): - self.add_dimension(name, value) - + self.provider.set_default_dimensions(**dimensions) self.default_dimensions.update(**dimensions) def clear_default_dimensions(self) -> None: diff --git a/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py b/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py index 243fc561593..267e4308390 100644 --- a/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py +++ b/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py @@ -317,7 +317,7 @@ def add_dimension(self, name: str, value: str) -> None: ) return - if name in self.dimension_set or name in self.default_dimensions: + if name in self.dimension_set and self.dimension_set[name] != value: warnings.warn( f"Dimension '{name}' has already been added. The previous value will be overwritten.", category=PowertoolsUserWarning, diff --git a/tests/functional/metrics/required_dependencies/test_metrics_cloudwatch_emf.py b/tests/functional/metrics/required_dependencies/test_metrics_cloudwatch_emf.py index 834575e4754..5c11f205cfa 100644 --- a/tests/functional/metrics/required_dependencies/test_metrics_cloudwatch_emf.py +++ b/tests/functional/metrics/required_dependencies/test_metrics_cloudwatch_emf.py @@ -1133,6 +1133,80 @@ def test_clear_default_dimensions(namespace): assert not my_metrics.default_dimensions +def test_set_default_dimensions_no_warning_on_first_call(namespace): + # GIVEN a Metrics instance with no dimensions set + my_metrics = Metrics(namespace=namespace) + + # WHEN we persist default dimensions for the first time + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default") + my_metrics.set_default_dimensions(environment="test", log_group="/lambda/test") + + # THEN no overwrite warning should be emitted + assert not [warning for warning in w if "has already been added" in str(warning.message)] + + +def test_set_default_dimensions_no_warning_when_unchanged(namespace): + # GIVEN a Metrics instance with default dimensions persisted + my_metrics = Metrics(namespace=namespace) + my_metrics.set_default_dimensions(environment="test", log_group="/lambda/test") + + # WHEN we persist the same default dimensions again e.g., on a warm invocation + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default") + my_metrics.set_default_dimensions(environment="test", log_group="/lambda/test") + + # THEN no overwrite warning should be emitted + assert not [warning for warning in w if "has already been added" in str(warning.message)] + + +def test_set_default_dimensions_warns_when_value_changes(namespace): + # GIVEN a Metrics instance with a default dimension persisted + my_metrics = Metrics(namespace=namespace) + my_metrics.set_default_dimensions(environment="test") + + # WHEN we persist the same default dimension with a different value + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default") + my_metrics.set_default_dimensions(environment="prod") + + # THEN a single overwrite warning should be emitted + assert len([warning for warning in w if "has already been added" in str(warning.message)]) == 1 + + +def test_log_metrics_with_default_dimensions_no_warning_across_invocations(namespace, metric, capsys): + # GIVEN a Metrics instance with default dimensions persisted + my_metrics = Metrics(namespace=namespace) + my_metrics.set_default_dimensions(environment="test", log_group="/lambda/test") + + @my_metrics.log_metrics + def lambda_handler(evt, ctx): + my_metrics.add_metric(**metric) + + # WHEN metrics are flushed across multiple invocations + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default") + lambda_handler({}, {}) + lambda_handler({}, {}) + + # THEN no overwrite warning should be emitted + assert not [warning for warning in w if "has already been added" in str(warning.message)] + + +def test_add_dimension_no_warning_when_value_unchanged(namespace): + # GIVEN a Metrics instance with a dimension added + my_metrics = Metrics(namespace=namespace) + my_metrics.add_dimension("environment", "test") + + # WHEN the same dimension is added again with the same value + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default") + my_metrics.add_dimension("environment", "test") + + # THEN no overwrite warning should be emitted + assert not [warning for warning in w if "has already been added" in str(warning.message)] + + def test_add_dimensions_with_empty_value(namespace, capsys, metric): # GIVEN Metrics is initialized my_metrics = Metrics(namespace=namespace)