Tracer is an opinionated thin wrapper for AWS X-Ray SDK for Node.js.
Key features¶fetch
and generating segments for each request.Tracer relies on AWS X-Ray SDK over OpenTelemetry Distro (ADOT) for optimal cold start (lower latency).
Installation¶Install the library in your project:
npm install @aws-lambda-powertools/tracer
Usage¶
The Tracer
utility must always be instantiated outside of the Lambda handler. In doing this, subsequent invocations processed by the same instance of your function can reuse these resources. This saves cost by reducing function run time. In addition, Tracer
can track cold start and annotate the traces accordingly.
handler.ts
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
export const handler = async (_event, _context): Promise<void> => {
tracer.getSegment();
};
Using with ESM?¶
Tracer relies on the AWS X-Ray SDK for Node.js, which is distributed as a CommonJS module and uses require
.
To use it in an ESM project, you can instruct your bundler to use the require
syntax for specific dependencies while using ESM for everything else. This is commonly known as polyfill.
esbuild
With AWS CDKWith AWS SAM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
import { Stack, type StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
export class MyStack extends Stack {
public constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const handler = new NodejsFunction(this, 'helloWorldFunction', {
runtime: Runtime.NODEJS_22_X,
handler: 'handler',
entry: 'src/index.ts',
bundling: {
format: OutputFormat.ESM,
minify: true,
esbuildArgs: {
"--tree-shaking": "true",
},
banner:
"import { createRequire } from 'module';const require = createRequire(import.meta.url);", // (1)!
},
});
}
}
esbuild
will include this arbitrary code at the top of your bundle to maximize CommonJS compatibility (require
keyword).1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Handler: src/index.handler
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: 'ES2020'
Sourcemap: true
Format: esm
EntryPoints:
- src/index.ts
Banner:
js: "import { createRequire } from 'module';const require = createRequire(import.meta.url);" # (1)!
esbuild
will include this arbitrary code at the top of your bundle to maximize CommonJS compatibility (require
keyword).The library has three optional settings. You can set them as environment variables, or pass them in the constructor:
Setting Description Environment variable Default Allowed Values Example Constructor parameter Service name Sets an annotation with the name of the service across all tracesPOWERTOOLS_SERVICE_NAME
service_undefined
Any string serverlessAirline
serviceName
Tracing enabled Enables or disables tracing. POWERTOOLS_TRACE_ENABLED
true
true
or false
false
enabled
Capture HTTPs Requests Defines whether HTTPs requests will be traced or not POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS
true
true
or false
false
captureHTTPsRequests
Capture Response Defines whether functions responses are serialized as metadata POWERTOOLS_TRACER_CAPTURE_RESPONSE
true
true
or false
false
captureResult
Capture Errors Defines whether functions errors are serialized as metadata POWERTOOLS_TRACER_CAPTURE_ERROR
true
true
or false
false
N/A Example using AWS Serverless Application Model (SAM)¶
The Tracer
utility is instantiated outside of the Lambda handler. In doing this, the same instance can be used across multiple invocations inside the same execution environment. This allows Tracer
to be aware of things like whether or not a given invocation had a cold start or not.
handler.tstemplate.yml
import { Tracer } from '@aws-lambda-powertools/tracer';
// Tracer parameter fetched from the environment variables (see template.yaml tab)
const tracer = new Tracer();
tracer.getSegment();
// You can also pass the parameter in the constructor
// const tracer = new Tracer({
// serviceName: 'serverlessAirline'
// });
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Tracing: Active
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: serverlessAirline
Lambda handler¶
You can quickly start by importing the Tracer
class, initialize it outside the Lambda handler, and instrument your function.
Middy MiddlewareDecoratorManual
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import { Tracer } from '@aws-lambda-powertools/tracer';
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
import middy from '@middy/core';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
const lambdaHandler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
tracer.putAnnotation('successfulBooking', true);
};
// Wrap the handler with middy
export const handler = middy(lambdaHandler)
// Use the middleware by passing the Tracer instance as a parameter
.use(captureLambdaHandler(tracer));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
// Decorate your handler class method
@tracer.captureLambdaHandler()
public async handler(_event: unknown, _context: unknown): Promise<void> {
tracer.getSegment();
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass); // (1)
this
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
import { Tracer } from '@aws-lambda-powertools/tracer';
import type { Subsegment } from 'aws-xray-sdk-core';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
export const handler = async (
_event: unknown,
_context: unknown
): Promise<unknown> => {
const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda)
let subsegment: Subsegment | undefined;
if (segment) {
// Create subsegment for the function & set it as active
subsegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
tracer.setSegment(subsegment);
}
// Annotate the subsegment with the cold start & serviceName
tracer.annotateColdStart();
tracer.addServiceNameAnnotation();
try {
// Add the response as metadata
tracer.addResponseAsMetadata({}, process.env._HANDLER);
} catch (err) {
// Add the error as metadata
tracer.addErrorAsMetadata(err as Error);
throw err;
} finally {
if (segment && subsegment) {
// Close subsegment (the AWS Lambda one is closed automatically)
subsegment.close();
// Set back the facade segment as active again
tracer.setSegment(segment);
}
}
return {};
};
When using the captureLambdaHandler
decorator or middleware, Tracer performs these additional tasks to ease operations:
ColdStart
annotation to easily filter traces that have had an initialization overheadService
annotation to easily filter traces that have a specific service nameAnnotations are key-values associated with traces and indexed by AWS X-Ray. You can use them to filter traces and to create Trace Groups to slice and dice your transactions.
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.
AnnotationsMetadata
You can add annotations using putAnnotation
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
export const handler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
const handlerSegment = tracer.getSegment()?.addNewSubsegment('### handler');
handlerSegment && tracer.setSegment(handlerSegment); // (1)!
tracer.putAnnotation('successfulBooking', true);
handlerSegment?.close();
handlerSegment && tracer.setSegment(handlerSegment?.parent); // (2)!
};
facade
. This segment cannot be annotated or modified by your code, so you need to create a new subsegment. This is done automatically by Tracer when using the decorator or middleware patternsYou can add metadata using putMetadata
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
export const handler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
const handlerSegment = tracer.getSegment()?.addNewSubsegment('### handler');
handlerSegment && tracer.setSegment(handlerSegment); // (1)!
tracer.putMetadata('paymentResponse', {
foo: 'bar',
});
handlerSegment?.close();
handlerSegment && tracer.setSegment(handlerSegment?.parent); // (2)!
};
facade
. This segment cannot be modified by your code, so you need to create a new subsegment. This is done automatically by Tracer when using the decorator or middleware patternsYou can trace other class methods using the captureMethod
decorator or any arbitrary asynchronous function using manual instrumentation.
DecoratorManual
Note
The class method decorators in this project follow the experimental implementation enabled via the experimentalDecorators
compiler option in TypeScript.
Additionally, they are implemented to decorate async methods. When decorating a synchronous one, the decorator replaces its implementation with an async one causing the caller to have to await
the now decorated method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
// Decorate your class method
@tracer.captureMethod() // (1)
public async getChargeId(): Promise<string> {
/* ... */
return 'foo bar';
}
public async handler(_event: unknown, _context: unknown): Promise<void> {
await this.getChargeId();
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass); // (2)
subSegmentName
to the decorator, like: @tracer.captureMethod({ subSegmentName: '### myCustomMethod' })
.this
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
import { Tracer } from '@aws-lambda-powertools/tracer';
import type { Subsegment } from 'aws-xray-sdk-core';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
const getChargeId = async (): Promise<unknown> => {
const parentSubsegment = tracer.getSegment(); // This is the subsegment currently active
let subsegment: Subsegment | undefined;
if (parentSubsegment) {
// Create subsegment for the function & set it as active
subsegment = parentSubsegment.addNewSubsegment('### chargeId');
tracer.setSegment(subsegment);
}
let res: unknown;
try {
/* ... */
// Add the response as metadata
tracer.addResponseAsMetadata(res, 'chargeId');
} catch (err) {
// Add the error as metadata
tracer.addErrorAsMetadata(err as Error);
throw err;
}
if (parentSubsegment && subsegment) {
// Close subsegment (the AWS Lambda one is closed automatically)
subsegment.close();
// Set the facade segment as active again
tracer.setSegment(parentSubsegment);
}
return res;
};
export const handler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
await getChargeId();
};
Patching AWS SDK clients¶
Tracer can patch any AWS SDK clients and create traces when your application makes calls to AWS services.
You can patch any AWS SDK clients by calling the captureAWSv3Client
method:
index.ts
import { Tracer } from '@aws-lambda-powertools/tracer';
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
// Instrument the AWS SDK client
const client = tracer.captureAWSv3Client(new SecretsManagerClient({}));
export default client;
You can patch all AWS SDK v2 clients by calling the captureAWS
method:
index.ts
import { Tracer } from '@aws-lambda-powertools/tracer';
import AWS from 'aws-sdk';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
// Instrument all AWS SDK clients created from this point onwards
tracer.captureAWS(AWS);
// Create a new client which will be automatically instrumented
const client = new AWS.SecretsManager();
export default client;
If you're looking to shave a few microseconds, or milliseconds depending on your function memory configuration, you can patch only specific AWS SDK v2 clients using captureAWSClient
:
index.ts
import { Tracer } from '@aws-lambda-powertools/tracer';
import { S3 } from 'aws-sdk';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
// Instrument the AWS SDK client
const client = tracer.captureAWSClient(new S3());
export default client;
Tracing HTTP requests¶
When your function makes outgoing requests to APIs, Tracer automatically traces those calls and adds the API to the service graph as a downstream service.
You can opt-out from this feature by setting the POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS=false
environment variable or by passing the captureHTTPsRequests: false
option to the Tracer
constructor.
Info
The following snippet shows how to trace fetch
requests, but you can use any HTTP client library built on top it, or on http, and https. Support to 3rd party HTTP clients is provided on a best effort basis.
index.ts
import { Tracer } from '@aws-lambda-powertools/tracer';
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
import middy from '@middy/core';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
export const handler = middy(
async (_event: unknown, _context: unknown): Promise<void> => {
await fetch('https://httpbin.org/status/200');
}
).use(captureLambdaHandler(tracer));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
{
"id": "22883fbc730e3a0b",
"name": "## index.handler",
"start_time": 1647956168.22749,
"end_time": 1647956169.0679862,
"subsegments": [
{
"id": "ab82ab2b7d525d8f",
"name": "httpbin.org",
"start_time": 1647956168.407,
"end_time": 1647956168.945,
"http": {
"request": {
"url": "https://httpbin.org/status/200",
"method": "GET"
},
"response": {
"status": 200,
"content_length": 0
}
},
"namespace": "remote"
}
]
}
Advanced¶ Disabling response auto-capture¶
Use POWERTOOLS_TRACER_CAPTURE_RESPONSE=false
environment variable to instruct Tracer not to serialize function responses as metadata.
This is commonly useful in three scenarios
message too long
errorAlternatively, use the captureResponse: false
option in both tracer.captureLambdaHandler()
and tracer.captureMethod()
decorators, or use the same option in the Middy captureLambdaHandler
middleware to instruct Tracer not to serialize function responses as metadata.
method.tshandler.tsmiddy.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
@tracer.captureMethod({ captureResponse: false })
public async getChargeId(): Promise<string> {
/* ... */
return 'foo bar';
}
public async handler(_event: unknown, _context: unknown): Promise<void> {
/* ... */
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass);
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
@tracer.captureLambdaHandler({ captureResponse: false })
public async handler(_event: unknown, _context: unknown): Promise<void> {
tracer.getSegment();
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import { Tracer } from '@aws-lambda-powertools/tracer';
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
import middy from '@middy/core';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
const lambdaHandler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
/* ... */
};
// Wrap the handler with middy
export const handler = middy(lambdaHandler)
// Use the middleware by passing the Tracer instance as a parameter,
// but specify the captureResponse option as false.
.use(captureLambdaHandler(tracer, { captureResponse: false }));
Disabling errors auto-capture¶
Use POWERTOOLS_TRACER_CAPTURE_ERROR=false
environment variable to instruct Tracer not to serialize errors as metadata.
Commonly useful in one scenario
Tracer exposes a getRootXrayTraceId()
method that allows you to retrieve the AWS X-Ray Root Trace ID corresponds to the current function execution.
This is commonly useful in two scenarios
index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
import { Logger } from '@aws-lambda-powertools/logger';
import { Tracer } from '@aws-lambda-powertools/tracer';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
const logger = new Logger({ serviceName: 'serverlessAirline' });
export const handler = async (): Promise<unknown> => {
try {
throw new Error('Something went wrong');
} catch (error) {
logger.error('An error occurred', { error });
const rootTraceId = tracer.getRootXrayTraceId();
// Example of returning an error response
return {
statusCode: 500,
body: `Internal Error - Please contact support and quote the following id: ${rootTraceId}`,
headers: { _X_AMZN_TRACE_ID: rootTraceId },
};
}
};
Escape hatch mechanism¶
You can use tracer.provider
attribute to access a subset of the methods provided by the AWS X-Ray SDK.
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, or a custom logger.
index.ts
import { Logger } from '@aws-lambda-powertools/logger';
import { Tracer } from '@aws-lambda-powertools/tracer';
const serviceName = 'serverlessAirline';
const logger = new Logger({ serviceName: serviceName });
const tracer = new Tracer({ serviceName: serviceName });
tracer.provider.setLogger(logger);
If you need to access a method that is not available you can import it directly from the AWS X-Ray SDK for Node.js. Compatibility with the Tracer utility is not guaranteed.
Testing your code¶Tracer is disabled by default when not running in the AWS Lambda environment - This means no code changes or environment variables to be set.
Tips¶RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4