Opinionated thin wrapper for AWS X-Ray SDK enabling distributed tracing in AWS Lambda functions with automatic cold start capture and AWS SDK 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.
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;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;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;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;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' };
}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);
};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);
}
};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 || ''
}
};
}
};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);
};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/middlewareImportant Notes:
finally block to ensure proper cleanupisTracingEnabled() to avoid creating subsegments when tracing is disabledInstall with Tessl CLI
npx tessl i tessl/npm-aws-lambda-powertools--tracer