diff --git a/.gitignore b/.gitignore index 5d28e3a615f..b776e1999c2 100644 --- a/.gitignore +++ b/.gitignore @@ -306,3 +306,4 @@ site/ !docs/overrides/*.html !.github/workflows/lib +examples/**/sam/.aws-sam diff --git a/docs/core/tracer.md b/docs/core/tracer.md index 963552c3e99..34eb1ed2b93 100644 --- a/docs/core/tracer.md +++ b/docs/core/tracer.md @@ -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." @@ -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 @@ -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 @@ -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 @@ -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 @@ -209,23 +142,8 @@ 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 @@ -233,22 +151,10 @@ def handler(event, context): ???+ 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 @@ -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 @@ -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 @@ -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 diff --git a/examples/tracer/sam/template.yaml b/examples/tracer/sam/template.yaml new file mode 100644 index 00000000000..11cee9be3a8 --- /dev/null +++ b/examples/tracer/sam/template.yaml @@ -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 diff --git a/examples/tracer/src/capture_lambda_handler.py b/examples/tracer/src/capture_lambda_handler.py index 461a31cd213..f5d2c1efcea 100644 --- a/examples/tracer/src/capture_lambda_handler.py +++ b/examples/tracer/src/capture_lambda_handler.py @@ -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) diff --git a/examples/tracer/src/capture_method.py b/examples/tracer/src/capture_method.py new file mode 100644 index 00000000000..edf1ed719f4 --- /dev/null +++ b/examples/tracer/src/capture_method.py @@ -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) diff --git a/examples/tracer/src/capture_method_async.py b/examples/tracer/src/capture_method_async.py new file mode 100644 index 00000000000..e142ef8f163 --- /dev/null +++ b/examples/tracer/src/capture_method_async.py @@ -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)) diff --git a/examples/tracer/src/capture_method_async_concurrency.py b/examples/tracer/src/capture_method_async_concurrency.py new file mode 100644 index 00000000000..82e89070c75 --- /dev/null +++ b/examples/tracer/src/capture_method_async_concurrency.py @@ -0,0 +1,31 @@ +import asyncio + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +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(): + async with tracer.provider.in_subsegment_async("## another_async_task_2") as subsegment: + subsegment.put_annotation(key="key", value="value") + subsegment.put_metadata(key="key", value="value", namespace="namespace") + ... + + +async def collect_payment(charge_id: str) -> str: + await asyncio.gather(another_async_task(), another_async_task_2()) + 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)) diff --git a/examples/tracer/src/capture_method_context_manager.py b/examples/tracer/src/capture_method_context_manager.py new file mode 100644 index 00000000000..083443607ac --- /dev/null +++ b/examples/tracer/src/capture_method_context_manager.py @@ -0,0 +1,27 @@ +import contextlib +from collections.abc import Generator + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() + + +@contextlib.contextmanager +@tracer.capture_method +def collect_payment(charge_id: str) -> Generator[str, None, None]: + try: + yield f"dummy payment collected for charge: {charge_id}" + finally: + tracer.put_annotation(key="PaymentId", value=charge_id) + + +@tracer.capture_lambda_handler +@logger.inject_lambda_context +def handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + with collect_payment(charge_id=charge_id) as receipt_id: + logger.info(f"Processing payment collection for charge {charge_id} with receipt {receipt_id}") + + return receipt_id diff --git a/examples/tracer/src/capture_method_generators.py b/examples/tracer/src/capture_method_generators.py new file mode 100644 index 00000000000..65b87c251e8 --- /dev/null +++ b/examples/tracer/src/capture_method_generators.py @@ -0,0 +1,17 @@ +from collections.abc import Generator + +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) -> Generator[str, None, None]: + yield 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 next(collect_payment(charge_id=charge_id)) diff --git a/examples/tracer/src/disable_capture_error.py b/examples/tracer/src/disable_capture_error.py new file mode 100644 index 00000000000..7b7d7e6ad23 --- /dev/null +++ b/examples/tracer/src/disable_capture_error.py @@ -0,0 +1,31 @@ +import os + +import requests + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +ENDPOINT = os.getenv("PAYMENT_API", "") + + +class PaymentError(Exception): + ... + + +@tracer.capture_method(capture_error=False) +def collect_payment(charge_id: str) -> dict: + try: + ret = requests.post(url=f"{ENDPOINT}/collect", data={"charge_id": charge_id}) + ret.raise_for_status() + return ret.json() + except requests.HTTPError as e: + raise PaymentError(f"Unable to collect payment for charge {charge_id}") from e + + +@tracer.capture_lambda_handler(capture_error=False) +def handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + ret = collect_payment(charge_id=charge_id) + + return ret.get("receipt_id", "") diff --git a/examples/tracer/src/disable_capture_response.py b/examples/tracer/src/disable_capture_response.py new file mode 100644 index 00000000000..ffe8230eece --- /dev/null +++ b/examples/tracer/src/disable_capture_response.py @@ -0,0 +1,18 @@ +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() + + +@tracer.capture_method(capture_response=False) +def collect_payment(charge_id: str) -> str: + tracer.put_annotation(key="PaymentId", value=charge_id) + logger.debug("Returning sensitive information....") + return f"dummy payment collected for charge: {charge_id}" + + +@tracer.capture_lambda_handler(capture_response=False) +def handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + return collect_payment(charge_id=charge_id) diff --git a/examples/tracer/src/disable_capture_response_streaming_body.py b/examples/tracer/src/disable_capture_response_streaming_body.py new file mode 100644 index 00000000000..3e458a98eb4 --- /dev/null +++ b/examples/tracer/src/disable_capture_response_streaming_body.py @@ -0,0 +1,30 @@ +import os + +import boto3 +from botocore.response import StreamingBody + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +BUCKET = os.getenv("BUCKET_NAME", "") +REPORT_KEY = os.getenv("REPORT_KEY", "") + +tracer = Tracer() +logger = Logger() + +session = boto3.Session() +s3 = session.client("s3") + + +@tracer.capture_method(capture_response=False) +def fetch_payment_report(payment_id: str) -> StreamingBody: + ret = s3.get_object(Bucket=BUCKET, Key=f"{REPORT_KEY}/{payment_id}") + logger.debug("Returning streaming body from S3 object....") + return ret["body"] + + +@tracer.capture_lambda_handler(capture_response=False) +def handler(event: dict, context: LambdaContext) -> str: + payment_id = event.get("payment_id", "") + report = fetch_payment_report(payment_id=payment_id) + return report.read().decode() diff --git a/examples/tracer/src/ignore_endpoints.py b/examples/tracer/src/ignore_endpoints.py new file mode 100644 index 00000000000..6484cfcf5b0 --- /dev/null +++ b/examples/tracer/src/ignore_endpoints.py @@ -0,0 +1,35 @@ +import os + +import requests + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +ENDPOINT = os.getenv("PAYMENT_API", "") +IGNORE_URLS = ["/collect", "/refund"] + +tracer = Tracer() +tracer.ignore_endpoint(hostname=ENDPOINT, urls=IGNORE_URLS) +tracer.ignore_endpoint(hostname=f"*.{ENDPOINT}", urls=IGNORE_URLS) # `.ENDPOINT` + + +class PaymentError(Exception): + ... + + +@tracer.capture_method(capture_error=False) +def collect_payment(charge_id: str) -> dict: + try: + ret = requests.post(url=f"{ENDPOINT}/collect", data={"charge_id": charge_id}) + ret.raise_for_status() + return ret.json() + except requests.HTTPError as e: + raise PaymentError(f"Unable to collect payment for charge {charge_id}") from e + + +@tracer.capture_lambda_handler(capture_error=False) +def handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + ret = collect_payment(charge_id=charge_id) + + return ret.get("receipt_id", "") diff --git a/examples/tracer/src/patch_modules.py b/examples/tracer/src/patch_modules.py new file mode 100644 index 00000000000..09e7092a85a --- /dev/null +++ b/examples/tracer/src/patch_modules.py @@ -0,0 +1,16 @@ +import requests + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +MODULES = ["requests"] + +tracer = Tracer(patch_modules=MODULES) + + +@tracer.capture_lambda_handler +def handler(event: dict, context: LambdaContext) -> str: + ret = requests.get("https://httpbin.org/get") + ret.raise_for_status() + + return ret.json() diff --git a/examples/tracer/src/put_trace_annotations.py b/examples/tracer/src/put_trace_annotations.py new file mode 100644 index 00000000000..0d9455c7acd --- /dev/null +++ b/examples/tracer/src/put_trace_annotations.py @@ -0,0 +1,15 @@ +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() + + +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) diff --git a/examples/tracer/src/put_trace_metadata.py b/examples/tracer/src/put_trace_metadata.py new file mode 100644 index 00000000000..23b6753677c --- /dev/null +++ b/examples/tracer/src/put_trace_metadata.py @@ -0,0 +1,21 @@ +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() + + +def collect_payment(charge_id: str) -> str: + return f"dummy payment collected for charge: {charge_id}" + + +@tracer.capture_lambda_handler +def handler(event: dict, context: LambdaContext) -> str: + payment_context = { + "charge_id": event.get("charge_id", ""), + "merchant_id": event.get("merchant_id", ""), + "request_id": context.aws_request_id, + } + payment_context["receipt_id"] = collect_payment(charge_id=payment_context["charge_id"]) + tracer.put_metadata(key="payment_response", value=payment_context) + + return payment_context["receipt_id"] diff --git a/examples/tracer/src/sdk_escape_hatch.py b/examples/tracer/src/sdk_escape_hatch.py new file mode 100644 index 00000000000..7f046caff9d --- /dev/null +++ b/examples/tracer/src/sdk_escape_hatch.py @@ -0,0 +1,19 @@ +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() + + +def collect_payment(charge_id: str) -> str: + 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", "") + with tracer.provider.in_subsegment("## collect_payment") as subsegment: + subsegment.put_annotation(key="PaymentId", value=charge_id) + ret = collect_payment(charge_id=charge_id) + subsegment.put_metadata(key="payment_response", value=ret) + + return ret diff --git a/examples/tracer/src/tracer_reuse.py b/examples/tracer/src/tracer_reuse.py new file mode 100644 index 00000000000..5f12f82b714 --- /dev/null +++ b/examples/tracer/src/tracer_reuse.py @@ -0,0 +1,12 @@ +from tracer_reuse_payment import collect_payment + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() + + +@tracer.capture_lambda_handler +def handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + return collect_payment(charge_id=charge_id) diff --git a/examples/tracer/src/tracer_reuse_payment.py b/examples/tracer/src/tracer_reuse_payment.py new file mode 100644 index 00000000000..109d1cfbc53 --- /dev/null +++ b/examples/tracer/src/tracer_reuse_payment.py @@ -0,0 +1,8 @@ +from aws_lambda_powertools import Tracer + +tracer = Tracer() + + +@tracer.capture_method +def collect_payment(charge_id: str) -> str: + return f"dummy payment collected for charge: {charge_id}" diff --git a/examples/tracer/src/tracing_aiohttp.py b/examples/tracer/src/tracing_aiohttp.py new file mode 100644 index 00000000000..45fe6a46f38 --- /dev/null +++ b/examples/tracer/src/tracing_aiohttp.py @@ -0,0 +1,25 @@ +import asyncio +import os + +import aiohttp + +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.tracing import aiohttp_trace_config +from aws_lambda_powertools.utilities.typing import LambdaContext + +ENDPOINT = os.getenv("PAYMENT_API", "") + +tracer = Tracer() + + +@tracer.capture_method +async def collect_payment(charge_id: str) -> dict: + async with aiohttp.ClientSession(trace_configs=[aiohttp_trace_config()]) as session: + async with session.get(f"{ENDPOINT}/collect") as resp: + return await resp.json() + + +@tracer.capture_lambda_handler +def handler(event: dict, context: LambdaContext) -> dict: + charge_id = event.get("charge_id", "") + return asyncio.run(collect_payment(charge_id=charge_id))