Skip to content

Commit 32459e2

Browse files
author
Tom McCarthy
authored
improv: better namespace/dimension handling for Metrics (#62)
* improv: namespace/dimension changes Use service name for default dimension instead of namespace Add namespace parameter * docs: document changes to namespace/service * chore: fix typo in docstring * chore: fix typo in docs * chore: fix test comments * chore: Add env var for namespace back into example app * chore: correct typo in docs * chore: update example in README
1 parent 1f074de commit 32459e2

File tree

8 files changed

+151
-61
lines changed

8 files changed

+151
-61
lines changed

Diff for: aws_lambda_powertools/metrics/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class MetricManager:
3535
3636
Environment variables
3737
---------------------
38-
POWERTOOLS_SERVICE_NAME : str
38+
POWERTOOLS_METRICS_NAMESPACE : str
3939
metric namespace to be set for all metrics
4040
4141
Raises
@@ -53,7 +53,7 @@ class MetricManager:
5353
def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):
5454
self.metric_set = metric_set if metric_set is not None else {}
5555
self.dimension_set = dimension_set if dimension_set is not None else {}
56-
self.namespace = namespace or os.getenv("POWERTOOLS_SERVICE_NAME")
56+
self.namespace = namespace or os.getenv("POWERTOOLS_METRICS_NAMESPACE")
5757
self._metric_units = [unit.value for unit in MetricUnit]
5858
self._metric_unit_options = list(MetricUnit.__members__)
5959

Diff for: aws_lambda_powertools/metrics/metric.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class SingleMetric(MetricManager):
2121
2222
Environment variables
2323
---------------------
24-
POWERTOOLS_SERVICE_NAME : str
24+
POWERTOOLS_METRICS_NAMESPACE : str
2525
metric namespace
2626
2727
Example
@@ -30,7 +30,7 @@ class SingleMetric(MetricManager):
3030
3131
from aws_lambda_powertools.metrics import SingleMetric, MetricUnit
3232
import json
33-
metric = Single_Metric(service="ServerlessAirline")
33+
metric = Single_Metric(namespace="ServerlessAirline")
3434
3535
metric.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
3636
metric.add_dimension(name="function_version", value=47)
@@ -62,7 +62,7 @@ def add_metric(self, name: str, unit: MetricUnit, value: float):
6262

6363

6464
@contextmanager
65-
def single_metric(name: str, unit: MetricUnit, value: float, service: str = None):
65+
def single_metric(name: str, unit: MetricUnit, value: float, namespace: str = None):
6666
"""Context manager to simplify creation of a single metric
6767
6868
Example
@@ -71,12 +71,12 @@ def single_metric(name: str, unit: MetricUnit, value: float, service: str = None
7171
7272
from aws_lambda_powertools.metrics import single_metric, MetricUnit
7373
74-
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, service="ServerlessAirline") as metric:
74+
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, namespace="ServerlessAirline") as metric:
7575
metric.add_dimension(name="function_version", value=47)
7676
7777
**Same as above but set namespace using environment variable**
7878
79-
$ export POWERTOOLS_SERVICE_NAME="ServerlessAirline"
79+
$ export POWERTOOLS_METRICS_NAMESPACE="ServerlessAirline"
8080
8181
from aws_lambda_powertools.metrics import single_metric, MetricUnit
8282
@@ -91,8 +91,8 @@ def single_metric(name: str, unit: MetricUnit, value: float, service: str = None
9191
`aws_lambda_powertools.helper.models.MetricUnit`
9292
value : float
9393
Metric value
94-
service: str
95-
Service name used as namespace
94+
namespace: str
95+
Namespace for metrics
9696
9797
Yields
9898
-------
@@ -106,7 +106,7 @@ def single_metric(name: str, unit: MetricUnit, value: float, service: str = None
106106
"""
107107
metric_set = None
108108
try:
109-
metric: SingleMetric = SingleMetric(namespace=service)
109+
metric: SingleMetric = SingleMetric(namespace=namespace)
110110
metric.add_metric(name=name, unit=unit, value=value)
111111
yield metric
112112
logger.debug("Serializing single metric")

Diff for: aws_lambda_powertools/metrics/metrics.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import functools
22
import json
33
import logging
4+
import os
45
from typing import Any, Callable
56

