Skip to content

feat(metrics): allow change ColdStart function_name dimension #6315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions aws_lambda_powertools/metrics/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
from datetime import datetime
from typing import TYPE_CHECKING

from aws_lambda_powertools.metrics.provider.cloudwatch_emf.exceptions import (
MetricResolutionError,
Expand All @@ -11,6 +12,9 @@
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.shared.functions import strtobool

if TYPE_CHECKING:
from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext


def extract_cloudwatch_metric_resolution_value(metric_resolutions: list, resolution: int | MetricResolution) -> int:
"""Return metric value from CloudWatch metric unit whether that's str or MetricResolution enum
Expand Down Expand Up @@ -161,3 +165,37 @@ def is_metrics_disabled() -> bool:
]

return any(disable_conditions)


def resolve_cold_start_function_name(function_name: str | None, context: LambdaContext) -> str:
"""
Resolve the function name for ColdStart metrics with a prioritized approach.

Parameters
----------
function_name : str, optional
Explicitly provided function name (highest priority).
context : LambdaContext
AWS Lambda context object.

Returns
-------
str
Resolved function name.

Notes
-----
Function name resolution follows this priority:
1. Explicitly provided function_name
2. Environment variable POWERTOOLS_METRICS_FUNCTION_NAME
3. Lambda context function name
"""

if function_name:
return function_name

metrics_function_name_env = os.getenv(constants.METRICS_FUNCTION_NAME_ENV)
if metrics_function_name_env:
return metrics_function_name_env

return context.function_name
2 changes: 2 additions & 0 deletions aws_lambda_powertools/metrics/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(
service: str | None = None,
namespace: str | None = None,
provider: AmazonCloudWatchEMFProvider | None = None,
function_name: str | None = None,
):
self.metric_set = self._metrics
self.metadata_set = self._metadata
Expand All @@ -102,6 +103,7 @@ def __init__(
dimension_set=self.dimension_set,
metadata_set=self.metadata_set,
default_dimensions=self._default_dimensions,
function_name=function_name,
)
else:
self.provider = provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
extract_cloudwatch_metric_resolution_value,
extract_cloudwatch_metric_unit_value,
is_metrics_disabled,
resolve_cold_start_function_name,
validate_emf_timestamp,
)
from aws_lambda_powertools.metrics.provider.base import BaseProvider
Expand Down Expand Up @@ -51,6 +52,10 @@ class AmazonCloudWatchEMFProvider(BaseProvider):
metric namespace to be set for all metrics
POWERTOOLS_SERVICE_NAME : str
service name used for default dimension
POWERTOOLS_METRICS_FUNCTION_NAME: str
function name used as dimension for the ColdStart metric
POWERTOOLS_METRICS_DISABLED: bool
disables all metrics emitted by Powertools

Raises
------
Expand All @@ -72,12 +77,14 @@ def __init__(
metadata_set: dict[str, Any] | None = None,
service: str | None = None,
default_dimensions: dict[str, Any] | None = None,
function_name: str | None = None,
):
self.metric_set = metric_set if metric_set is not None else {}
self.dimension_set = dimension_set if dimension_set is not None else {}
self.default_dimensions = default_dimensions or {}
self.namespace = resolve_env_var_choice(choice=namespace, env=os.getenv(constants.METRICS_NAMESPACE_ENV))
self.service = resolve_env_var_choice(choice=service, env=os.getenv(constants.SERVICE_NAME_ENV))
self.function_name = function_name

self.metadata_set = metadata_set if metadata_set is not None else {}
self.timestamp: int | None = None
Expand Down Expand Up @@ -445,9 +452,11 @@ def add_cold_start_metric(self, context: LambdaContext) -> None:
context : Any
Lambda context
"""

cold_start_function_name = resolve_cold_start_function_name(function_name=self.function_name, context=context)
logger.debug("Adding cold start metric and function_name dimension")
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, namespace=self.namespace) as metric:
metric.add_dimension(name="function_name", value=context.function_name)
metric.add_dimension(name="function_name", value=cold_start_function_name)
if self.service:
metric.add_dimension(name="service", value=str(self.service))

Expand Down
9 changes: 7 additions & 2 deletions aws_lambda_powertools/metrics/provider/datadog/datadog.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import TYPE_CHECKING, Any

