Skip to content

docs(tracer): snippets split, improved, and lint #1261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
838f3c5
chore(governance): warn message on closed issues
heitorlessa Jun 10, 2022
1a02f6f
Merge branch 'develop' of https://github.com/heitorlessa/aws-lambda-p…
heitorlessa Jun 23, 2022
02af174
docs(tracer): split initial example
heitorlessa Jun 23, 2022
cb44708
chore(ci): rebuild doc to include examples' changes
heitorlessa Jun 23, 2022
466c2bd
chore: add cfn-lint in pre-commit
heitorlessa Jun 23, 2022
ef1d90f
feat(ci): include examples folder in linting/fmt
heitorlessa Jun 23, 2022
87bccfe
fix(pre-commit): ensure flake8 includes examples
heitorlessa Jun 23, 2022
7f6ef84
feat(ci): include cfn-lint in docs workflow
heitorlessa Jun 23, 2022
c28d715
chore: move to approach B for multiple IaC
heitorlessa Jun 23, 2022
495e4ba
chore: add sam build gitignore
heitorlessa Jun 23, 2022
0252da6
chore: add sam build gitignore
heitorlessa Jun 23, 2022
ab2ad8d
chore: move to approach B for multiple IaC
heitorlessa Jun 23, 2022
e8fc575
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-power…
heitorlessa Jun 23, 2022
0539142
Merge branch 'develop' into docs/tracer-code-split
heitorlessa Jun 23, 2022
78d7b35
chore: add sam build gitignore v2
heitorlessa Jun 23, 2022
cc3743e
chore: remove build leftover
heitorlessa Jun 23, 2022
8bd2349
docs(tracer): split annotations and metadata snippet
heitorlessa Jun 23, 2022
64d92d9
docs(tracer): split synchronous fn snippet
heitorlessa Jun 23, 2022
c187c9e
docs(tracer): split async, ctx manager, and generator
heitorlessa Jun 23, 2022
e5d89a7
docs(tracer): split patch_modules
heitorlessa Jun 23, 2022
fd5df4b
docs(tracer): split disable capture response
heitorlessa Jun 23, 2022
c556f57
docs(tracer): fix highlighting after linting
heitorlessa Jun 23, 2022
b2497fd
docs(tracer): snippet capture error
heitorlessa Jun 23, 2022
6a4654e
docs(tracer): snippet ignore endpoints
heitorlessa Jun 23, 2022
b82dd71
docs(tracer): snippet aiohttp
heitorlessa Jun 23, 2022
a942347
docs(tracer): snippet escape hatch
heitorlessa Jun 23, 2022
139af49
docs(tracer): snippet async concurrency
heitorlessa Jun 23, 2022
bc4a1a3
docs(tracer): snippet tracer reuse
heitorlessa Jun 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,4 @@ site/
!docs/overrides/*.html

!.github/workflows/lib
examples/**/sam/.aws-sam
202 changes: 35 additions & 167 deletions docs/core/tracer.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,22 @@ You can quickly start by initializing `Tracer` and use `capture_lambda_handler`

