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 disabled