diff --git a/docs/core/tracer.md b/docs/core/tracer.md
index 424f54caf4..21c3bf2cf4 100644
--- a/docs/core/tracer.md
+++ b/docs/core/tracer.md
@@ -3,7 +3,7 @@ title: Tracer
description: Core utility
---
-Tracer is an opinionated thin wrapper for [AWS X-Ray Python SDK](https://github.com/aws/aws-xray-sdk-python/).
+Tracer is an opinionated thin wrapper for [AWS X-Ray SDK for Node.js](https://github.com/aws/aws-xray-sdk-node).

@@ -11,8 +11,8 @@ Tracer is an opinionated thin wrapper for [AWS X-Ray Python SDK](https://github.
* Auto capture cold start as annotation, and responses or full exceptions as metadata
* Auto-disable when not running in AWS Lambda environment
-* Support tracing async methods, generators, and context managers
-* Auto patch supported modules by AWS X-Ray
+* Support tracing functions via decorators, middleware, and manual instrumentation
+* Support tracing AWS SDK v2 and v3 via AWS X-Ray SDK for Node.js
## Getting started
@@ -38,26 +38,79 @@ Before your use this utility, your AWS Lambda function [must have permissions](h
### Lambda handler
-You can quickly start by importing the `Tracer` class, initialize it outside the Lambda handler, and use `capture_lambda_handler` decorator.
+You can quickly start by importing the `Tracer` class, initialize it outside the Lambda handler, and instrument your function.
-=== "index.ts"
+=== "Middleware"
+
+ ```typescript hl_lines="1 3 6"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
+
+ const tracer = Tracer(); // Sets service via env var
+ // OR tracer = Tracer({ service: 'example' });
- ```typescript hl_lines="1 3 6"
- import { Tracer } from '@aws-lambda-powertools/tracer';
+ // TODO: update example once middleware has been implemented.
+
+ export const handler = async (_event: any, _context: any) => {
+ ...
+ }
+ ```
+
+=== "Decorator"
+
+ ```typescript hl_lines="1 3 7"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
+
+ const tracer = Tracer(); // Sets service via env var
+ // OR tracer = Tracer({ service: 'example' });
+
+ class Lambda {
+ @tracer.captureLambdaHanlder()
+ public handler(event: any, context: any) {
+ ...
+ }
+ }
+
+ export const handlerClass = new Lambda();
+ export const handler = handlerClass.handler;
+ ```
- const tracer = Tracer(); // Sets service via env var
- // OR tracer = Tracer({ service: 'example' });
+=== "Manual"
- @tracer.capture_lambda_handler
- const handler = (event, context) => {
- const chargeId = event.chargeId;
- const payment = collectPayment(chargeId);
- ...
- }
- ```
+ ```typescript hl_lines="1-2 4 8-9 11 17 20 24"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
+ import { Segment } from 'aws-xray-sdk-core';
+
+ const tracer = Tracer(); // Sets service via env var
+ // OR tracer = Tracer({ service: 'example' });
+
+ export const handler = async (_event: any, context: any) => {
+ // Create subsegment & set it as active
+ const subsegment = new Subsegment(`## ${context.functionName}`);
+ tracer.setSegment(subsegment);
+ // Add the ColdStart annotation
+ this.putAnnotation('ColdStart', tracer.coldStart);
+
+ let res;
+ try {
+ res = await someLogic(); // Do something
+ // Add the response as metadata
+ tracer.putMetadata(`${context.functionName} response`, data);
+ } catch (err) {
+ // Add the error as metadata
+ subsegment.addError(err, false);
+ }
+
+ // Close subsegment
+ subsegment.close();
+
+ return res;
+ }
+ ```
-When using this `capture_lambda_handler` decorator, Tracer performs these additional tasks to ease operations:
+
+When using thes `captureLambdaHanlder` decorator or the `TBD` middleware, Tracer performs these additional tasks to ease operations:
+* Handles the lifecycle of the subsegment
* Creates a `ColdStart` annotation to easily filter traces that have had an initialization overhead
* Captures any response, or full exceptions generated by the handler, and include as tracing metadata
@@ -70,146 +123,162 @@ When using this `capture_lambda_handler` decorator, Tracer performs these additi
=== "Annotations"
You can add annotations using `putAnnotation` method.
- ```typescript hl_lines="7"
- import { Tracer } from '@aws-lambda-powertools/tracer';
-
- const tracer = Tracer()
-
- @tracer.capture_lambda_handler
- const handler = (event, context) => {
- ...
- tracer.addAnnotation('payment_response', 'SUCCESS');
- }
+ ```typescript hl_lines="6"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
+
+ const tracer = new Tracer({ serviceName: 'my-service' });
+
+ export const handler = async (_event: any, _context: any) => {
+ tracer.putAnnotation('PaymentStatus', "SUCCESS");
+ }
```
=== "Metadata"
- You can add metadata using `putMetadata` method.
+ You can add metadata using `putMetadata` method.
- ```typescript hl_lines="9"
+ ```typescript hl_lines="7"
import { Tracer } from '@aws-lambda-powertools/tracer';
- const tracer = Tracer()
+ const tracer = new Tracer({ serviceName: 'my-service' });
- @tracer.capture_lambda_handler
- const handler = (event, context) => {
- ...
- const res = someLogic();
- tracer.putMetadata('payment_response', res);
- }
+ export const handler = async (_event: any, _context: any) => {
+ const res = someLogic();
+ tracer.putMetadata('PaymentResponse', res);
+ }
```
-[//]:# (START EDITING FROM HERE DOWN)
-
-### Synchronous functions
+### Methods
-You can trace synchronous functions using the `capture_method` decorator.
+You can trace other methods using the `captureMethod` decorator.
-!!! warning
- **When `capture_response` is enabled, the function response will be read and serialized as json.**
+=== "Middleware"
- The serialization is performed by the aws-xray-sdk which uses the `jsonpickle` module. This can cause
- unintended consequences if there are side effects to recursively reading the returned value, for example if the
- decorated function response contains a file-like object or a `StreamingBody` for S3 objects.
+ ```typescript hl_lines="1 3 6"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
- ```python hl_lines="7 13"
- @tracer.capture_method
- def collect_payment(charge_id):
- ret = requests.post(PAYMENT_ENDPOINT) # logic
- tracer.put_annotation("PAYMENT_STATUS", "SUCCESS") # custom annotation
- return ret
- ```
+ const tracer = Tracer();
-### Asynchronous and generator functions
+ // TODO: update example once middleware has been implemented.
-!!! warning
- **We do not support async Lambda handler** - Lambda handler itself must be synchronous
-You can trace asynchronous functions and generator functions (including context managers) using `capture_method`.
-=== "Async"
+ export const handler = async (_event: any, _context: any) => {
+ ...
+ }
+ ```
- ```python hl_lines="7"
- import asyncio
- import contextlib
- from aws_lambda_powertools import Tracer
+=== "Decorator"
- tracer = Tracer()
+ ```typescript hl_lines="6"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
- @tracer.capture_method
- async def collect_payment():
- ...
- ```
+ const tracer = Tracer();
-=== "Context manager"
+ class Lambda {
+ @tracer.captureMethod()
+ public getChargeId(): string {
+ ...
+ return 'foo bar'
+ }
- ```python hl_lines="7-8"
- import asyncio
- import contextlib
- from aws_lambda_powertools import Tracer
+ public handler(event: any, context: any) {
+ const chargeId = this.getChargeId();
+ const payment = collectPayment(chargeId);
+ ...
+ }
+ }
+
+ export const handlerClass = new Lambda();
+ export const getChargeId = handlerClass.getChargeId;
+ export const handler = handlerClass.handler;
+ ```
- tracer = Tracer()
+=== "Manual"
- @contextlib.contextmanager
- @tracer.capture_method
- def collect_payment_ctxman():
- yield result
+ ```typescript hl_lines="2 8-9 15 18 22"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
+ import { Segment } from 'aws-xray-sdk-core';
+
+ const tracer = new Tracer({ serviceName: 'my-service' });
+
+ const chargeId = async () => {
+ // Create subsegment & set it as active
+ const subsegment = new Subsegment(`### chargeId`);
+ tracer.setSegment(subsegment);
+
+ let res;
+ try {
+ res = await someLogic(); // Do something
+ // Add the response as metadata
+ tracer.putMetadata(`chargeId response`, data);
+ } catch (err) {
+ // Add the error as metadata
+ subsegment.addError(err, false);
+ }
+
+ // Close subsegment
+ subsegment.close();
+
+ return res;
+ }
+
+ export const handler = async (_event: any, _context: any) => {
+ const chargeId = this.getChargeId();
+ const payment = collectPayment(chargeId);
...
+ }
```
-=== "Generators"
-
- ```python hl_lines="9"
- import asyncio
- import contextlib
- from aws_lambda_powertools import Tracer
+## Advanced
- tracer = Tracer()
+### Patching AWS SDK clients
- @tracer.capture_method
- def collect_payment_gen():
- yield result
- ...
- ```
+Tracer can patch [AWS SDK clients](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-awssdkclients.html) and create traces when your application makes calls to AWS services.
-The decorator will detect whether your function is asynchronous, a generator, or a context manager and adapt its behaviour accordingly.
+!!! info
+ The following snippet assumes you are using [**AWS SDK v3** for JavaScript](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/)
-=== "app.py"
+You can patch any AWS SDK clients by calling `captureAWSv3Client` method:
- ```python
- @tracer.capture_lambda_handler
- def handler(evt, ctx):
- asyncio.run(collect_payment())
+=== "index.ts"
- with collect_payment_ctxman as result:
- do_something_with(result)
+ ```typescript hl_lines="6"
+ import { S3Client } from "@aws-sdk/client-s3";
+ import { Tracer } from '@aws-lambda-powertools/tracer';
- another_result = list(collect_payment_gen())
+ const tracer = new Tracer();
+ const client = new S3Client({});
+ tracer.captureAWSv3Client(client);
```
-## Advanced
+!!! info
+ The following two snippets assume you are using [**AWS SDK v2** for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/welcome.html)
-### Patching modules
+You can patch all AWS SDK clients by calling `captureAWS` method:
-Tracer automatically patches all [supported libraries by X-Ray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-python-patching.html) during initialization, by default. Underneath, AWS X-Ray SDK checks whether a supported library has been imported before patching.
+=== "index.ts"
+
+ ```typescript hl_lines="4"
+ import { Tracer } from '@aws-lambda-powertools/tracer';
-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:
+ const tracer = new Tracer();
+ const AWS = tracer.captureAWS(require('aws-sdk'));
+ ```
-=== "app.py"
+If you're looking to shave a few microseconds, or milliseconds depending on your function memory configuration, you can patch specific clients using `captureAWSClient`:
- ```python hl_lines="7"
- import boto3
- import requests
+=== "index.ts"
- from aws_lambda_powertools import Tracer
+ ```typescript hl_lines="5"
+ import { S3 } from "aws-sdk";
+ import { Tracer } from '@aws-lambda-powertools/tracer';
- modules_to_be_patched = ["boto3", "requests"]
- tracer = Tracer(patch_modules=modules_to_be_patched)
+ const tracer = new Tracer();
+ const s3 = tracer.captureAWSClient(new S3({ apiVersion: "2006-03-01" }));
```
### Disabling response auto-capture
-> New in 1.9.0
-
-Use **`capture_response=False`** parameter in both `capture_lambda_handler` and `capture_method` decorators to instruct Tracer **not** to serialize function responses as metadata.
+Use **`POWERTOOLS_TRACER_CAPTURE_RESPONSE=false`** environment variable to instruct Tracer **not** to serialize function responses as metadata.
!!! info "This is commonly useful in three scenarios"
@@ -217,159 +286,29 @@ Use **`capture_response=False`** parameter in both `capture_lambda_handler` and
2. You might manipulate **streaming objects that can be read only once**; this prevents subsequent calls from being empty
3. You might return **more than 64K** of data _e.g., `message too long` error_
-=== "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()
- ```
-=== "streaming_object_scenario.py"
-
- ```python hl_lines="3"
- from aws_lambda_powertools import Tracer
-
- @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
- ```
-
### Disabling exception auto-capture
-> New in 1.10.0
-
-Use **`capture_error=False`** parameter in both `capture_lambda_handler` and `capture_method` decorators to instruct Tracer **not** to serialize exceptions as metadata.
+Use **`POWERTOOLS_TRACER_CAPTURE_ERROR=false`** environment variable to instruct Tracer **not** to serialize exceptions as metadata.
!!! info "Commonly useful in one scenario"
1. You might **return sensitive** information from exceptions, stack traces you might not control
-=== "sensitive_data_exception.py"
-
- ```python hl_lines="3 5"
- 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...")
- ```
-
-### 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.
-
-=== "aiohttp_example.py"
-
- ```python hl_lines="5 10"
- import asyncio
- import aiohttp
-
- from aws_lambda_powertools import Tracer
- from aws_lambda_powertools.tracing import aiohttp_trace_config
-
- tracer = Tracer()
-
- 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
- ```
-
### Escape hatch mechanism
-You can use `tracer.provider` attribute to access all methods provided by AWS X-Ray `xray_recorder` object.
-
-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).
-
-=== "escape_hatch_context_manager_example.py"
+You can use `tracer.provider` attribute to access all methods provided by the [AWS X-Ray SDK](https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/AWSXRay.html).
- ```python hl_lines="7"
- 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)
- ```
+This is useful when you need a feature available in X-Ray that is not available in the Tracer utility, for example [SQL queries tracing](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-sqlclients.html), or [a custom logger](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-configuration.html#xray-sdk-nodejs-configuration-logging).
-### Concurrent asynchronous functions
-
-!!! warning
- [As of now, X-Ray SDK will raise an exception when async functions are run and traced concurrently](https://github.com/aws/aws-xray-sdk-python/issues/164)
-
-A safe workaround mechanism is to use `in_subsegment_async` available via Tracer escape hatch (`tracer.provider`).
-
-=== "concurrent_async_workaround.py"
-
- ```python hl_lines="6 7 12 15 17"
- 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())
- ...
- ```
-
-### Reusing Tracer across your code
-
-Tracer keeps a copy of its configuration after the first initialization. This is useful for scenarios where you want to use Tracer in more than one location across your code base.
-
-!!! warning
- When reusing Tracer in Lambda Layers, or in multiple modules, **do not set `auto_patch=False`**, because import order matters.
-
- This can result in the first Tracer config being inherited by new instances, and their modules not being patched.
-
-=== "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)
- ```
-=== "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
+=== "index.ts"
- tracer = Tracer(service="payment")
+ ```typescript hl_lines="6"
+ import { Logger } from '@aws-lambda-powertools/logger';
+ import { Tracer } from '@aws-lambda-powertools/tracer';
- @tracer.capture_method
- def collect_payment(charge_id: str):
- ...
+ const logger = new Logger();
+ const tracer = new Tracer()
+ tracer.provider.setLogger(logger)
```
## Testing your code
@@ -378,6 +317,6 @@ Tracer is disabled by default when not running in the AWS Lambda environment - T
## Tips
-* Use annotations on key operations to slice and dice traces, create unique views, and create metrics from it via Trace Groups
-* Use a namespace when adding metadata to group data more easily
-* Annotations and metadata are added to the current subsegment opened. If you want them in a specific subsegment, use a [context manager](https://github.com/aws/aws-xray-sdk-python/#start-a-custom-segmentsubsegment) via the escape hatch mechanism
+- Use annotations on key operations to slice and dice traces, create unique views, and create metrics from it via Trace Groups
+- Use a namespace when adding metadata to group data more easily
+- Annotations and metadata are added to the current subsegment opened. If you want them in a specific subsegment, [create one](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-subsegments.html#xray-sdk-nodejs-subsegments-lambda) via the escape hatch mechanism
diff --git a/packages/tracing/package.json b/packages/tracing/package.json
index 70e22f248c..582d1777dd 100644
--- a/packages/tracing/package.json
+++ b/packages/tracing/package.json
@@ -26,8 +26,8 @@
},
"homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/master/packages/tracer#readme",
"license": "MIT",
- "main": "./lib/index.js",
- "types": "./lib/index.d.ts",
+ "main": "./lib/packages/tracing/src/index.js",
+ "types": "./lib/packages/tracing/src/index.d.ts",
"devDependencies": {
"@types/aws-lambda": "^8.10.72",
"@types/jest": "^27.0.0",
diff --git a/packages/tracing/src/Tracer.ts b/packages/tracing/src/Tracer.ts
index ae1bb434bf..849d8328cb 100644
--- a/packages/tracing/src/Tracer.ts
+++ b/packages/tracing/src/Tracer.ts
@@ -5,6 +5,85 @@ import { HandlerMethodDecorator, TracerOptions, MethodDecorator } from '../types
import { ProviderService, ProviderServiceInterface } from './provider';
import { Segment, Subsegment } from 'aws-xray-sdk-core';
+/**
+ * ## Intro
+ * Tracer is an opinionated thin wrapper for [AWS X-Ray SDK for Node.js](https://github.com/aws/aws-xray-sdk-node).
+ *
+ * Tracing data can be visualized through AWS X-Ray Console.
+ *
+ * ## Key features
+ * * Auto capture cold start as annotation, and responses or full exceptions as metadata
+ * * Auto-disable when not running in AWS Lambda environment
+ * * Support tracing functions via decorators, middleware, and manual instrumentation
+ * * Support tracing AWS SDK v2 and v3 via AWS X-Ray SDK for Node.js
+ *
+ * ## Usage
+ *
+ * ### Functions usage with middlewares
+ * TBD
+ *
+ * ### Object oriented usage with decorators
+ *
+ * If you use TypeScript Classes to wrap your Lambda handler you can use the [@tracer.captureLambdaHanlder()](./_aws_lambda_powertools_tracer.Tracer.html#captureLambdaHanlder) decorator to automatically:
+ * * handle the subsegment lifecycle
+ * * add the `ColdStart` annotation
+ * * add the function response as metadata
+ * * add the function error as metadata (if any)
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * // FYI: Decorator might not render properly in VSCode mouse over due to https://github.com/microsoft/TypeScript/issues/39371 and might show as *@tracer* instead of `@tracer.captureLambdaHanlder`
+ *
+ * class Lambda {
+ * @tracer.captureLambdaHanlder()
+ * public handler(event: any, context: any) {
+ * ...
+ * }
+ * }
+ *
+ * export const handlerClass = new Lambda();
+ * export const handler = handlerClass.handler;
+ * ```
+ *
+ * ### Functions usage with manual instrumentation
+ *
+ * If you prefer to manually instrument your Lambda handler you can use the methods in the tracer class directly.
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ * import { Segment } from 'aws-xray-sdk-core';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * export const handler = async (_event: any, context: any) => {
+ * // Create subsegment & set it as active
+ * const subsegment = new Subsegment(`## ${context.functionName}`);
+ * tracer.setSegment(subsegment);
+ * // Add the ColdStart annotation
+ * this.putAnnotation('ColdStart', tracer.coldStart);
+ *
+ * let res;
+ * try {
+ * res = await someLogic(); // Do something
+ * // Add the response as metadata
+ * tracer.putMetadata(`${context.functionName} response`, data);
+ * } catch (err) {
+ * // Add the error as metadata
+ * subsegment.addError(err, false);
+ * }
+ *
+ * // Close subsegment
+ * subsegment.close();
+ *
+ * return res;
+ * }
+ * ```
+ */
class Tracer implements TracerInterface {
public static coldStart: boolean = true;
@@ -27,24 +106,125 @@ class Tracer implements TracerInterface {
this.provider = new ProviderService();
}
+ /**
+ * Patch all AWS SDK v2 clients and create traces when your application makes calls to AWS services.
+ *
+ * If you want to patch a specific client use {@link captureAWSClient} and if you are using AWS SDK v3 use {@link captureAWSv3Client} instead.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-awssdkclients.html
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ * const AWS = tracer.captureAWS(require('aws-sdk'));
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * ...
+ * }
+ * ```
+ *
+ * @param aws - AWS SDK v2 import
+ * @returns AWS - Instrumented AWS SDK
+ */
public captureAWS(aws: T): T {
if (this.tracingEnabled === false) return aws;
return this.provider.captureAWS(aws);
}
+ /**
+ * Patch a specific AWS SDK v2 client and create traces when your application makes calls to that AWS service.
+ *
+ * If you want to patch all clients use {@link captureAWS} and if you are using AWS SDK v3 use {@link captureAWSv3Client} instead.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-awssdkclients.html
+ *
+ * @example
+ * ```typescript
+ * import { S3 } from "aws-sdk";
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ * tracer.captureAWS(require('aws-sdk'));
+ * const s3 = tracer.captureAWSClient(new S3({ apiVersion: "2006-03-01" }));
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * ...
+ * }
+ * ```
+ *
+ * @param service - AWS SDK v2 client
+ * @returns service - Instrumented AWS SDK v2 client
+ */
public captureAWSClient(service: T): T {
if (this.tracingEnabled === false) return service;
return this.provider.captureAWSClient(service);
}
+ /**
+ * Patch an AWS SDK v3 client and create traces when your application makes calls to that AWS service.
+ *
+ * If you are using AWS SDK v2 use {@link captureAWSClient} instead.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-awssdkclients.html
+ *
+ * @example
+ * ```typescript
+ * import { S3Client } from "@aws-sdk/client-s3";
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ * const client = new S3Client({});
+ * tracer.captureAWSv3Client(client);
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * ...
+ * }
+ * ```
+ *
+ * @param service - AWS SDK v3 client
+ * @returns service - Instrumented AWS SDK v3 client
+ */
public captureAWSv3Client(service: T): T {
if (this.tracingEnabled === false) return service;
return this.provider.captureAWSv3Client(service);
}
+ /**
+ * A decorator automating capture of metadata and annotations on segments or subsegments for a Lambda Handler.
+ *
+ * Using this decorator on your handler function will automatically:
+ * * handle the subsegment lifecycle
+ * * add the `ColdStart` annotation
+ * * add the function response as metadata
+ * * add the function error as metadata (if any)
+ *
+ * Note: Currently TypeScript only supports decorators on classes and methods. If you are using the
+ * function syntax, you should use the middleware instead.
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * class Lambda {
+ * @tracer.captureLambdaHanlder()
+ * public handler(event: any, context: any) {
+ * ...
+ * }
+ * }
+ *
+ * export const handlerClass = new Lambda();
+ * export const handler = handlerClass.handler;
+ * ```
+ *
+ * @decorator Class
+ */
public captureLambdaHanlder(): HandlerMethodDecorator {
return (target, _propertyKey, descriptor) => {
const originalMethod = descriptor.value;
@@ -76,6 +256,41 @@ class Tracer implements TracerInterface {
};
}
+ /**
+ * A decorator automating capture of metadata and annotations on segments or subsegments for an arbitrary function.
+ *
+ * Using this decorator on your function will automatically:
+ * * handle the subsegment lifecycle
+ * * add the function response as metadata
+ * * add the function error as metadata (if any)
+ *
+ * Note: Currently TypeScript only supports decorators on classes and methods. If you are using the
+ * function syntax, you should use the middleware instead.
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * class Lambda {
+ * @tracer.captureMethod()
+ * public myMethod(param: any) {
+ * ...
+ * }
+ *
+ * public handler(event: any, context: any) {
+ * ...
+ * }
+ * }
+ *
+ * export const handlerClass = new Lambda();
+ * export const myMethod = handlerClass.myMethod;
+ * export const handler = handlerClass.handler;
+ * ```
+ *
+ * @decorator Class
+ */
public captureMethod(): MethodDecorator {
return (target, _propertyKey, descriptor) => {
const originalMethod = descriptor.value;
@@ -106,6 +321,28 @@ class Tracer implements TracerInterface {
};
}
+ /**
+ * Get the active segment or subsegment in the current scope.
+ *
+ * Usually you won't need to call this method unless you are manipulating segments using the escape hatch pattern.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-segments
+ * @see https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/#escape-hatch-mechanism
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * const currentSegment = tracer.getSegment();
+ * ... // Do something with segment
+ * }
+ * ```
+ *
+ * @returns segment - The active segment or subsegment in the current scope.
+ */
public getSegment(): Segment | Subsegment {
const segment = this.provider.getSegment();
if (segment === undefined) {
@@ -115,6 +352,17 @@ class Tracer implements TracerInterface {
return segment;
}
+ /**
+ * Retrieve the current value of `ColdStart`.
+ *
+ * If Tracer has been initialized outside of the Lambda handler then the same instance
+ * of Tracer will be reused throghout the lifecycle of that same Lambda execution environment
+ * and this method will return `false` after the first invocation.
+ *
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html
+ *
+ * @returns boolean - true if is cold start otherwise false
+ */
public static isColdStart(): boolean {
if (Tracer.coldStart === true) {
Tracer.coldStart = false;
@@ -125,6 +373,25 @@ class Tracer implements TracerInterface {
return false;
}
+ /**
+ * Adds annotation to existing segment or subsegment.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-segment.html#xray-sdk-nodejs-segment-annotations
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * tracer.putAnnotation('PaymentStatus', "SUCCESS");
+ * }
+ * ```
+ *
+ * @param key - Annotation key
+ * @param value - Value for annotation
+ */
public putAnnotation(key: string, value: string | number | boolean): void {
if (this.tracingEnabled === false) return;
@@ -137,6 +404,27 @@ class Tracer implements TracerInterface {
document?.addAnnotation(key, value);
}
+ /**
+ * Adds metadata to existing segment or subsegment.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-segment.html#xray-sdk-nodejs-segment-metadata
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * const res = someLogic();
+ * tracer.putMetadata('PaymentResponse', res);
+ * }
+ * ```
+ *
+ * @param key - Metadata key
+ * @param value - Value for metadata
+ * @param timestamp - Namespace that metadata will lie under, if none is passed it will use the serviceName
+ */
public putMetadata(key: string, value: unknown, namespace?: string | undefined): void {
if (this.tracingEnabled === false) return;
@@ -151,10 +439,40 @@ class Tracer implements TracerInterface {
document?.addMetadata(key, value, namespace);
}
+ /**
+ * Sets the passed subsegment as the current active subsegment.
+ *
+ * If you are using a middleware or a decorator this is done automatically for you.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs-subsegments.html
+ *
+ * @example
+ * ```typescript
+ * import { Tracer } from '@aws-lambda-powertools/tracer';
+ * import { Segment } from 'aws-xray-sdk-core';
+ *
+ * const tracer = new Tracer({ serviceName: 'my-service' });
+ *
+ * export const handler = async (_event: any, _context: any) => {
+ * const subsegment = new Subsegment('### foo.bar');
+ * tracer.setSegment(subsegment);
+ * }
+ * ```
+ *
+ * @param segment - Subsegment to set as the current segment
+ */
public setSegment(segment: Segment | Subsegment): void {
return this.provider.setSegment(segment);
}
+ /**
+ * Add an error to the current segment or subsegment as metadata.
+ * Used internally by decoratorators and middlewares.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-errors
+ *
+ * @param error - Error to serialize as metadata
+ */
private addErrorAsMetadata(error: Error): void {
const subsegment = this.getSegment();
if (this.captureError === false) {
@@ -166,6 +484,15 @@ class Tracer implements TracerInterface {
subsegment.addError(error, false);
}
+ /**
+ * Add an data to the current segment or subsegment as metadata.
+ * Used internally by decoratorators and middlewares.
+ *
+ * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-errors
+ *
+ * @param data - Data to serialize as metadata
+ * @param methodName - Name of the method that is being traced
+ */
private addResponseAsMetadata(data?: unknown, methodName?: string): void {
if (data === undefined || this.captureResponse === false || this.tracingEnabled === false) {
return;
@@ -174,32 +501,62 @@ class Tracer implements TracerInterface {
this.putMetadata(`${methodName} response`, data);
}
+ /**
+ * Add ColdStart annotation to the current segment or subsegment.
+ * Used internally by decoratorators and middlewares.
+ */
private annotateColdStart(): void {
if (Tracer.isColdStart()) {
this.putAnnotation('ColdStart', true);
}
}
+ /**
+ * Getter for `customConfigService`.
+ * Used internally during initialization.
+ */
private getCustomConfigService(): ConfigServiceInterface | undefined {
return this.customConfigService;
}
+ /**
+ * Getter for `envVarsService`.
+ * Used internally during initialization.
+ */
private getEnvVarsService(): EnvironmentVariablesService {
return this.envVarsService;
}
+ /**
+ * Determine if we are running in a Lambda execution environment.
+ * Used internally during initialization.
+ */
private isLambdaExecutionEnv(): boolean {
return this.getEnvVarsService()?.getAwsExecutionEnv() !== '';
}
+ /**
+ * Determine if we are running inside a SAM CLI process.
+ * Used internally during initialization.
+ */
private isLambdaSamCli(): boolean {
return this.getEnvVarsService()?.getSamLocal() !== '';
}
+ /**
+ * Validate that the service name provided is valid.
+ * Used internally during initialization.
+ *
+ * @param serviceName - Service name to validate
+ */
private isValidServiceName(serviceName?: string): boolean {
return typeof serviceName === 'string' && serviceName.trim().length > 0;
}
+ /**
+ * Setter for `captureError` based on configuration passed and environment variables.
+ * Used internally during initialization.
+ */
private setCaptureError(): void {
const customConfigValue = this.getCustomConfigService()?.getTracingCaptureError();
if (customConfigValue !== undefined && customConfigValue.toLowerCase() === 'false') {
@@ -216,6 +573,10 @@ class Tracer implements TracerInterface {
}
}
+ /**
+ * Setter for `captureResponse` based on configuration passed and environment variables.
+ * Used internally during initialization.
+ */
private setCaptureResponse(): void {
const customConfigValue = this.getCustomConfigService()?.getTracingCaptureResponse();
if (customConfigValue !== undefined && customConfigValue.toLowerCase() === 'false') {
@@ -232,14 +593,30 @@ class Tracer implements TracerInterface {
}
}
+ /**
+ * Setter for `customConfigService` based on configuration passed.
+ * Used internally during initialization.
+ *
+ * @param customConfigService - Custom configuration service to use
+ */
private setCustomConfigService(customConfigService?: ConfigServiceInterface): void {
this.customConfigService = customConfigService ? customConfigService : undefined;
}
+ /**
+ * Setter and initializer for `envVarsService`.
+ * Used internally during initialization.
+ */
private setEnvVarsService(): void {
this.envVarsService = new EnvironmentVariablesService();
}
+ /**
+ * Method that reconciles the configuration passed with the environment variables.
+ * Used internally during initialization.
+ *
+ * @param options - Configuration passed to the tracer
+ */
private setOptions(options: TracerOptions): Tracer {
const {
enabled,
@@ -257,6 +634,12 @@ class Tracer implements TracerInterface {
return this;
}
+ /**
+ * Setter for `customConfigService` based on configurations passed and environment variables.
+ * Used internally during initialization.
+ *
+ * @param serviceName - Name of the service to use
+ */
private setServiceName(serviceName?: string): void {
if (serviceName !== undefined && this.isValidServiceName(serviceName)) {
this.serviceName = serviceName;
@@ -279,6 +662,12 @@ class Tracer implements TracerInterface {
}
}
+ /**
+ * Setter for `tracingEnabled` based on configurations passed and environment variables.
+ * Used internally during initialization.
+ *
+ * @param enabled - Whether or not tracing is enabled
+ */
private setTracingEnabled(enabled?: boolean): void {
if (enabled !== undefined && enabled === false) {
this.tracingEnabled = enabled;
diff --git a/packages/tracing/tsconfig.json b/packages/tracing/tsconfig.json
index 30559ec1d7..d28abaa6d5 100644
--- a/packages/tracing/tsconfig.json
+++ b/packages/tracing/tsconfig.json
@@ -7,6 +7,7 @@
"declaration": true,
"declarationMap": true,
"outDir": "lib",
+ "removeComments": false,
"strict": true,
"inlineSourceMap": true,
"moduleResolution": "node",
diff --git a/packages/tracing/types/Tracer.ts b/packages/tracing/types/Tracer.ts
index 7ff38ee1c4..74f966a0f9 100644
--- a/packages/tracing/types/Tracer.ts
+++ b/packages/tracing/types/Tracer.ts
@@ -2,6 +2,22 @@ import { ConfigServiceInterface } from '../src/config';
import { Handler } from 'aws-lambda';
import { LambdaInterface } from '../examples/utils/lambda';
+/**
+ * Options for the tracer class to be used during initialization.
+ *
+ * Usage:
+ * @example
+ * ```typescript
+ * const customConfigService: ConfigServiceInterface;
+ * const tracerOptions: TracerOptions = {
+ * enabled?: true,
+ * serviceName?: 'my-service',
+ * customConfigService?: customConfigService, // Only needed for advanced uses
+ * };
+ *
+ * const tracer = new Tracer(tracerOptions);
+ * ```
+ */
type TracerOptions = {
enabled?: boolean
serviceName?: string