Skip to content

Commit 11463fe

Browse files
committed
Merge branch 'develop' into feat/precommit-hooks
* develop: improv: include example tests in `make tests` (#63) chore: rename Makefile target docs-dev to docs-local (#65) improv: better namespace/dimension handling for Metrics (#62)
2 parents dee4fab + 993e8e5 commit 11463fe

File tree

14 files changed

+187
-79
lines changed

14 files changed

+187
-79
lines changed

Diff for: Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ build-docs-website: dev-docs
4242
cd docs && npm run build
4343
cp -R docs/public/* dist/
4444

45-
docs-dev:
45+
docs-local:
4646
cd docs && npm run start
4747

48-
docs-api-dev:
48+
docs-api-local:
4949
poetry run pdoc --http : aws_lambda_powertools
5050

5151
security-baseline:

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

+20-19
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
40-
metrics = Metrics() # highlight-line
41+
# POWERTOOLS_METRICS_NAMESPACE and POWERTOOLS_SERVICE_NAME defined
42+
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

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Summary
22

3-
This example uses both [tracing](https://github.com/awslabs/aws-lambda-powertools/tree/develop/python#tracing) and [logging](https://github.com/awslabs/aws-lambda-powertools/tree/develop/python#logging) features, includes all environment variables that can be used, and demonstrates how to explicitly disable tracing while running unit tests - That is not necessary when running within SAM CLI as it detects the local env automatically.
3+
This example uses [tracer](https://awslabs.github.io/aws-lambda-powertools-python/core/tracer/), [metrics](https://awslabs.github.io/aws-lambda-powertools-python/core/metrics/),and [logger](https://awslabs.github.io/aws-lambda-powertools-python/core/logger/) features, includes all environment variables that can be used, and demonstrates how to explicitly disable tracing while running unit tests - That is not necessary when running within SAM CLI as it detects the local env automatically.
44

55
**Quick commands**
66

@@ -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

@@ -118,7 +118,7 @@ Tests are defined in the `tests` folder in this project. Use PIP to install the
118118
```bash
119119
example$ pip install -r hello_world/requirements.txt
120120
example$ pip install -r requirements-dev.txt
121-
example$ POWERTOOLS_TRACE_DISABLED=1 python -m pytest tests/ -v
121+
example$ pytest -v
122122
```
123123

124124
## Cleanup

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:

Diff for: example/tests/__init__.py

Whitespace-only changes.

Diff for: example/tests/test_handler.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
import json
2+
import os
3+
import sys
24
from dataclasses import dataclass
35

46
import pytest
57

6-
from hello_world import app
8+
9+
@pytest.fixture()
10+
def env_vars(monkeypatch):
11+
monkeypatch.setenv("POWERTOOLS_METRICS_NAMESPACE", "example_namespace")
12+
monkeypatch.setenv("POWERTOOLS_SERVICE_NAME", "example_service")
13+
monkeypatch.setenv("POWERTOOLS_TRACE_DISABLED", "1")
14+
15+
16+
@pytest.fixture()
17+
def lambda_handler(env_vars):
18+
from hello_world import app
19+
20+
return app.lambda_handler
21+
722

823

924
@pytest.fixture()
@@ -71,8 +86,9 @@ class Context:
7186
aws_request_id: str = "5b441b59-a550-11c8-6564-f1c833cf438c"
7287

7388

74-
def test_lambda_handler(apigw_event, mocker, capsys):
75-
ret = app.lambda_handler(apigw_event, Context())
89+
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
90+
def test_lambda_handler(lambda_handler, apigw_event, mocker, capsys):
91+
ret = lambda_handler(apigw_event, Context())
7692
data = json.loads(ret["body"])
7793

7894
output = capsys.readouterr()

Diff for: poetry.lock

+10-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ radon = "^4.1.0"
4646
xenon = "^0.7.0"
4747
flake8-bugbear = "^20.1.4"
4848
flake8-eradicate = "^0.3.0"
49-
pre-commit = "^2.4.0"
49+
dataclasses = {version = "*", python = "~3.6"}
5050

5151
[tool.coverage.run]
5252
source = ["aws_lambda_powertools"]

Diff for: pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[pytest]
22
addopts = -ra --cov --cov-config=.coveragerc
3-
testpaths = ./tests
3+
testpaths = ./tests ./example/tests

0 commit comments

Comments
 (0)