Skip to content

Commit 755d9bb

Browse files
committed
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-powertools-python into develop
* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: feat(logging): Include exception_name (#320) chore: remove gatsby mention as migrated completed refactor(parameters): Consistently reference env (#319) docs(metrics): remove minimum dimensions docs: Correct code examples (#317) docs(metrics): Correct code examples in markdown (#316) fix(idempotency): TypeError when calling is_missing_idempotency_key with an int (#315) docs(metrics): Corrections to the code examples (#314) fix(idempotency): Correctly handle save_inprogress errors (#313)
2 parents 25facef + 0ec47dc commit 755d9bb

File tree

21 files changed

+144
-53
lines changed

21 files changed

+144
-53
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip insta
3737

3838
* Structured logging initial implementation from [aws-lambda-logging](https://gitlab.com/hadrien/aws_lambda_logging)
3939
* Powertools idea [DAZN Powertools](https://github.com/getndazn/dazn-lambda-powertools/)
40-
* [Gatsby Apollo Theme for Docs](https://github.com/apollographql/gatsby-theme-apollo/tree/master/packages/gatsby-theme-apollo-docs)
4140

4241
## License
4342

aws_lambda_powertools/logging/formatter.py

+19
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,24 @@ def _extract_log_exception(self, log_record: logging.LogRecord) -> Optional[str]
126126

127127
return None
128128

129+
def _extract_log_exception_name(self, log_record: logging.LogRecord) -> Optional[str]:
130+
"""Extract the exception name, if available
131+
132+
Parameters
133+
----------
134+
log_record : logging.LogRecord
135+
Log record to extract exception name from
136+
137+
Returns
138+
-------
139+
log_record: Optional[str]
140+
Log record with exception name
141+
"""
142+
if log_record.exc_info:
143+
return log_record.exc_info[0].__name__
144+
145+
return None
146+
129147
def _extract_log_keys(self, log_record: logging.LogRecord) -> Dict:
130148
"""Extract and parse custom and reserved log keys
131149
@@ -164,6 +182,7 @@ def _extract_log_keys(self, log_record: logging.LogRecord) -> Dict:
164182
def format(self, record): # noqa: A003
165183
formatted_log = self._extract_log_keys(log_record=record)
166184
formatted_log["message"] = self._extract_log_message(log_record=record)
185+
formatted_log["exception_name"] = self._extract_log_exception_name(log_record=record)
167186
formatted_log["exception"] = self._extract_log_exception(log_record=record)
168187
formatted_log.update({"xray_trace_id": self._get_latest_trace_id()}) # fetch latest Trace ID, if any
169188

aws_lambda_powertools/logging/logger.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class Logger(logging.Logger): # lgtm [py/missing-call-to-init]
5353
LOG_LEVEL: str
5454
logging level (e.g. INFO, DEBUG)
5555
POWERTOOLS_LOGGER_SAMPLE_RATE: float
56-
samping rate ranging from 0 to 1, 1 being 100% sampling
56+
sampling rate ranging from 0 to 1, 1 being 100% sampling
5757
5858
Parameters
5959
----------

aws_lambda_powertools/metrics/base.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None, me
197197
metric_names_and_units.append({"Name": metric_name, "Unit": metric_unit})
198198
metric_names_and_values.update({metric_name: metric_value})
199199

200-
embedded_metrics_object = {
200+
return {
201201
"_aws": {
202202
"Timestamp": int(datetime.datetime.now().timestamp() * 1000), # epoch
203203
"CloudWatchMetrics": [
@@ -213,8 +213,6 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None, me
213213
**metric_names_and_values, # "single_metric": 1.0
214214
}
215215

216-
return embedded_metrics_object
217-
218216
def add_dimension(self, name: str, value: str):
219217
"""Adds given dimension to all metrics
220218

aws_lambda_powertools/metrics/metric.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ class SingleMetric(MetricManager):
2727
-------
2828
**Creates cold start metric with function_version as dimension**
2929
30-
from aws_lambda_powertools.metrics import SingleMetric, MetricUnit
3130
import json
32-
metric = Single_Metric(namespace="ServerlessAirline")
31+
from aws_lambda_powertools.metrics import single_metric, MetricUnit
32+
metric = single_metric(namespace="ServerlessAirline")
3333
3434
metric.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
3535
metric.add_dimension(name="function_version", value=47)
@@ -72,7 +72,7 @@ def single_metric(name: str, unit: MetricUnit, value: float, namespace: str = No
7272
from aws_lambda_powertools.metrics import MetricUnit
7373
7474
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1, namespace="ServerlessAirline") as metric:
75-
metric.add_dimension(name="function_version", value=47)
75+
metric.add_dimension(name="function_version", value="47")
7676
7777
**Same as above but set namespace using environment variable**
7878
@@ -82,7 +82,7 @@ def single_metric(name: str, unit: MetricUnit, value: float, namespace: str = No
8282
from aws_lambda_powertools.metrics import MetricUnit
8383
8484
with single_metric(name="ColdStart", unit=MetricUnit.Count, value=1) as metric:
85-
metric.add_dimension(name="function_version", value=47)
85+
metric.add_dimension(name="function_version", value="47")
8686
8787
Parameters
8888
----------

aws_lambda_powertools/metrics/metrics.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,13 @@ class Metrics(MetricManager):
3939
metrics.add_dimension(name="function_version", value="$LATEST")
4040
...
4141
42-
@tracer.capture_lambda_handler
4342
@metrics.log_metrics()
4443
def lambda_handler():
45-
do_something()
46-
return True
44+
do_something()
45+
return True
4746
4847
def do_something():
49-
metrics.add_metric(name="Something", unit="Count", value=1)
48+
metrics.add_metric(name="Something", unit="Count", value=1)
5049
5150
Environment variables
5251
---------------------
@@ -111,12 +110,14 @@ def log_metrics(
111110
-------
112111
**Lambda function using tracer and metrics decorators**
113112
113+
from aws_lambda_powertools import Metrics, Tracer
114+
114115
metrics = Metrics(service="payment")
115116
tracer = Tracer(service="payment")
116117
117118
@tracer.capture_lambda_handler
118119
@metrics.log_metrics
119-
def handler(event, context):
120+
def handler(event, context):
120121
...
121122
122123
Parameters
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
class MiddlewareInvalidArgumentError(Exception):
22
"""When middleware receives non keyword=arguments"""
3-
4-
pass

aws_lambda_powertools/tracing/tracer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
99

1010
from ..shared import constants
11-
from ..shared.functions import resolve_truthy_env_var_choice
11+
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
1212
from ..shared.lazy_import import LazyLoader
1313
from .base import BaseProvider, BaseSegment
1414

@@ -720,7 +720,7 @@ def __build_config(
720720
):
721721
""" Populates Tracer config for new and existing initializations """
722722
is_disabled = disabled if disabled is not None else self._is_tracer_disabled()
723-
is_service = service if service is not None else os.getenv(constants.SERVICE_NAME_ENV)
723+
is_service = resolve_env_var_choice(choice=service, env=os.getenv(constants.SERVICE_NAME_ENV))
724724

725725
self._config["provider"] = provider or self._config["provider"] or aws_xray_sdk.core.xray_recorder
726726
self._config["auto_patch"] = auto_patch if auto_patch is not None else self._config["auto_patch"]

aws_lambda_powertools/utilities/batch/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def batch_processor(
125125
Examples
126126
--------
127127
**Processes Lambda's event with PartialSQSProcessor**
128+
128129
>>> from aws_lambda_powertools.utilities.batch import batch_processor, PartialSQSProcessor
129130
>>>
130131
>>> def record_handler(record):

aws_lambda_powertools/utilities/batch/sqs.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class PartialSQSProcessor(BasePartialProcessor):
5353
>>> # have been deleted from the queue after context's exit.
5454
>>>
5555
>>> return result
56+
5657
"""
5758

5859
def __init__(self, config: Optional[Config] = None, suppress_exception: bool = False):
@@ -163,10 +164,11 @@ def sqs_batch_processor(
163164
Examples
164165
--------
165166
**Processes Lambda's event with PartialSQSProcessor**
167+
166168
>>> from aws_lambda_powertools.utilities.batch import sqs_batch_processor
167169
>>>
168170
>>> def record_handler(record):
169-
>>> return record["body"]
171+
>>> return record["body"]
170172
>>>
171173
>>> @sqs_batch_processor(record_handler=record_handler)
172174
>>> def handler(event, context):

aws_lambda_powertools/utilities/idempotency/idempotency.py

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def idempotent(
5151
Examples
5252
--------
5353
**Processes Lambda's event in an idempotent manner**
54+
5455
>>> from aws_lambda_powertools.utilities.idempotency import (
5556
>>> idempotent, DynamoDBPersistenceLayer, IdempotencyConfig
5657
>>> )
@@ -135,6 +136,8 @@ def handle(self) -> Any:
135136
# Now we know the item already exists, we can retrieve it
136137
record = self._get_idempotency_record()
137138
return self._handle_for_status(record)
139+
except Exception as exc:
140+
raise IdempotencyPersistenceLayerError("Failed to save in progress record to idempotency store") from exc
138141

139142
return self._call_lambda_handler()
140143

aws_lambda_powertools/utilities/idempotency/persistence/base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@ def _get_hashed_idempotency_key(self, lambda_event: Dict[str, Any]) -> str:
183183

184184
@staticmethod
185185
def is_missing_idempotency_key(data) -> bool:
186-
return data is None or not data or all(x is None for x in data)
186+
if type(data).__name__ in ("tuple", "list", "dict"):
187+
return all(x is None for x in data)
188+
return not data
187189

188190
def _get_hashed_payload(self, lambda_event: Dict[str, Any]) -> str:
189191
"""

aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ def __init__(
5353
Examples
5454
--------
5555
**Create a DynamoDB persistence layer with custom settings**
56-
>>> from aws_lambda_powertools.utilities.idempotency import idempotent, DynamoDBPersistenceLayer
56+
57+
>>> from aws_lambda_powertools.utilities.idempotency import (
58+
>>> idempotent, DynamoDBPersistenceLayer
59+
>>> )
5760
>>>
5861
>>> persistence_store = DynamoDBPersistenceLayer(table_name="idempotency_store")
5962
>>>
60-
>>> @idempotent(persistence_store=persistence_store, event_key="body")
63+
>>> @idempotent(persistence_store=persistence_store)
6164
>>> def handler(event, context):
6265
>>> return {"StatusCode": 200}
6366
"""

aws_lambda_powertools/utilities/parameters/appconfig.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import boto3
1111
from botocore.config import Config
1212

13+
from ...shared import constants
14+
from ...shared.functions import resolve_env_var_choice
1315
from .base import DEFAULT_PROVIDERS, BaseProvider
1416

1517
CLIENT_ID = str(uuid4())
@@ -33,6 +35,7 @@ class AppConfigProvider(BaseProvider):
3335
**Retrieves the latest configuration value from App Config**
3436
3537
>>> from aws_lambda_powertools.utilities import parameters
38+
>>>
3639
>>> appconf_provider = parameters.AppConfigProvider(environment="my_env", application="my_app")
3740
>>>
3841
>>> value : bytes = appconf_provider.get("my_conf")
@@ -66,7 +69,9 @@ def __init__(
6669

6770
config = config or Config()
6871
self.client = boto3.client("appconfig", config=config)
69-
self.application = application or os.getenv("POWERTOOLS_SERVICE_NAME") or "application_undefined"
72+
self.application = resolve_env_var_choice(
73+
choice=application, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined")
74+
)
7075
self.environment = environment
7176
self.current_version = ""
7277

docs/core/logger.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ You can also change the order of the following log record keys via the `log_reco
451451

452452
#### Logging exceptions
453453

454-
When logging exceptions, Logger will add a new key named `exception`, and will serialize the full traceback as a string.
454+
When logging exceptions, Logger will add new keys named `exception_name` and `exception` with the full traceback as a string.
455455

456456
=== "logging_an_exception.py"
457457

@@ -475,6 +475,7 @@ When logging exceptions, Logger will add a new key named `exception`, and will s
475475
"timestamp": "2020-08-28 18:11:38,886",
476476
"service": "service_undefined",
477477
"sampling_rate": 0.0,
478+
"exception_name":"ValueError",
478479
"exception": "Traceback (most recent call last):\n File \"<input>\", line 2, in <module>\nValueError: something went wrong"
479480
}
480481
```

docs/core/metrics.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ This decorator also **validates**, **serializes**, and **flushes** all your metr
114114

115115
@metrics.log_metrics
116116
def lambda_handler(evt, ctx):
117-
metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
117+
metrics.add_metric(name="BookingConfirmation", unit=MetricUnit.Count, value=1)
118118
...
119119
```
120120
=== "Example CloudWatch Logs excerpt"
@@ -148,7 +148,6 @@ This decorator also **validates**, **serializes**, and **flushes** all your metr
148148
!!! tip "Metric validation"
149149
If metrics are provided, and any of the following criteria are not met, **`SchemaValidationError`** exception will be raised:
150150

151-
* Minimum of 1 dimension
152151
* Maximum of 9 dimensions
153152
* Namespace is set, and no more than one
154153
* Metric units must be [supported by CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html)
@@ -162,6 +161,8 @@ If you want to ensure that at least one metric is emitted, you can pass `raise_o
162161
```python hl_lines="3"
163162
from aws_lambda_powertools.metrics import Metrics
164163

164+
metrics = Metrics()
165+
165166
@metrics.log_metrics(raise_on_empty_metrics=True)
166167
def lambda_handler(evt, ctx):
167168
...
@@ -183,12 +184,12 @@ When using multiple middlewares, use `log_metrics` as your **last decorator** wr
183184
tracer = Tracer(service="booking")
184185
metrics = Metrics(namespace="ExampleApplication", service="booking")
185186

186-
metrics.add_metric(name="ColdStart", unit="Count", value=1)
187+
metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
187188

188189
@metrics.log_metrics
189190
@tracer.capture_lambda_handler
190191
def lambda_handler(evt, ctx):
191-
metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
192+
metrics.add_metric(name="BookingConfirmation", unit=MetricUnit.Count, value=1)
192193
...
193194
```
194195

@@ -200,7 +201,6 @@ You can optionally capture cold start metrics with `log_metrics` decorator via `
200201

201202
```python hl_lines="6"
202203
from aws_lambda_powertools import Metrics
203-
from aws_lambda_powertools.metrics import MetricUnit
204204

205205
metrics = Metrics(service="ExampleService")
206206

@@ -300,7 +300,7 @@ If you prefer not to use `log_metrics` because you might want to encapsulate add
300300
from aws_lambda_powertools.metrics import MetricUnit
301301

302302
metrics = Metrics(namespace="ExampleApplication", service="booking")
303-
metrics.add_metric(name="ColdStart", unit="Count", value=1)
303+
metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
304304

305305
your_metrics_object = metrics.serialize_metric_set()
306306
metrics.clear_metrics()

0 commit comments

Comments
 (0)