Skip to content

Commit 7311c5d

Browse files
authored
Merge pull request #174 from heitorlessa/improv/docs-logger-metrics-testing
docs: add testing tips, increase content width, and improve log sampling wording
2 parents b8d15c3 + 0ac09d6 commit 7311c5d

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

docs/content/core/logger.mdx

+49-7
Original file line numberDiff line numberDiff line change
@@ -222,21 +222,24 @@ If you ever forget to use `child` param, we will return an existing `Logger` wit
222222

223223
## Sampling debug logs
224224

225-
You can dynamically set a percentage of your logs to **DEBUG** level using `sample_rate` param or via env var `POWERTOOLS_LOGGER_SAMPLE_RATE`.
225+
Sampling allows you to set your Logger Log Level as DEBUG based on a percentage of your concurrent/cold start invocations. You can set a sampling value of `0.0` to `1` (100%) using either `sample_rate` parameter or `POWERTOOLS_LOGGER_SAMPLE_RATE` env var.
226226

227-
Sampling calculation happens at the Logger class initialization. This means, when configured it, sampling it's more likely to happen during concurrent requests, or infrequent invocations as [new Lambda execution contexts are created](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html), not reused.
227+
This is useful when you want to troubleshoot an issue, say a sudden increase in concurrency, and you might not have enough information in your logs as Logger log level was understandably set as INFO.
228+
229+
Sampling decision happens at the Logger class initialization, which only happens during a cold start. This means sampling may happen significant more or less than you expect if you have a steady low number of invocations and thus few cold starts.
228230

229231
<Note type="info">
230-
If you want this logic to happen on every invocation regardless whether Lambda reuses the execution environment or not, then create your Logger inside your Lambda handler.
232+
If you want Logger to calculate sampling on every invocation, then please open a feature request.
231233
</Note><br/>
232234

233235
```python:title=collect.py
234236
from aws_lambda_powertools import Logger
235237

236238
# Sample 10% of debug logs e.g. 0.1
237-
logger = Logger(sample_rate=0.1) # highlight-line
239+
logger = Logger(sample_rate=0.1, level="INFO") # highlight-line
238240

239241
def handler(event, context):
242+
logger.debug("Verifying whether order_id is present")
240243
if "order_id" in event:
241244
logger.info("Collecting payment")
242245
...
@@ -245,7 +248,21 @@ def handler(event, context):
245248
<details>
246249
<summary><strong>Excerpt output in CloudWatch Logs</strong></summary>
247250

248-
```json:title=cloudwatch_logs.json
251+
```json:title=sampled_log_request_as_debug.json
252+
{
253+
"timestamp": "2020-05-24 18:17:33,774",
254+
"level": "DEBUG", // highlight-line
255+
"location": "collect.handler:1",
256+
"service": "payment",
257+
"lambda_function_name": "test",
258+
"lambda_function_memory_size": 128,
259+
"lambda_function_arn": "arn:aws:lambda:eu-west-1:12345678910:function:test",
260+
"lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
261+
"cold_start": true,
262+
"sampling_rate": 0.1, // highlight-line
263+
"message": "Verifying whether order_id is present"
264+
}
265+
249266
{
250267
"timestamp": "2020-05-24 18:17:33,774",
251268
"level": "INFO",
@@ -260,6 +277,7 @@ def handler(event, context):
260277
"message": "Collecting payment"
261278
}
262279
```
280+
263281
</details>
264282

265283

@@ -305,7 +323,7 @@ This can be fixed by either ensuring both has the `service` value as `payment`,
305323

306324
You might want to continue to use the same date formatting style, or override `location` to display the `package.function_name:line_number` as you previously had.
307325

308-
Logger allows you to either change the format or suppress the following keys altogether at the initialization: `location`, `timestamp`, `level`, and `datefmt`
326+
Logger allows you to either change the format or suppress the following keys altogether at the initialization: `location`, `timestamp`, `level`, `xray_trace_id`, and `datefmt`
309327

