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

manual-instrumentation.mddocs/

Manual Instrumentation

Manual instrumentation provides direct control over segment and subsegment lifecycle for custom tracing scenarios. This pattern is ideal when you need fine-grained control over trace structure or when decorators and middleware don't fit your use case.

Capabilities

Segment Management

Get and set the active segment or subsegment for manual trace construction.

/**
 * Get the active segment or subsegment (if any) in the current scope
 *
 * Returns undefined if no segment is available (e.g., not in Lambda environment)
 */
getSegment(): Segment | Subsegment | undefined;

/**
 * Set the passed subsegment as the current active subsegment
 *
 * Used to change the active segment context for subsequent operations
 */
setSegment(segment: Segment | Subsegment): void;

Adding Annotations

Add searchable annotations to traces that can be used for filtering in X-Ray console.

/**
 * Add annotation to existing segment or subsegment
 *
 * Annotations are indexed and searchable in X-Ray console.
 * Limited to string, number, and boolean values.
 */
putAnnotation(key: string, value: string | number | boolean): void;

/**
 * Add service name to the current segment or subsegment as annotation
 */
addServiceNameAnnotation(): void;

/**
 * Add ColdStart annotation to the current segment or subsegment
 *
 * Automatically tracks whether this is a cold start (first invocation)
 * or warm start (subsequent invocations)
 */
annotateColdStart(): void;

Adding Metadata

Add detailed metadata to traces that is visible in trace details but not searchable.

/**
 * Add metadata to existing segment or subsegment
 *
 * Metadata is not indexed but visible in trace details.
 * Can contain any serializable data.
 *
 * @param key - Metadata key
 * @param value - Any serializable value
 * @param namespace - Optional namespace (defaults to serviceName)
 */
putMetadata(key: string, value: unknown, namespace?: string): void;

/**
 * Add response data to the current segment or subsegment as metadata
 *
 * @param data - Response data to serialize
 * @param methodName - Name of the method that is being traced
 */
addResponseAsMetadata(data?: unknown, methodName?: string): void;

/**
 * Add error to the current segment or subsegment as metadata
 *
 * @param error - Error to serialize as metadata
 * @param remote - Whether the error was thrown by a remote service (default: false)
 */
addErrorAsMetadata(error: Error, remote?: boolean): void;

Trace Utilities

Utility methods for inspecting trace state.

/**
 * Get the current root AWS X-Ray trace id
 *
 * Useful as correlation id for downstream processes or error reporting
 */
getRootXrayTraceId(): string | undefined;

/**
 * Get the current value of the AWS X-Ray Sampled flag
 *
 * Returns true if the current trace is being sampled
 */
isTraceSampled(): boolean;

/**
 * Get the current value of the tracingEnabled property
 *
 * Use this during manual instrumentation to determine if tracer is enabled
 */
isTracingEnabled(): boolean;

Basic Manual Instrumentation

import { Tracer } from '@aws-lambda-powertools/tracer';

const tracer = new Tracer({ serviceName: 'serverlessAirline' });

export const handler = async (event: unknown, context: unknown) => {
  // Get the facade segment (created by AWS Lambda)
  const segment = tracer.getSegment();
  if (!segment) {
    return; // Not in Lambda environment
  }

  // Create subsegment for the function
  const subsegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
  tracer.setSegment(subsegment);

  // Annotate with cold start and service name
  tracer.annotateColdStart();
  tracer.addServiceNameAnnotation();

  let response;
  try {
    // Your business logic
    response = await processRequest(event);

    // Add the response as metadata
    tracer.addResponseAsMetadata(response, process.env._HANDLER);
  } catch (err) {
    // Add the error as metadata
    tracer.addErrorAsMetadata(err as Error);
    throw err;
  } finally {
    // Close the subsegment
    subsegment.close();
    // Set the facade segment as active again
    tracer.setSegment(segment);
  }

  return response;
};

async function processRequest(event: unknown) {
  return { status: 'success' };
}

Creating Custom Subsegments

Create custom subsegments for specific operations:

import { Tracer } from '@aws-lambda-powertools/tracer';
import { Segment, Subsegment } from 'aws-xray-sdk-core';

const tracer = new Tracer({ serviceName: 'orderService' });

async function processOrder(orderId: string): Promise<void> {
  const parentSegment = tracer.getSegment();
  if (!parentSegment) return;

  // Create a custom subsegment
  const subsegment = parentSegment.addNewSubsegment('### processOrder');
  tracer.setSegment(subsegment);

  try {
    // Add annotations and metadata
    tracer.putAnnotation('orderId', orderId);
    tracer.putMetadata('orderDetails', { id: orderId, timestamp: Date.now() });

    // Process order logic
    await validateOrder(orderId);
    await saveOrder(orderId);

    // Mark as successful
    tracer.putAnnotation('orderProcessed', true);
  } catch (error) {
    tracer.addErrorAsMetadata(error as Error);
    throw error;
  } finally {
    subsegment.close();
    tracer.setSegment(parentSegment);
  }
}