from aws_lambda_powertools.metrics.exceptions import MetricValueError, SchemaValidationError
from aws_lambda_powertools.metrics.functions import is_metrics_disabled
from aws_lambda_powertools.metrics.functions import is_metrics_disabled, resolve_cold_start_function_name
from aws_lambda_powertools.metrics.provider import BaseProvider
from aws_lambda_powertools.metrics.provider.datadog.warnings import DatadogDataValidationWarning
from aws_lambda_powertools.shared import constants
Expand Down Expand Up @@ -58,8 +58,10 @@ def __init__(
namespace: str | None = None,
flush_to_log: bool | None = None,
default_tags: dict[str, Any] | None = None,
function_name: str | None = None,
):
self.metric_set = metric_set if metric_set is not None else []
self.function_name = function_name
self.namespace = (
resolve_env_var_choice(choice=namespace, env=os.getenv(constants.METRICS_NAMESPACE_ENV))
or DEFAULT_NAMESPACE
Expand Down Expand Up @@ -224,8 +226,11 @@ def add_cold_start_metric(self, context: LambdaContext) -> None:
context : Any
Lambda context
"""

cold_start_function_name = resolve_cold_start_function_name(function_name=self.function_name, context=context)

logger.debug("Adding cold start metric and function_name tagging")
self.add_metric(name="ColdStart", value=1, function_name=context.function_name)
self.add_metric(name="ColdStart", value=1, function_name=cold_start_function_name)

def log_metrics(
self,
Expand Down
1 change: 1 addition & 0 deletions aws_lambda_powertools/shared/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
DATADOG_FLUSH_TO_LOG: str = "DD_FLUSH_TO_LOG"
SERVICE_NAME_ENV: str = "POWERTOOLS_SERVICE_NAME"
METRICS_DISABLED_ENV: str = "POWERTOOLS_METRICS_DISABLED"
METRICS_FUNCTION_NAME_ENV: str = "POWERTOOLS_METRICS_FUNCTION_NAME"
# If the timestamp of log event is more than 2 hours in future, the log event is skipped.
# If the timestamp of log event is more than 14 days in past, the log event is skipped.
# See https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html
Expand Down
30 changes: 24 additions & 6 deletions docs/core/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,20 @@ If you're new to Amazon CloudWatch, there are five terminologies you must be awa
???+ tip
All examples shared in this documentation are available within the [project repository](https://github.com/aws-powertools/powertools-lambda-python/tree/develop/examples){target="_blank"}.

Metric has three global settings that will be used across all metrics emitted:
Metric has two global settings that will be used across all metrics emitted:

| Setting | Description | Environment variable | Constructor parameter |
| ------------------------------- | ------------------------------------------------------------------------------- | ------------------------------ | --------------------- |
| **Metric namespace** | Logical container where all metrics will be placed e.g. `ServerlessAirline` | `POWERTOOLS_METRICS_NAMESPACE` | `namespace` |
| **Service** | Optionally, sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `service` |
| **Disable Powertools Metrics** | Optionally, disables all Powertools metrics. | `POWERTOOLS_METRICS_DISABLED` | N/A |

???+ info
`POWERTOOLS_METRICS_DISABLED` will not disable default metrics created by AWS services.

???+ tip
Use your application or main service as the metric namespace to easily group all metrics.

```yaml hl_lines="13" title="AWS Serverless Application Model (SAM) example"
```yaml hl_lines="12-14" title="AWS Serverless Application Model (SAM) example"
--8<-- "examples/metrics/sam/template.yaml"
```

Expand Down Expand Up @@ -214,13 +213,32 @@ This has the advantage of keeping cold start metric separate from your applicati
???+ info
We do not emit 0 as a value for ColdStart metric for cost reasons. [Let us know](https://github.com/aws-powertools/powertools-lambda-python/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=){target="_blank"} if you'd prefer a flag to override it.

#### Customizing function name for cold start metrics

When emitting cold start metrics, the `function_name` dimension defaults to `context.function_name`. If you want to change the value you can set the `function_name` parameter in the metrics constructor, or define the environment variable `POWERTOOLS_METRICS_FUNCTION_NAME`.

The priority of the `function_name` dimension value is defined as:

1. `function_name` constructor option
2. `POWERTOOLS_METRICS_FUNCTION_NAME` environment variable
3. `context.function_name` property

=== "working_with_custom_cold_start_function_name.py"

```python hl_lines="4"
--8<-- "examples/metrics/src/working_with_custom_cold_start_function_name.py"
```

### Environment variables

The following environment variable is available to configure Metrics at a global scope:

| Setting | Description | Environment variable | Default |
| ------------------ | -------------------------------- | ------------------------------ | ------- |
| **Namespace Name** | Sets namespace used for metrics. | `POWERTOOLS_METRICS_NAMESPACE` | `None` |
| Setting | Description | Environment variable | Default |
| ------------------ | ------------------------------------------------------------ | ---------------------------------- | ------- |
| **Namespace Name** | Sets **namespace** used for metrics. | `POWERTOOLS_METRICS_NAMESPACE` | `None` |
| **Service** | Sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `None` |
| **Function Name** | Function name used as dimension for the **ColdStart** metric. | `POWERTOOLS_METRICS_FUNCTION_NAME` | `None` |
| **Disable Powertools Metrics** | **Disables** all metrics emitted by Powertools. | `POWERTOOLS_METRICS_DISABLED` | `None` |

`POWERTOOLS_METRICS_NAMESPACE` is also available on a per-instance basis with the `namespace` parameter, which will consequently override the environment variable value.

Expand Down
28 changes: 15 additions & 13 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,19 +405,21 @@ Core utilities such as Tracing, Logging, Metrics, and Event Handler will be avai

| Environment variable | Description | Utility | Default |
| ----------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | --------------------- |
| __POWERTOOLS_SERVICE_NAME__ | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `"service_undefined"` |
| __POWERTOOLS_METRICS_NAMESPACE__ | Sets namespace used for metrics | [Metrics](./core/metrics.md){target="_blank"} | `None` |
| __POWERTOOLS_TRACE_DISABLED__ | Explicitly disables tracing | [Tracing](./core/tracer.md){target="_blank"} | `false` |
| __POWERTOOLS_TRACER_CAPTURE_RESPONSE__ | Captures Lambda or method return as metadata. | [Tracing](./core/tracer.md){target="_blank"} | `true` |
| __POWERTOOLS_TRACER_CAPTURE_ERROR__ | Captures Lambda or method exception as metadata. | [Tracing](./core/tracer.md){target="_blank"} | `true` |
| __POWERTOOLS_TRACE_MIDDLEWARES__ | Creates sub-segment for each custom middleware | [Middleware factory](./utilities/middleware_factory.md){target="_blank"} | `false` |
| __POWERTOOLS_LOGGER_LOG_EVENT__ | Logs incoming event | [Logging](./core/logger.md){target="_blank"} | `false` |
| __POWERTOOLS_LOGGER_SAMPLE_RATE__ | Debug log sampling | [Logging](./core/logger.md){target="_blank"} | `0` |
| __POWERTOOLS_LOG_DEDUPLICATION_DISABLED__ | Disables log deduplication filter protection to use Pytest Live Log feature | [Logging](./core/logger.md){target="_blank"} | `false` |
| __POWERTOOLS_PARAMETERS_MAX_AGE__ | Adjust how long values are kept in cache (in seconds) | [Parameters](./utilities/parameters.md#adjusting-cache-ttl){target="_blank"} | `5` |
| __POWERTOOLS_PARAMETERS_SSM_DECRYPT__ | Sets whether to decrypt or not values retrieved from AWS SSM Parameters Store | [Parameters](./utilities/parameters.md#ssmprovider){target="_blank"} | `false` |
| __POWERTOOLS_DEV__ | Increases verbosity across utilities | Multiple; see [POWERTOOLS_DEV effect below](#optimizing-for-non-production-environments) | `false` |
| __POWERTOOLS_LOG_LEVEL__ | Sets logging level | [Logging](./core/logger.md){target="_blank"} | `INFO` |
| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `"service_undefined"` |
| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics.md){target="_blank"} | `None` |
| **POWERTOOLS_METRICS_FUNCTION_NAME** | Function name used as dimension for the **ColdStart** metric metrics | [Metrics](./core/metrics.md){target="_blank"} | `None` |
| **POWERTOOLS_METRICS_DISABLED** | **Disables** all metrics emitted by Powertools metrics | [Metrics](./core/metrics.md){target="_blank"} | `None` |
| **POWERTOOLS_TRACE_DISABLED** | Explicitly disables tracing | [Tracing](./core/tracer.md){target="_blank"} | `false` |
| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracing](./core/tracer.md){target="_blank"} | `true` |
| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracing](./core/tracer.md){target="_blank"} | `true` |
| **POWERTOOLS_TRACE_MIDDLEWARES** | Creates sub-segment for each custom middleware | [Middleware factory](./utilities/middleware_factory.md){target="_blank"} | `false` |
| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logging](./core/logger.md){target="_blank"} | `false` |
| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logger.md){target="_blank"} | `0` |
| **POWERTOOLS_LOG_DEDUPLICATION_DISABLED** | Disables log deduplication filter protection to use Pytest Live Log feature | [Logging](./core/logger.md){target="_blank"} | `false` |
| **POWERTOOLS_PARAMETERS_MAX_AGE** | Adjust how long values are kept in cache (in seconds) | [Parameters](./utilities/parameters.md#adjusting-cache-ttl){target="_blank"} | `5` |
| **POWERTOOLS_PARAMETERS_SSM_DECRYPT** | Sets whether to decrypt or not values retrieved from AWS SSM Parameters Store | [Parameters](./utilities/parameters.md#ssmprovider){target="_blank"} | `false` |
| **POWERTOOLS_DEV** | Increases verbosity across utilities | Multiple; see [POWERTOOLS_DEV effect below](#optimizing-for-non-production-environments) | `false` |
| **POWERTOOLS_LOG_LEVEL** | Sets logging level | [Logging](./core/logger.md){target="_blank"} | `INFO` |

### Optimizing for non-production environments

Expand Down
1 change: 1 addition & 0 deletions examples/metrics/sam/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Globals:
Variables:
POWERTOOLS_SERVICE_NAME: booking
POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline
POWERTOOLS_METRICS_FUNCTION_NAME: my-function-name

Layers:
# Find the latest Layer version in the official documentation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from aws_lambda_powertools import Metrics
from aws_lambda_powertools.utilities.typing import LambdaContext

metrics = Metrics(function_name="my-function-name")


@metrics.log_metrics(capture_cold_start_metric=True)
def lambda_handler(event: dict, context: LambdaContext): ...
Loading
Loading