67
from aws_lambda_powertools.metrics.base import MetricManager
@@ -29,10 +30,9 @@ class Metrics(MetricManager):
2930
3031
from aws_lambda_powertools.metrics import Metrics
3132
32-
metrics = Metrics(service="ServerlessAirline")
33+
metrics = Metrics(namespace="ServerlessAirline", service="payment")
3334
metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
3435
metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
35-
metrics.add_dimension(name="service", value="booking")
3636
metrics.add_dimension(name="function_version", value="$LATEST")
3737
...
3838
@@ -47,8 +47,10 @@ def do_something():
4747
4848
Environment variables
4949
---------------------
50-
POWERTOOLS_SERVICE_NAME : str
50+
POWERTOOLS_METRICS_NAMESPACE : str
5151
metric namespace
52+
POWERTOOLS_SERVICE_NAME : str
53+
service name used for default dimension
5254
5355
Parameters
5456
----------
@@ -64,13 +66,14 @@ def do_something():
6466
_metrics = {}
6567
_dimensions = {}
6668

67-
def __init__(
68-
self, service: str = None,
69-
):
69+
def __init__(self, service: str = None, namespace: str = None):
7070
self.metric_set = self._metrics
7171
self.dimension_set = self._dimensions
72-
self.service = service
73-
super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set, namespace=self.service)
72+
self.service = service or os.environ.get("POWERTOOLS_SERVICE_NAME")
73+
self.namespace = namespace
74+
if self.service:
75+
self.dimension_set["service"] = self.service
76+
super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set, namespace=self.namespace)
7477

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

Diff for: docs/content/core/metrics.mdx

+19-18
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Metrics creates custom metrics asynchronously via logging metrics to standard ou
1616

1717
## Initialization
1818

19-
Set `POWERTOOLS_SERVICE_NAME` env var as a start - Here is an example using AWS Serverless Application Model (SAM)
19+
Set `POWERTOOLS_SERVICE_NAME` and `POWERTOOLS_METRICS_NAMESPACE` env vars as a start - Here is an example using AWS Serverless Application Model (SAM)
2020

2121
```yaml:title=template.yaml
2222
Resources:
@@ -27,37 +27,39 @@ Resources:
2727
Runtime: python3.8
2828
Environment:
2929
Variables:
30-
POWERTOOLS_SERVICE_NAME: ServerlessAirline # highlight-line
30+
POWERTOOLS_SERVICE_NAME: payment # highlight-line
31+
POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline # highlight-line
3132
```
3233

3334
We recommend you use your application or main service as a metric namespace.
34-
You can explicitly set a namespace name via `service` param or via `POWERTOOLS_SERVICE_NAME` env var. This sets **namespace** key that will be used for all metrics.
35+
You can explicitly set a namespace name via `namespace` param or via `POWERTOOLS_METRICS_NAMESPACE` env var. This sets **namespace** key that will be used for all metrics.
36+
You can also pass a service name via `service` param or `POWERTOOLS_SERVICE_NAME` env var. This will create a dimension with the service name.
3537

3638
```python:title=app.py
3739
from aws_lambda_powertools.metrics import Metrics, MetricUnit
3840

39-
# POWERTOOLS_SERVICE_NAME defined
41+
# POWERTOOLS_METRICS_NAMESPACE and POWERTOOLS_SERVICE_NAME defined
4042
metrics = Metrics() # highlight-line
4143

4244
# Explicit definition
43-
Metrics(service="ServerlessAirline") # sets namespace to "ServerlessAirline"
45+
Metrics(namespace="ServerlessAirline", service="orders") # creates a default dimension {"service": "orders"} under the namespace "ServerlessAirline"
4446

4547

4648
```
4749

48-
You can initialize Metrics anywhere in your code as many time as you need - It'll keep track of your aggregate metrics in memory.
50+
You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory.
4951

5052
## Creating metrics
5153

52-
You can create metrics using `add_metric`, and set dimensions for all your aggregate metrics using `add_dimension`.
54+
You can create metrics using `add_metric`, and manually create dimensions for all your aggregate metrics using `add_dimension`.
5355

5456
```python:title=app.py
5557
from aws_lambda_powertools.metrics import Metrics, MetricUnit
5658

57-
metrics = Metrics(service="ExampleService")
59+
metrics = Metrics(namespace="ExampleApplication", service="booking")
5860
# highlight-start
59-
metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
60-
metrics.add_dimension(name="service", value="booking")
61+
metrics.add_dimension(name="environment", value="prod")
62+
metrics.add_metric(name="SuccessfulBooking", unit=MetricUnit.Count, value=1)
6163
# highlight-end
6264
```
6365

@@ -79,7 +81,7 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `single_met
7981
```python:title=single_metric.py
8082
from aws_lambda_powertools.metrics import MetricUnit, single_metric
8183

82-
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, service="ExampleService") as metric: # highlight-line
84+
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, namespace="ExampleApplication") as metric: # highlight-line
8385
metric.add_dimension(name="function_context", value="$LATEST")
8486
...
8587
```
@@ -115,15 +117,14 @@ def lambda_handler(evt, ctx):
115117
```python:title=lambda_handler_nested_middlewares.py
116118
from aws_lambda_powertools.metrics import Metrics, MetricUnit
117119