export const handler = async (event: OrderEvent, context: unknown) => {
  await processOrder(event.orderId);
};

Nested Subsegments

Create deeply nested subsegments for complex workflows:

import { Tracer } from '@aws-lambda-powertools/tracer';

const tracer = new Tracer({ serviceName: 'bookingService' });

async function getBookingDetails(bookingId: string): Promise<BookingDetails> {
  const parentSegment = tracer.getSegment();
  if (!parentSegment) {
    return getBookingFromDB(bookingId); // Fallback without tracing
  }

  const subsegment = parentSegment.addNewSubsegment('### getBookingDetails');
  tracer.setSegment(subsegment);

  try {
    tracer.putAnnotation('bookingId', bookingId);

    // Create nested subsegment for database call
    const dbSubsegment = subsegment.addNewSubsegment('#### DynamoDB.GetItem');
    tracer.setSegment(dbSubsegment);

    let booking: BookingDetails;
    try {
      booking = await getBookingFromDB(bookingId);
      tracer.putMetadata('dbResponse', booking);
    } finally {
      dbSubsegment.close();
      tracer.setSegment(subsegment);
    }

    // Create nested subsegment for enrichment
    const enrichSubsegment = subsegment.addNewSubsegment('#### EnrichBooking');
    tracer.setSegment(enrichSubsegment);

    try {
      const enrichedBooking = await enrichBookingData(booking);
      tracer.putMetadata('enrichedData', enrichedBooking);
      return enrichedBooking;
    } finally {
      enrichSubsegment.close();
      tracer.setSegment(subsegment);
    }
  } catch (error) {
    tracer.addErrorAsMetadata(error as Error);
    throw error;
  } finally {
    subsegment.close();
    tracer.setSegment(parentSegment);
  }
}

export const handler = async (event: BookingEvent, context: unknown) => {
  const segment = tracer.getSegment();
  if (!segment) return;

  const handlerSubsegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
  tracer.setSegment(handlerSubsegment);

  try {
    tracer.annotateColdStart();
    tracer.addServiceNameAnnotation();

    const details = await getBookingDetails(event.bookingId);
    tracer.addResponseAsMetadata(details, 'handler');

    return details;
  } catch (error) {
    tracer.addErrorAsMetadata(error as Error);
    throw error;
  } finally {
    handlerSubsegment.close();
    tracer.setSegment(segment);
  }
};

Using Trace ID for Correlation

Use the trace ID as a correlation ID for logging and error reporting:

import { Tracer } from '@aws-lambda-powertools/tracer';

const tracer = new Tracer({ serviceName: 'apiService' });

export const handler = async (event: APIEvent, context: unknown) => {
  try {
    // Process request
    return await processRequest(event);
  } catch (error) {
    // Get trace ID for error reporting
    const traceId = tracer.getRootXrayTraceId();

    // Return error response with trace ID for support
    return {
      statusCode: 500,
      body: JSON.stringify({
        message: 'Internal Server Error',
        traceId: traceId,
        supportMessage: `Please contact support with trace ID: ${traceId}`
      }),
      headers: {
        '_X_AMZN_TRACE_ID': traceId || ''
      }
    };
  }
};

Conditional Tracing

Use isTracingEnabled() to conditionally execute tracing logic:

import { Tracer } from '@aws-lambda-powertools/tracer';

const tracer = new Tracer({ serviceName: 'dataService' });

async function processData(data: unknown[]): Promise<ProcessResult> {
  // Only create subsegment if tracing is enabled
  const parentSegment = tracer.isTracingEnabled() ? tracer.getSegment() : undefined;
  let subsegment;

  if (parentSegment) {
    subsegment = parentSegment.addNewSubsegment('### processData');
    tracer.setSegment(subsegment);
    tracer.putAnnotation('dataCount', data.length);
  }

  try {
    const result = await performProcessing(data);

    if (tracer.isTracingEnabled()) {
      tracer.putMetadata('processingResult', result);
    }

    return result;
  } finally {
    if (subsegment) {
      subsegment.close();
      if (parentSegment) {
        tracer.setSegment(parentSegment);
      }
    }
  }
}

export const handler = async (event: DataEvent, context: unknown) => {
  return await processData(event.data);
};

Segment and Subsegment Types

The getSegment() and setSegment() methods work with types from aws-xray-sdk-core:

import type { Segment, Subsegment } from 'aws-xray-sdk-core';

// Segment represents the root trace created by AWS Lambda
// Subsegment represents a child trace created manually or by decorators/middleware

Important Notes:

  1. Always close subsegments in a finally block to ensure proper cleanup
  2. Always restore the parent segment after closing a subsegment
  3. The facade segment (root segment) is created automatically by AWS Lambda
  4. Check if segment exists before using it to handle non-Lambda environments
  5. Use isTracingEnabled() to avoid creating subsegments when tracing is disabled