Skip to content

Commit 763f75e

Browse files
committed
* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: docs(tracer): snippets split, improved, and lint (aws-powertools#1261) fix(ci): merged_pr add issues write access
2 parents e8fc575 + 159dcbb commit 763f75e

20 files changed

+380
-170
lines changed

.github/workflows/on_merged_pr.yml

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
release_label_on_merge:
1212
if: github.event.pull_request.merged == true && github.event.pull_request.user.login != 'dependabot[bot]'
1313
runs-on: ubuntu-latest
14+
permissions:
15+
issues: write # required for new scoped token
1416
steps:
1517
- name: "Label PR related issue for release"
1618
uses: actions/github-script@v6

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,4 @@ site/
306306
!docs/overrides/*.html
307307

308308
!.github/workflows/lib
309-
.aws-sam/*
309+
examples/**/sam/.aws-sam

docs/core/tracer.md

+35-167
Original file line numberDiff line numberDiff line change
@@ -42,39 +42,22 @@ You can quickly start by initializing `Tracer` and use `capture_lambda_handler`
4242

4343
**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.
4444

45-
```python hl_lines="7" title="Adding annotations with put_annotation method"
46-
from aws_lambda_powertools import Tracer
47-
tracer = Tracer()
48-
49-
@tracer.capture_lambda_handler
50-
def handler(event, context):
51-
...
52-
tracer.put_annotation(key="PaymentStatus", value="SUCCESS")
45+
```python hl_lines="8" title="Adding annotations with put_annotation method"
46+
--8<-- "examples/tracer/src/put_trace_annotations.py"
5347
```
5448

5549
**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.
5650

57-
```python hl_lines="8" title="Adding arbitrary metadata with put_metadata method"
58-
from aws_lambda_powertools import Tracer
59-
tracer = Tracer()
60-
61-
@tracer.capture_lambda_handler
62-
def handler(event, context):
63-
...
64-
ret = some_logic()
65-
tracer.put_metadata(key="payment_response", value=ret)
51+
```python hl_lines="19" title="Adding arbitrary metadata with put_metadata method"
52+
--8<-- "examples/tracer/src/put_trace_metadata.py"
6653
```
6754

6855
### Synchronous functions
6956

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

72-
```python hl_lines="7 13" title="Tracing an arbitrary function with capture_method"
73-
@tracer.capture_method
74-
def collect_payment(charge_id):
75-
ret = requests.post(PAYMENT_ENDPOINT) # logic
76-
tracer.put_annotation("PAYMENT_STATUS", "SUCCESS") # custom annotation
77-
return ret
59+
```python hl_lines="7" title="Tracing an arbitrary function with capture_method"
60+
--8<-- "examples/tracer/src/capture_method.py"
7861
```
7962

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

9477
=== "Async"
9578

96-
```python hl_lines="7"
97-
import asyncio
98-
import contextlib
99-
from aws_lambda_powertools import Tracer
100-
101-
tracer = Tracer()
102-
103-
@tracer.capture_method
104-
async def collect_payment():
105-
...
79+
```python hl_lines="8"
80+
--8<-- "examples/tracer/src/capture_method_async.py"
10681
```
10782

10883
=== "Context manager"
10984

110-
```python hl_lines="7-8"
111-
import asyncio
112-
import contextlib
113-
from aws_lambda_powertools import Tracer
114-
115-
tracer = Tracer()
116-
117-
@contextlib.contextmanager
118-
@tracer.capture_method
119-
def collect_payment_ctxman():
120-
yield result
121-
...
85+
```python hl_lines="12-13"
86+
--8<-- "examples/tracer/src/capture_method_context_manager.py"
12287
```
12388

12489
=== "Generators"
12590

12691
```python hl_lines="9"
127-
import asyncio
128-
import contextlib
129-
from aws_lambda_powertools import Tracer
130-
131-
tracer = Tracer()
132-
133-
@tracer.capture_method
134-
def collect_payment_gen():
135-
yield result
136-
...
92+
--8<-- "examples/tracer/src/capture_method_generators.py"
13793
```
13894

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

145101
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:
146102

147-
```python hl_lines="7" title="Example of explicitly patching boto3 and requests only"
148-
import boto3
149-
import requests
150-
151-
from aws_lambda_powertools import Tracer
152-
153-
modules_to_be_patched = ["boto3", "requests"]
154-
tracer = Tracer(patch_modules=modules_to_be_patched)
103+
```python hl_lines="8" title="Example of explicitly patching requests only"
104+
--8<-- "examples/tracer/src/patch_modules.py"
155105
```
156106

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

166116
=== "sensitive_data_scenario.py"
167117

168-
```python hl_lines="3 7"
169-
from aws_lambda_powertools import Tracer
170-
171-
@tracer.capture_method(capture_response=False)
172-
def fetch_sensitive_information():
173-
return "sensitive_information"
174-
175-
@tracer.capture_lambda_handler(capture_response=False)
176-
def handler(event, context):
177-
sensitive_information = fetch_sensitive_information()
118+
```python hl_lines="8 15"
119+
--8<-- "examples/tracer/src/disable_capture_response.py"
178120
```
179-
=== "streaming_object_scenario.py"
180121

181-
```python hl_lines="3"
182-
from aws_lambda_powertools import Tracer
122+
=== "streaming_object_scenario.py"
183123

184-
@tracer.capture_method(capture_response=False)
185-
def get_s3_object(bucket_name, object_key):
186-
s3 = boto3.client("s3")
187-
s3_object = get_object(Bucket=bucket_name, Key=object_key)
188-
return s3_object
124+
```python hl_lines="19"
125+
--8<-- "examples/tracer/src/disable_capture_response_streaming_body.py"
189126
```
190127

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

198-
```python hl_lines="3 5" title="Disabling exception auto-capture for tracing metadata"
199-
from aws_lambda_powertools import Tracer
200-
201-
@tracer.capture_lambda_handler(capture_error=False)
202-
def handler(event, context):
203-
raise ValueError("some sensitive info in the stack trace...")
135+
```python hl_lines="16 26" title="Disabling exception auto-capture for tracing metadata"
136+
--8<-- "examples/tracer/src/disable_capture_error.py"
204137
```
205138

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

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

212-
```python title="Ignoring certain HTTP endpoints from being traced"
213-
from aws_lambda_powertools import Tracer
214-
215-
tracer = Tracer()
216-
# ignore all calls to `ec2.amazon.com`
217-
tracer.ignore_endpoint(hostname="ec2.amazon.com")
218-
# ignore calls to `*.sensitive.com/password` and `*.sensitive.com/credit-card`
219-
tracer.ignore_endpoint(hostname="*.sensitive.com", urls=["/password", "/credit-card"])
220-
221-
222-
def ec2_api_calls():
223-
return "suppress_api_responses"
224-
225-
@tracer.capture_lambda_handler
226-
def handler(event, context):
227-
for x in long_list:
228-
ec2_api_calls()
145+
```python hl_lines="12-13" title="Ignoring certain HTTP endpoints from being traced"
146+
--8<-- "examples/tracer/src/ignore_endpoints.py"
229147
```
230148

231149
### Tracing aiohttp requests
232150

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

236-
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.
237-
238-
```python hl_lines="5 10" title="Tracing aiohttp requests"
239-
import asyncio
240-
import aiohttp
241-
242-
from aws_lambda_powertools import Tracer
243-
from aws_lambda_powertools.tracing import aiohttp_trace_config
244-
245-
tracer = Tracer()
154+
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.
246155

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

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

258164
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).
259165

260-
```python hl_lines="7" title="Tracing a code block with in_subsegment escape hatch"
261-
from aws_lambda_powertools import Tracer
262-
263-
tracer = Tracer()
264-
265-
@tracer.capture_lambda_handler
266-
def handler(event, context):
267-
with tracer.provider.in_subsegment('## custom subsegment') as subsegment:
268-
ret = some_work()
269-
subsegment.put_metadata('response', ret)
166+
```python hl_lines="14" title="Tracing a code block with in_subsegment escape hatch"
167+
--8<-- "examples/tracer/src/sdk_escape_hatch.py"
270168
```
271169

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

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

279-
```python hl_lines="6 7 12 15 17" title="Workaround to safely trace async concurrent functions"
280-
import asyncio
281-
282-
from aws_lambda_powertools import Tracer
283-
tracer = Tracer()
284-
285-
async def another_async_task():
286-
async with tracer.provider.in_subsegment_async("## another_async_task") as subsegment:
287-
subsegment.put_annotation(key="key", value="value")
288-
subsegment.put_metadata(key="key", value="value", namespace="namespace")
289-
...
290-
291-
async def another_async_task_2():
292-
...
293-
294-
@tracer.capture_method
295-
async def collect_payment(charge_id):
296-
asyncio.gather(another_async_task(), another_async_task_2())
297-
...
177+
```python hl_lines="10 17 24" title="Workaround to safely trace async concurrent functions"
178+
--8<-- "examples/tracer/src/capture_method_async_concurrency.py"
298179
```
299180

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

311192
=== "handler.py"
312193

313-
```python hl_lines="2 4 9"
314-
from aws_lambda_powertools import Tracer
315-
from payment import collect_payment
316-
317-
tracer = Tracer(service="payment")
318-
319-
@tracer.capture_lambda_handler
320-
def handler(event, context):
321-
charge_id = event.get('charge_id')
322-
payment = collect_payment(charge_id)
194+
```python hl_lines="1 6"
195+
--8<-- "examples/tracer/src/tracer_reuse.py"
323196
```
324-
=== "payment.py"
325-
A new instance of Tracer will be created but will reuse the previous Tracer instance configuration, similar to a Singleton.
326-
327-
```python hl_lines="3 5"
328-
from aws_lambda_powertools import Tracer
329197

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

332-
@tracer.capture_method
333-
def collect_payment(charge_id: str):
334-
...
201+
```python hl_lines="3"
202+
--8<-- "examples/tracer/src/tracer_reuse_payment.py"
335203
```
336204

337205
## Testing your code

examples/tracer/src/capture_lambda_handler.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ def collect_payment(charge_id: str) -> str:
1111

1212
@tracer.capture_lambda_handler
1313
def handler(event: dict, context: LambdaContext) -> str:
14-
charge_id = event.get("charge_id")
15-
return collect_payment(charge_id)
14+
charge_id = event.get("charge_id", "")
15+
return collect_payment(charge_id=charge_id)

examples/tracer/src/capture_method.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from aws_lambda_powertools import Tracer
2+
from aws_lambda_powertools.utilities.typing import LambdaContext
3+
4+
tracer = Tracer()
5+
6+
7+
@tracer.capture_method
8+
def collect_payment(charge_id: str) -> str:
9+
tracer.put_annotation(key="PaymentId", value=charge_id)
10+
return f"dummy payment collected for charge: {charge_id}"
11+
12+
13+
@tracer.capture_lambda_handler
14+
def handler(event: dict, context: LambdaContext) -> str:
15+
charge_id = event.get("charge_id", "")
16+
return collect_payment(charge_id=charge_id)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
3+
from aws_lambda_powertools import Tracer
4+
from aws_lambda_powertools.utilities.typing import LambdaContext
5+
6+
tracer = Tracer()
7+
8+
9+
@tracer.capture_method
10+
async def collect_payment(charge_id: str) -> str:
11+
tracer.put_annotation(key="PaymentId", value=charge_id)
12+
await asyncio.sleep(0.5)
13+
return f"dummy payment collected for charge: {charge_id}"
14+
15+
16+
@tracer.capture_lambda_handler
17+
def handler(event: dict, context: LambdaContext) -> str:
18+
charge_id = event.get("charge_id", "")
19+
return asyncio.run(collect_payment(charge_id=charge_id))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import asyncio
2+
3+
from aws_lambda_powertools import Tracer
4+
from aws_lambda_powertools.utilities.typing import LambdaContext
5+
6+
tracer = Tracer()
7+
8+
9+
async def another_async_task():
10+
async with tracer.provider.in_subsegment_async("## another_async_task") as subsegment:
11+
subsegment.put_annotation(key="key", value="value")
12+
subsegment.put_metadata(key="key", value="value", namespace="namespace")
13+
...
14+
15+
16+
async def another_async_task_2():
17+
async with tracer.provider.in_subsegment_async("## another_async_task_2") as subsegment:
18+
subsegment.put_annotation(key="key", value="value")
19+
subsegment.put_metadata(key="key", value="value", namespace="namespace")
20+
...
21+
22+
23+
async def collect_payment(charge_id: str) -> str:
24+
await asyncio.gather(another_async_task(), another_async_task_2())
25+
return f"dummy payment collected for charge: {charge_id}"
26+
27+
28+
@tracer.capture_lambda_handler
29+
def handler(event: dict, context: LambdaContext) -> str:
30+
charge_id = event.get("charge_id", "")
31+
return asyncio.run(collect_payment(charge_id=charge_id))

0 commit comments

Comments
 (0)