Skip to content

Commit d4607f3

Browse files
rubenfonsecaheitorlessaleandrodamascena
authored
feat(metrics): add flush_metrics() method to allow manual flushing of metrics (#2171)
Co-authored-by: Heitor Lessa <[email protected]> Co-authored-by: Leandro Damascena <[email protected]>
1 parent 14147cb commit d4607f3

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
lines changed

Diff for: aws_lambda_powertools/metrics/base.py

+23-10
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,28 @@ def clear_metrics(self) -> None:
328328
self.dimension_set.clear()
329329
self.metadata_set.clear()
330330

331+
def flush_metrics(self, raise_on_empty_metrics: bool = False) -> None:
332+
"""Manually flushes the metrics. This is normally not necessary,
333+
unless you're running on other runtimes besides Lambda, where the @log_metrics
334+
decorator already handles things for you.
335+
336+
Parameters
337+
----------
338+
raise_on_empty_metrics : bool, optional
339+
raise exception if no metrics are emitted, by default False
340+
"""
341+
if not raise_on_empty_metrics and not self.metric_set:
342+
warnings.warn(
343+
"No application metrics to publish. The cold-start metric may be published if enabled. "
344+
"If application metrics should never be empty, consider using 'raise_on_empty_metrics'",
345+
stacklevel=2,
346+
)
347+
else:
348+
logger.debug("Flushing existing metrics")
349+
metrics = self.serialize_metric_set()
350+
print(json.dumps(metrics, separators=(",", ":")))
351+
self.clear_metrics()
352+
331353
def log_metrics(
332354
self,
333355
lambda_handler: Union[Callable[[Dict, Any], Any], Optional[Callable[[Dict, Any, Optional[Dict]], Any]]] = None,
@@ -390,16 +412,7 @@ def decorate(event, context):
390412
if capture_cold_start_metric:
391413
self._add_cold_start_metric(context=context)
392414
finally:
393-
if not raise_on_empty_metrics and not self.metric_set:
394-
warnings.warn(
395-
"No application metrics to publish. The cold-start metric may be published if enabled. "
396-
"If application metrics should never be empty, consider using 'raise_on_empty_metrics'",
397-
stacklevel=2,
398-
)
399-
else:
400-
metrics = self.serialize_metric_set()
401-
self.clear_metrics()
402-
print(json.dumps(metrics, separators=(",", ":")))
415+
self.flush_metrics(raise_on_empty_metrics=raise_on_empty_metrics)
403416

404417
return response
405418

Diff for: docs/core/metrics.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,15 @@ By default it will skip all previously defined dimensions including default dime
251251

252252
### Flushing metrics manually
253253

254-
If you prefer not to use `log_metrics` because you might want to encapsulate additional logic when doing so, you can manually flush and clear metrics as follows:
254+
If you are using the AWS Lambda Web Adapter project, or a middleware with custom metric logic, you can use `flush_metrics()`. This method will serialize, print metrics available to standard output, and clear in-memory metrics data.
255255

256256
???+ warning
257-
Metrics, dimensions and namespace validation still applies
257+
This does not capture Cold Start metrics, and metric data validation still applies.
258258

259-
```python hl_lines="11-14" title="Manually flushing and clearing metrics from memory"
260-
--8<-- "examples/metrics/src/single_metric.py"
259+
Contrary to the `log_metrics` decorator, you are now also responsible to flush metrics in the event of an exception.
260+
261+
```python hl_lines="18" title="Manually flushing and clearing metrics from memory"
262+
--8<-- "examples/metrics/src/flush_metrics.py"
261263
```
262264

263265
### Metrics isolation
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import json
2-
31
from aws_lambda_powertools import Metrics
42
from aws_lambda_powertools.metrics import MetricUnit
53
from aws_lambda_powertools.utilities.typing import LambdaContext
64

75
metrics = Metrics()
86

97

10-
def lambda_handler(event: dict, context: LambdaContext):
8+
def book_flight(flight_id: str, **kwargs):
9+
# logic to book flight
10+
...
1111
metrics.add_metric(name="SuccessfulBooking", unit=MetricUnit.Count, value=1)
12-
your_metrics_object = metrics.serialize_metric_set()
13-
metrics.clear_metrics()
14-
print(json.dumps(your_metrics_object))
12+
13+
14+
def lambda_handler(event: dict, context: LambdaContext):
15+
try:
16+
book_flight(flight_id=event.get("flight_id", ""))
17+
finally:
18+
metrics.flush_metrics()

Diff for: tests/functional/test_metrics.py

+20
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,26 @@ def lambda_handler(evt, ctx):
249249
assert expected == output
250250

251251

252+
def test_log_metrics_manual_flush(capsys, metrics, dimensions, namespace):
253+
# GIVEN Metrics is initialized
254+
my_metrics = Metrics(namespace=namespace)
255+
for metric in metrics:
256+
my_metrics.add_metric(**metric)
257+
for dimension in dimensions:
258+
my_metrics.add_dimension(**dimension)
259+
260+
# WHEN we manually the metrics
261+
my_metrics.flush_metrics()
262+
263+
output = capture_metrics_output(capsys)
264+
expected = serialize_metrics(metrics=metrics, dimensions=dimensions, namespace=namespace)
265+
266+
# THEN we should have no exceptions
267+
# and a valid EMF object should be flushed correctly
268+
remove_timestamp(metrics=[output, expected])
269+
assert expected == output
270+
271+
252272
def test_namespace_env_var(monkeypatch, capsys, metric, dimension, namespace):
253273
# GIVEN POWERTOOLS_METRICS_NAMESPACE is set
254274
monkeypatch.setenv("POWERTOOLS_METRICS_NAMESPACE", namespace)

0 commit comments

Comments
 (0)