118-
metrics = Metrics(service="ExampleService")
120+
metrics = Metrics(namespace="ExampleApplication", service="booking")
119121
metrics.add_metric(name="ColdStart", unit="Count", value=1)
120122

121123
# highlight-start
122124
@metrics.log_metrics
123125
@tracer.capture_lambda_handler
124126
# highlight-end
125127
def lambda_handler(evt, ctx):
126-
metrics.add_dimension(name="service", value="booking")
127128
metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
128129
...
129130
```
@@ -136,9 +137,8 @@ If you prefer not to use `log_metrics` because you might want to encapsulate add
136137
import json
137138
from aws_lambda_powertools.metrics import Metrics, MetricUnit
138139

139-
metrics = Metrics(service="ExampleService")
140+
metrics = Metrics(namespace="ExampleApplication", service="booking")
140141
metrics.add_metric(name="ColdStart", unit="Count", value=1)
141-
metrics.add_dimension(name="service", value="booking")
142142

143143
# highlight-start
144144
your_metrics_object = metrics.serialize_metric_set()
@@ -149,10 +149,11 @@ print(json.dumps(your_metrics_object))
149149

150150
## Testing your code
151151

152-
Use `POWERTOOLS_SERVICE_NAME` env var when unit testing your code to ensure a metric namespace object is created, and your code doesn't fail validation.
152+
Use `POWERTOOLS_METRICS_NAMESPACE` and `POWERTOOLS_SERVICE_NAME` env vars when unit testing your code to ensure metric namespace and dimension objects are created, and your code doesn't fail validation.
153153

154154
```bash:title=pytest_metric_namespace.sh
155-
POWERTOOLS_SERVICE_NAME="Example" python -m pytest
155+
156+
POWERTOOLS_SERVICE_NAME="Example" POWERTOOLS_METRICS_NAMESPACE="Application" python -m pytest
156157
```
157158

158-
You can ignore this if you are explicitly setting namespace by passing a service name when initializing Metrics: `metrics = Metrics(service=ServiceName)`.
159+
You can ignore this if you are explicitly setting namespace/default dimension by passing the `namespace` and `service` parameters when initializing Metrics: `metrics = Metrics(namespace=ApplicationName, service=ServiceName)`.

Diff for: docs/content/index.mdx

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ _`*` Core utilities are Tracer, Logger and Metrics. Optional utilities may vary
3636

3737
Environment variable | Description | Utility
3838
------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------
39-
**POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics namespace and structured logging | all
39+
**POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | all
40+
**POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics)
4041
**POWERTOOLS_TRACE_DISABLED** | Disables tracing | [Tracing](./core/tracer)
4142
**POWERTOOLS_TRACE_MIDDLEWARES** | Creates sub-segment for each custom middleware | [middleware_factory](./utilities/middleware_factory)
4243
**POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logging](./core/logger)

Diff for: example/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ This example uses both [tracing](https://github.com/awslabs/aws-lambda-powertool
99
* **Deploy**: `sam deploy --guided`
1010
* **Unit Tests**: We recommend proceeding with the following commands in a virtual environment
1111
- **Install deps**: `pip install -r hello_world/requirements.txt && pip install -r requirements-dev.txt`
12-
- **Run tests with tracing disabled and namespace set**
13-
- `POWERTOOLS_SERVICE_NAME="Example" POWERTOOLS_TRACE_DISABLED=1 python -m pytest`
14-
- Both are necessary because `app.py` initializes them in the global scope, since both Tracer and Metrics will be initialized and configured during import time. For unit tests, we could always patch and explicitly config but env vars do just fine for this example.
12+
- **Run tests with namespace and service set, and tracing disabled**
13+
- `POWERTOOLS_METRICS_NAMESPACE="Example" POWERTOOLS_SERVICE_NAME="Example" POWERTOOLS_TRACE_DISABLED=1 python -m pytest`
14+
- These are necessary because `app.py` initializes them in the global scope, since both Tracer and Metrics will be initialized and configured during import time. For unit tests, we could always patch and explicitly config but env vars do just fine for this example.
1515

1616
# Example code
1717

Diff for: example/template.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Resources:
2424
POWERTOOLS_TRACE_DISABLED: "false" # Explicitly disables tracing, default
2525
POWERTOOLS_LOGGER_LOG_EVENT: "false" # Logs incoming event, default
2626
POWERTOOLS_LOGGER_SAMPLE_RATE: "0" # Debug log sampling percentage, default
27+
POWERTOOLS_METRICS_NAMESPACE: "Example" # Metric Namespace
2728
LOG_LEVEL: INFO # Log level for Logger (INFO, DEBUG, etc.), default
2829
Events:
2930
HelloWorld:

0 commit comments

Comments
 (0)