310328
```python
311329
from aws_lambda_powertools import Logger
@@ -317,7 +335,7 @@ logger = Logger(stream=stdout, location="[%(funcName)s] %(module)s", datefmt="fa
317335
logger = Logger(stream=stdout, location=None) # highlight-line
318336
```
319337

320-
Alternatively, you can also change the order of the following log record keys via the `log_record_order` parameter: `level`, `location`, `message`, and `timestamp`
338+
Alternatively, you can also change the order of the following log record keys via the `log_record_order` parameter: `level`, `location`, `message`, `xray_trace_id`, and `timestamp`
321339

322340
```python
323341
from aws_lambda_powertools import Logger
@@ -358,3 +376,27 @@ except Exception:
358376
}
359377
```
360378
</details>
379+
380+
381+
## Testing your code
382+
383+
When unit testing your code that makes use of `inject_lambda_context` decorator, you need to pass a dummy Lambda Context, or else Logger will fail.
384+
385+
This is a Pytest sample that provides the minimum information necessary for Logger to succeed:
386+
387+
```python:title=fake_lambda_context_for_logger.py
388+
@pytest.fixture
389+
def lambda_context():
390+
lambda_context = {
391+
"function_name": "test",
392+
"memory_limit_in_mb": 128,
393+
"invoked_function_arn": "arn:aws:lambda:eu-west-1:809313241:function:test",
394+
"aws_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
395+
}
396+
397+
return namedtuple("LambdaContext", lambda_context.keys())(*lambda_context.values())
398+
399+
def test_lambda_handler(lambda_handler, lambda_context):
400+
test_event = {'test': 'event'}
401+
lambda_handler(test_event, lambda_context) # this will now have a Context object populated
402+
```

docs/content/core/metrics.mdx

+28-2
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,37 @@ This has the advantage of keeping cold start metric separate from your applicati
251251

252252
## Testing your code
253253

254+
### Environment variables
255+
254256
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.
255257

256258
```bash:title=pytest_metric_namespace.sh
257-
258259
POWERTOOLS_SERVICE_NAME="Example" POWERTOOLS_METRICS_NAMESPACE="Application" python -m pytest
259260
```
260261

261-
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)`.
262+
If you prefer setting environment variable for specific tests, and are using Pytest, you can use [monkeypatch](https://docs.pytest.org/en/latest/monkeypatch.html) fixture:
263+
264+
```python:title=pytest_env_var.py
265+
def test_namespace_env_var(monkeypatch):
266+
# Set POWERTOOLS_METRICS_NAMESPACE before initializating Metrics
267+
monkeypatch.setenv("POWERTOOLS_METRICS_NAMESPACE", namespace)
268+
269+
metrics = Metrics()
270+
...
271+
```
272+
273+
> Ignore this, if you are explicitly setting namespace/default dimension via `namespace` and `service` parameters: `metrics = Metrics(namespace=ApplicationName, service=ServiceName)`
274+
275+
### Clearing metrics
276+
277+
`Metrics` keep metrics in memory across multiple instances. If you need to test this behaviour, you can use the following Pytest fixture to ensure metrics are reset incl. cold start:
278+
279+
```python:title=pytest_metrics_reset_fixture.py
280+
@pytest.fixture(scope="function", autouse=True)
281+
def reset_metric_set():
282+
# Clear out every metric data prior to every test
283+
metrics = Metrics()
284+
metrics.clear_metrics()
285+
metrics_global.is_cold_start = True # ensure each test has cold start
286+
yield
287+
```

docs/src/gatsby-theme-apollo-core/components/flex-wrapper.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import styled from '@emotion/styled';
33
const FlexWrapper = styled.div({
44
display: 'flex',
55
minHeight: '100vh',
6-
maxWidth: 1600,
6+
maxWidth: '87vw',
77
margin: '0 auto'
88
});
99

docs/src/styles/global.css

+4
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ tr > td {
2525
.token.property {
2626
color: darkmagenta !important
2727
}
28+
29+
blockquote {
30+
font-size: 1.15em
31+
}

0 commit comments

Comments
 (0)