Skip to content

Commit a9fc2bd

Browse files
authored
Feature - EMF Metrics middleware (#15)
* feat: add metrics middleware * fix(Makefile): increase verbosity for test results * chore: linting * chore: add deprecation warning log_metric * docs: add initial docs for metrics * chore: version bump * fix: replace jsonschema, automate wheels for Linux * feat: add docs generation * feat(Makefile): add docs target * docs: update to reflect metrics middleware, beta * docs: update history * improv(metrics): address feedbacks from Jacob * fix(metrics): correct decorator logic * chore: version bump * fix(Makefile): fix build linux wheels target * fix(metrics): lambda handler return and exceptions * chore: version bump * improv: update example to use new metrics feat Co-authored-by: heitorlessa <[email protected]>
1 parent 094aaee commit a9fc2bd

21 files changed

+1247
-222
lines changed

Diff for: python/.flake8

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[flake8]
22
exclude = docs, .eggs, setup.py, example, .aws-sam
3-
ignore = E203, E266, W503, BLK100, W291
3+
ignore = E203, E266, W503, BLK100, W291, I004
44
max-line-length = 120
55
max-complexity = 18
66

Diff for: python/.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,6 @@ $RECYCLE.BIN/
293293

294294
# Misc
295295

296-
test_report
296+
test_report
297+
wheelhouse
298+
docs

Diff for: python/HISTORY.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# HISTORY
22

3+
## April 9th, 2020
4+
5+
**0.6.3**
6+
7+
* Fix `log_metrics` decorator logic not calling the decorated function, and exception handling
8+
9+
## April 8th, 2020
10+
11+
**0.6.1**
12+
13+
* Introduces Metrics middleware to utilise CloudWatch Embedded Metric Format
14+
* Adds deprecation warning for `log_metrics`
15+
316
## February 20th, 2020
417

518
**0.5.0**

Diff for: python/Makefile

+20-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ lint: format
1515
poetry run flake8
1616

1717
test:
18-
poetry run pytest
18+
poetry run pytest -vvv
1919

2020
test-html:
2121
poetry run pytest --cov-report html
@@ -25,18 +25,30 @@ pr: lint test
2525
build: pr
2626
poetry run build
2727

28+
docs: dev
29+
poetry run pdoc --html --output-dir docs ./aws_lambda_powertools --force
30+
31+
docs-dev:
32+
poetry run pdoc --http : aws_lambda_powertools
33+
2834
#
2935
# Use `poetry version <major>/<minor></patch>` for version bump
3036
#
31-
release:
37+
release-prod:
3238
poetry config pypi-token.pypi ${PYPI_TOKEN}
33-
@$(MAKE) build
34-
poetry publish
35-
rm -rf dist
39+
poetry publish -n
3640

3741
release-test:
3842
poetry config repositories.testpypi https://test.pypi.org/legacy
3943
poetry config pypi-token.pypi ${PYPI_TEST_TOKEN}
40-
@$(MAKE) build
41-
poetry publish --repository testpypi
42-
rm -rf dist
44+
poetry publish --repository testpypi -n
45+
46+
build-linux-wheels:
47+
poetry build
48+
docker run --env PLAT=manylinux1_x86_64 --rm -it -v ${PWD}:/io -w /io quay.io/pypa/manylinux1_x86_64 /io/build_linux_wheels.sh
49+
cp ./wheelhouse/* dist/ && rm -rf wheelhouse
50+
51+
release:
52+
$(MAKE) build-linux-wheels
53+
$(MAKE) release-test
54+
$(MAKE) release-prod

Diff for: python/README.md

+48-21
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray,
2525
* Log sampling enables DEBUG log level for a percentage of requests (disabled by default)
2626
- Enable via `POWERTOOLS_LOGGER_SAMPLE_RATE=0.1`, ranges from 0 to 1, where 0.1 is 10% and 1 is 100%
2727

28+
**Metrics**
29+
30+
* Aggregate up to 100 metrics using a single CloudWatch Embedded Metric Format object (large JSON blob)
31+
* Context manager to create an one off metric with a different dimension than metrics already aggregated
32+
* Validate against common metric definitions mistakes (metric unit, values, max dimensions, max metrics, etc)
33+
* No stack, custom resource, data collection needed — Metrics are created async by CloudWatch EMF
34+
2835
**Environment variables** used across suite of utilities
2936

3037
Environment variable | Description | Default | Utility
@@ -33,6 +40,7 @@ POWERTOOLS_SERVICE_NAME | Sets service name used for tracing namespace, metrics
3340
POWERTOOLS_TRACE_DISABLED | Disables tracing | "false" | tracing
3441
POWERTOOLS_LOGGER_LOG_EVENT | Logs incoming event | "false" | logging
3542
POWERTOOLS_LOGGER_SAMPLE_RATE | Debug log sampling | 0 | logging
43+
POWERTOOLS_METRICS_NAMESPACE | Metrics namespace | None | metrics
3644
LOG_LEVEL | Sets logging level | "INFO" | logging
3745

3846
## Usage
@@ -148,41 +156,60 @@ def handler(event, context)
148156

149157
#### Custom Metrics async
150158

151-
> **NOTE**: This will **likely change after Beta** in light of [new Amazon CloudWatch embedded metric format](https://aws.amazon.com/about-aws/whats-new/2019/11/amazon-cloudwatch-launches-embedded-metric-format/), meaning we won't need an additional stack and interface could change.
159+
> **NOTE** `log_metric` will be removed once it's GA.
160+
161+
This feature makes use of CloudWatch Embedded Metric Format (EMF) and metrics are created asynchronously by CloudWatch service
162+
163+
> Contrary to `log_metric`, you don't need any custom resource or additional CloudFormation stack anymore.
164+
165+
Metrics middleware validates against the minimum necessary for a metric to be published:
152166

153-
This feature requires [Custom Metrics SAR App](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:374852340823:applications~async-custom-metrics) in order to process canonical metric lines in CloudWatch Logs.
167+
* At least of one Metric and Dimension
168+
* Maximum of 9 dimensions
169+
* Only one Namespace
170+
* [Any Metric unit supported by CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html)
154171

155-
If you're starting from scratch, you may want to see a working example, tune to your needs and deploy within your account - [Serverless Airline Log Processing Stack](https://github.com/aws-samples/aws-serverless-airline-booking/blob/develop/src/backend/log-processing/template.yaml)
172+
**Creating multiple metrics**
173+
174+
`log_metrics` decorator calls the decorated function, so leave that for last decorator or will fail with `SchemaValidationError` if no metrics are recorded.
156175

157176
```python
158-
from aws_lambda_powertools.logging import MetricUnit, log_metric
177+
from aws_lambda_powertools.metrics import Metrics, MetricUnit
159178

160-
def handler(event, context)
161-
log_metric(name="SuccessfulPayment", unit=MetricUnit.Count, value=10, namespace="MyApplication")
162-
163-
# Optional dimensions
164-
log_metric(name="SuccessfulPayment", unit=MetricUnit.Count, value=10, namespace="MyApplication", customer_id="123-abc", charge_id="abc-123")
165-
166-
# Explicit service name
167-
log_metric(service="paymentTest", name="SuccessfulPayment", namespace="MyApplication".....)
168-
...
179+
metrics = Metrics()
180+
metrics.add_namespace(name="ServerlessAirline")
181+
metrics.add_metric(name="ColdStart", unit="Count", value=1)
182+
metrics.add_dimension(name="service", value="booking")
183+
184+
@metrics.log_metrics
185+
@tracer.capture_lambda_handler
186+
def lambda_handler(evt, ctx):
187+
metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
188+
some_code()
189+
return True
190+
191+
def some_code():
192+
metrics.add_metric(name="some_other_metric", unit=MetricUnit.Seconds, value=1)
193+
...
169194
```
170195

171-
**Exerpt output in CloudWatch Logs**
196+
CloudWatch EMF uses the same dimensions across all metrics. If you have metrics that should have different dimensions, use `single_metric` to create a single metric with any dimension you want. Generally, this would be an edge case since you [pay for unique metric](https://aws.amazon.com/cloudwatch/pricing/)
172197

173-
```
174-
MONITORING|10|Count|SuccessfulPayment|MyApplication|service="payment
175-
MONITORING|10|Count|SuccessfulPayment|MyApplication|customer_id="123-abc",charge_id="abc-123",service="payment
176-
MONITORING|10|Count|SuccessfulPayment|MyApplication|service="paymentTest
177-
```
198+
> unique metric = (metric_name + dimension_name + dimension_value)
178199
200+
```python
201+
from aws_lambda_powertools.metrics import MetricUnit, single_metric
202+
203+
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1) as metric:
204+
metric.add_dimension(name="function_context", value="$LATEST")
205+
```
179206

180207
## Beta
181208

209+
> **[Progress towards GA](https://github.com/awslabs/aws-lambda-powertools/projects/1)**
210+
182211
This library may change its API/methods or environment variables as it receives feedback from customers. Currently looking for ideas in the following areas before making it stable:
183212

184213
* **Should Tracer patch all possible imported libraries by default or only AWS SDKs?**
185214
- Patching all libraries may have a small performance penalty (~50ms) at cold start
186215
- Alternatively, we could patch only AWS SDK if available and to provide a param to patch multiple `Tracer(modules=("boto3", "requests"))`
187-
* **Create a Tracer provider to support additional tracing**
188-
- Either duck typing or ABC to allow additional tracing providers

Diff for: python/aws_lambda_powertools/logging/logger.py

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import os
55
import random
6+
import warnings
67
from distutils.util import strtobool
78
from typing import Any, Callable, Dict
89

@@ -237,6 +238,7 @@ def log_metric(
237238
keyword arguments as additional dimensions (e.g. customer=customerId)
238239
"""
239240

241+
warnings.warn(message="This method will be removed in GA; use Metrics instead", category=DeprecationWarning)
240242
logger.debug(f"Building new custom metric. Name: {name}, Unit: {unit}, Value: {value}, Dimensions: {dimensions}")
241243
service = os.getenv("POWERTOOLS_SERVICE_NAME") or service
242244
dimensions = __build_dimensions(**dimensions)

Diff for: python/aws_lambda_powertools/metrics/__init__.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""CloudWatch Embedded Metric Format utility
2+
"""
3+
from aws_lambda_powertools.helper.models import MetricUnit
4+
5+
from .exceptions import MetricUnitError, MetricValueError, SchemaValidationError, UniqueNamespaceError
6+
from .metric import single_metric
7+
from .metrics import Metrics
8+
9+
__all__ = [
10+
"Metrics",
11+
"single_metric",
12+
"MetricUnit",
13+
"MetricUnitError",
14+
"SchemaValidationError",
15+
"MetricValueError",
16+
"UniqueNamespaceError",
17+
]

0 commit comments

Comments
 (0)