**Annotations** are key-values associated with traces and indexed by AWS X-Ray. You can use them to filter traces and to create [Trace Groups](https://aws.amazon.com/about-aws/whats-new/2018/11/aws-xray-adds-the-ability-to-group-traces/) to slice and dice your transactions.

```python hl_lines="7" title="Adding annotations with put_annotation method"
from aws_lambda_powertools import Tracer
tracer = Tracer()

@tracer.capture_lambda_handler
def handler(event, context):
...
tracer.put_annotation(key="PaymentStatus", value="SUCCESS")
```python hl_lines="8" title="Adding annotations with put_annotation method"
--8<-- "examples/tracer/src/put_trace_annotations.py"
```

**Metadata** are key-values also associated with traces but not indexed by AWS X-Ray. You can use them to add additional context for an operation using any native object.

```python hl_lines="8" title="Adding arbitrary metadata with put_metadata method"
from aws_lambda_powertools import Tracer
tracer = Tracer()

@tracer.capture_lambda_handler
def handler(event, context):
...
ret = some_logic()
tracer.put_metadata(key="payment_response", value=ret)
```python hl_lines="19" title="Adding arbitrary metadata with put_metadata method"
--8<-- "examples/tracer/src/put_trace_metadata.py"
```

### Synchronous functions

You can trace synchronous functions using the `capture_method` decorator.

```python hl_lines="7 13" title="Tracing an arbitrary function with capture_method"
@tracer.capture_method
def collect_payment(charge_id):
ret = requests.post(PAYMENT_ENDPOINT) # logic
tracer.put_annotation("PAYMENT_STATUS", "SUCCESS") # custom annotation
return ret
```python hl_lines="7" title="Tracing an arbitrary function with capture_method"
--8<-- "examples/tracer/src/capture_method.py"
```

???+ note "Note: Function responses are auto-captured and stored as JSON, by default."
Expand All @@ -93,47 +76,20 @@ You can trace asynchronous functions and generator functions (including context

=== "Async"

```python hl_lines="7"
import asyncio
import contextlib
from aws_lambda_powertools import Tracer

tracer = Tracer()

@tracer.capture_method
async def collect_payment():
...
```python hl_lines="8"
--8<-- "examples/tracer/src/capture_method_async.py"
```

=== "Context manager"

```python hl_lines="7-8"
import asyncio
import contextlib
from aws_lambda_powertools import Tracer

tracer = Tracer()

@contextlib.contextmanager
@tracer.capture_method
def collect_payment_ctxman():
yield result
...
```python hl_lines="12-13"
--8<-- "examples/tracer/src/capture_method_context_manager.py"
```

=== "Generators"

```python hl_lines="9"
import asyncio
import contextlib
from aws_lambda_powertools import Tracer

tracer = Tracer()

@tracer.capture_method
def collect_payment_gen():
yield result
...
--8<-- "examples/tracer/src/capture_method_generators.py"
```

## Advanced
Expand All @@ -144,14 +100,8 @@ Tracer automatically patches all [supported libraries by X-Ray](https://docs.aws

If you're looking to shave a few microseconds, or milliseconds depending on your function memory configuration, you can patch specific modules using `patch_modules` param:

```python hl_lines="7" title="Example of explicitly patching boto3 and requests only"
import boto3
import requests

from aws_lambda_powertools import Tracer

modules_to_be_patched = ["boto3", "requests"]
tracer = Tracer(patch_modules=modules_to_be_patched)
```python hl_lines="8" title="Example of explicitly patching requests only"
--8<-- "examples/tracer/src/patch_modules.py"
```

### Disabling response auto-capture
Expand All @@ -165,27 +115,14 @@ Use **`capture_response=False`** parameter in both `capture_lambda_handler` and

=== "sensitive_data_scenario.py"

```python hl_lines="3 7"
from aws_lambda_powertools import Tracer

@tracer.capture_method(capture_response=False)
def fetch_sensitive_information():
return "sensitive_information"

@tracer.capture_lambda_handler(capture_response=False)
def handler(event, context):
sensitive_information = fetch_sensitive_information()
```python hl_lines="8 15"
--8<-- "examples/tracer/src/disable_capture_response.py"
```
=== "streaming_object_scenario.py"

```python hl_lines="3"
from aws_lambda_powertools import Tracer
=== "streaming_object_scenario.py"

@tracer.capture_method(capture_response=False)
def get_s3_object(bucket_name, object_key):
s3 = boto3.client("s3")
s3_object = get_object(Bucket=bucket_name, Key=object_key)
return s3_object
```python hl_lines="19"
--8<-- "examples/tracer/src/disable_capture_response_streaming_body.py"
```

### Disabling exception auto-capture
Expand All @@ -195,12 +132,8 @@ Use **`capture_error=False`** parameter in both `capture_lambda_handler` and `ca
???+ info
Useful when returning sensitive information in exceptions/stack traces you don't control

```python hl_lines="3 5" title="Disabling exception auto-capture for tracing metadata"
from aws_lambda_powertools import Tracer

@tracer.capture_lambda_handler(capture_error=False)
def handler(event, context):
raise ValueError("some sensitive info in the stack trace...")
```python hl_lines="16 26" title="Disabling exception auto-capture for tracing metadata"
--8<-- "examples/tracer/src/disable_capture_error.py"
```

### Ignoring certain HTTP endpoints
Expand All @@ -209,46 +142,19 @@ You might have endpoints you don't want requests to be traced, perhaps due to th

You can use `ignore_endpoint` method with the hostname and/or URLs you'd like it to be ignored - globs (`*`) are allowed.

```python title="Ignoring certain HTTP endpoints from being traced"
from aws_lambda_powertools import Tracer

tracer = Tracer()
# ignore all calls to `ec2.amazon.com`
tracer.ignore_endpoint(hostname="ec2.amazon.com")
# ignore calls to `*.sensitive.com/password` and `*.sensitive.com/credit-card`
tracer.ignore_endpoint(hostname="*.sensitive.com", urls=["/password", "/credit-card"])


def ec2_api_calls():
return "suppress_api_responses"

@tracer.capture_lambda_handler
def handler(event, context):
for x in long_list:
ec2_api_calls()
```python hl_lines="12-13" title="Ignoring certain HTTP endpoints from being traced"
--8<-- "examples/tracer/src/ignore_endpoints.py"
```

### Tracing aiohttp requests

???+ info
This snippet assumes you have aiohttp as a dependency

You can use `aiohttp_trace_config` function to create a valid [aiohttp trace_config object](https://docs.aiohttp.org/en/stable/tracing_reference.html). This is necessary since X-Ray utilizes aiohttp trace hooks to capture requests end-to-end.

```python hl_lines="5 10" title="Tracing aiohttp requests"
import asyncio
import aiohttp

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.tracing import aiohttp_trace_config

tracer = Tracer()
You can use `aiohttp_trace_config` function to create a valid [aiohttp trace_config object](https://docs.aiohttp.org/en/stable/tracing_reference.html){target="_blank"}. This is necessary since X-Ray utilizes [aiohttp](https://docs.aiohttp.org/en/stable/){target="_blank"} trace hooks to capture requests end-to-end.

async def aiohttp_task():
async with aiohttp.ClientSession(trace_configs=[aiohttp_trace_config()]) as session:
async with session.get("https://httpbin.org/json") as resp:
resp = await resp.json()
return resp
```python hl_lines="7 17" title="Tracing aiohttp requests"
--8<-- "examples/tracer/src/tracing_aiohttp.py"
```

### Escape hatch mechanism
Expand All @@ -257,16 +163,8 @@ You can use `tracer.provider` attribute to access all methods provided by AWS X-

This is useful when you need a feature available in X-Ray that is not available in the Tracer utility, for example [thread-safe](https://github.com/aws/aws-xray-sdk-python/#user-content-trace-threadpoolexecutor), or [context managers](https://github.com/aws/aws-xray-sdk-python/#user-content-start-a-custom-segmentsubsegment).

```python hl_lines="7" title="Tracing a code block with in_subsegment escape hatch"
from aws_lambda_powertools import Tracer

tracer = Tracer()

@tracer.capture_lambda_handler
def handler(event, context):
with tracer.provider.in_subsegment('## custom subsegment') as subsegment:
ret = some_work()
subsegment.put_metadata('response', ret)
```python hl_lines="14" title="Tracing a code block with in_subsegment escape hatch"
--8<-- "examples/tracer/src/sdk_escape_hatch.py"
```

### Concurrent asynchronous functions
Expand All @@ -276,25 +174,8 @@ def handler(event, context):

A safe workaround mechanism is to use `in_subsegment_async` available via Tracer escape hatch (`tracer.provider`).

```python hl_lines="6 7 12 15 17" title="Workaround to safely trace async concurrent functions"
import asyncio

from aws_lambda_powertools import Tracer
tracer = Tracer()

async def another_async_task():
async with tracer.provider.in_subsegment_async("## another_async_task") as subsegment:
subsegment.put_annotation(key="key", value="value")
subsegment.put_metadata(key="key", value="value", namespace="namespace")
...

async def another_async_task_2():
...

@tracer.capture_method
async def collect_payment(charge_id):
asyncio.gather(another_async_task(), another_async_task_2())
...
```python hl_lines="10 17 24" title="Workaround to safely trace async concurrent functions"
--8<-- "examples/tracer/src/capture_method_async_concurrency.py"
```

### Reusing Tracer across your code
Expand All @@ -310,28 +191,15 @@ Tracer keeps a copy of its configuration after the first initialization. This is

=== "handler.py"

```python hl_lines="2 4 9"
from aws_lambda_powertools import Tracer
from payment import collect_payment

tracer = Tracer(service="payment")

@tracer.capture_lambda_handler
def handler(event, context):
charge_id = event.get('charge_id')
payment = collect_payment(charge_id)
```python hl_lines="1 6"
--8<-- "examples/tracer/src/tracer_reuse.py"
```
=== "payment.py"
A new instance of Tracer will be created but will reuse the previous Tracer instance configuration, similar to a Singleton.

```python hl_lines="3 5"
from aws_lambda_powertools import Tracer

tracer = Tracer(service="payment")
=== "tracer_reuse_payment.py"
A new instance of Tracer will be created but will reuse the previous Tracer instance configuration, similar to a Singleton.

@tracer.capture_method
def collect_payment(charge_id: str):
...
```python hl_lines="3"
--8<-- "examples/tracer/src/tracer_reuse_payment.py"
```

## Testing your code
Expand Down
23 changes: 23 additions & 0 deletions examples/tracer/sam/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: AWS Lambda Powertools Tracer doc examples

Globals:
Function:
Timeout: 5
Runtime: python3.9
Tracing: Active
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: example
Layers:
# Find the latest Layer version in the official documentation
# https://awslabs.github.io/aws-lambda-powertools-python/latest/#lambda-layer
- !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:21

Resources:
CaptureLambdaHandlerExample:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../src
Handler: capture_lambda_handler.handler
4 changes: 2 additions & 2 deletions examples/tracer/src/capture_lambda_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def collect_payment(charge_id: str) -> str:

@tracer.capture_lambda_handler
def handler(event: dict, context: LambdaContext) -> str:
charge_id = event.get("charge_id")
return collect_payment(charge_id)
charge_id = event.get("charge_id", "")
return collect_payment(charge_id=charge_id)
16 changes: 16 additions & 0 deletions examples/tracer/src/capture_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from aws_lambda_powertools import Tracer
from aws_lambda_powertools.utilities.typing import LambdaContext

tracer = Tracer()


@tracer.capture_method
def collect_payment(charge_id: str) -> str:
tracer.put_annotation(key="PaymentId", value=charge_id)
return f"dummy payment collected for charge: {charge_id}"


@tracer.capture_lambda_handler
def handler(event: dict, context: LambdaContext) -> str:
charge_id = event.get("charge_id", "")
return collect_payment(charge_id=charge_id)
19 changes: 19 additions & 0 deletions examples/tracer/src/capture_method_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import asyncio

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.utilities.typing import LambdaContext

tracer = Tracer()


@tracer.capture_method
async def collect_payment(charge_id: str) -> str:
tracer.put_annotation(key="PaymentId", value=charge_id)
await asyncio.sleep(0.5)
return f"dummy payment collected for charge: {charge_id}"


@tracer.capture_lambda_handler
def handler(event: dict, context: LambdaContext) -> str:
charge_id = event.get("charge_id", "")
return asyncio.run(collect_payment(charge_id=charge_id))
Loading