Decorator-based tracing enables automatic instrumentation of TypeScript class methods using decorators. This pattern is ideal for object-oriented Lambda handlers and provides clean, declarative tracing.
Automatically trace Lambda handler execution with cold start detection and error handling.
/**
* Decorator automating capture of metadata and annotations on segments or subsegments for a Lambda Handler
*
* Automatically:
* - Handles subsegment lifecycle
* - Adds ColdStart annotation
* - Adds ServiceName annotation
* - Adds function response as metadata (configurable)
* - Adds function error as metadata (if any)
*/
captureLambdaHandler(options?: CaptureLambdaHandlerOptions): HandlerMethodDecorator;
interface CaptureLambdaHandlerOptions {
/** Whether to capture the Lambda handler response as subsegment metadata (default: true) */
captureResponse?: boolean;
}Usage Example:
import { Tracer } from '@aws-lambda-powertools/tracer';
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
@tracer.captureLambdaHandler()
public async handler(event: unknown, context: unknown): Promise<void> {
// Handler logic - automatically traced
tracer.putAnnotation('userId', '12345');
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass);Disabling Response Capture:
class Lambda implements LambdaInterface {
@tracer.captureLambdaHandler({ captureResponse: false })
public async handler(event: unknown, context: unknown): Promise<void> {
// Response will not be captured as metadata
}
}Automatically trace arbitrary class methods with custom subsegment names.
/**
* Decorator automating capture of metadata and annotations on segments or subsegments for an arbitrary function
*
* Automatically:
* - Handles subsegment lifecycle
* - Adds function response as metadata (configurable)
* - Adds function error as metadata (if any)
*/
captureMethod<T extends AnyClass>(options?: CaptureMethodOptions): MethodDecorator<T>;
interface CaptureMethodOptions {
/** Set a custom name for the subsegment (default: ### methodName) */
subSegmentName?: string;
/** Disable response serialization as subsegment metadata (default: true) */
captureResponse?: boolean;
}
type AnyClass = new (...args: any[]) => any;
type MethodDecorator<T extends AnyClass> = (
target: InstanceType<T>,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<AnyClassMethod>
) => void;
type AnyClassMethod = (...args: any[]) => any;Usage Example:
import { Tracer } from '@aws-lambda-powertools/tracer';
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
const tracer = new Tracer({ serviceName: 'serverlessAirline' });
class Lambda implements LambdaInterface {
@tracer.captureMethod()
private async getBooking(bookingId: string): Promise<Booking> {
// This method is automatically traced with subsegment name "### getBooking"
return { id: bookingId, status: 'confirmed' };
}
@tracer.captureMethod({ subSegmentName: 'customGetUser' })
private async getUser(userId: string): Promise<User> {
// This method is traced with custom subsegment name "customGetUser"
return { id: userId, name: 'Alice' };
}
@tracer.captureMethod({ captureResponse: false })
private async processPayment(payment: Payment): Promise<void> {
// Response is not captured as metadata (useful for sensitive data)
}
@tracer.captureLambdaHandler()
public async handler(event: BookingEvent, context: unknown): Promise<void> {
const booking = await this.getBooking(event.bookingId);
const user = await this.getUser(event.userId);
await this.processPayment(event.payment);
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass);You can combine @tracer.captureLambdaHandler() and @tracer.captureMethod() to trace the entire execution flow:
import { Tracer } from '@aws-lambda-powertools/tracer';
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
const tracer = new Tracer({ serviceName: 'orderService' });
const dynamoDB = tracer.captureAWSv3Client(new DynamoDBClient({}));
class OrderHandler implements LambdaInterface {
@tracer.captureMethod()
private async validateOrder(order: Order): Promise<boolean> {
// Validation logic - creates subsegment "### validateOrder"
return order.items.length > 0 && order.totalAmount > 0;
}
@tracer.captureMethod({ subSegmentName: 'DynamoDB.SaveOrder' })
private async saveOrder(order: Order): Promise<void> {
// Database logic - creates subsegment "DynamoDB.SaveOrder"
await dynamoDB.send(new PutItemCommand({
TableName: 'Orders',
Item: { orderId: { S: order.id } }
}));
}
@tracer.captureMethod()
private async notifyCustomer(order: Order): Promise<void> {
// Notification logic - creates subsegment "### notifyCustomer"
tracer.putMetadata('notification', { orderId: order.id, sent: true });
}
@tracer.captureLambdaHandler()
public async handler(event: OrderEvent, context: unknown): Promise<void> {
// Main handler - creates subsegment "## index.handler"
const order = event.order;
tracer.putAnnotation('orderId', order.id);
const isValid = await this.validateOrder(order);
if (!isValid) {
throw new Error('Invalid order');
}
await this.saveOrder(order);
await this.notifyCustomer(order);
tracer.putAnnotation('orderProcessed', true);
}
}
const handlerClass = new OrderHandler();
export const handler = handlerClass.handler.bind(handlerClass);Important Notes:
handlerClass.handler.bind(handlerClass)experimentalDecorators enabled in tsconfig.json:{
"compilerOptions": {
"experimentalDecorators": true
}
}