or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aws-sdk-instrumentation.mddecorator-tracing.mdindex.mdmanual-instrumentation.mdmiddleware-tracing.mdtypes.md
tile.json

decorator-tracing.mddocs/

Decorator-based Tracing

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.

Capabilities

Lambda Handler Decorator

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
  }
}

Method Decorator

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);

Combining Decorators

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);

Decorator Requirements

Important Notes:

  1. Decorators work only on TypeScript class methods, not on standalone functions
  2. The handler must be bound to the class instance: handlerClass.handler.bind(handlerClass)
  3. Decorators require TypeScript with experimentalDecorators enabled in tsconfig.json:
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}
  1. For function-based handlers, use the Middleware Integration instead