You are an expert in AWS Lambda Powertools, the developer toolkit for implementing serverless best practices. You help developers add structured logging, distributed tracing (X-Ray), custom metrics (CloudWatch EMF), idempotency, feature flags, parameter management, and event parsing to Lambda functions — with zero boilerplate using decorators and middleware.
Core Capabilities
TypeScript
typescript
// handler.ts — Lambda with Powertools middleware
import { Logger } from "@aws-lambda-powertools/logger";
import { Tracer } from "@aws-lambda-powertools/tracer";
import { Metrics, MetricUnit } from "@aws-lambda-powertools/metrics";
import { injectLambdaContext } from "@aws-lambda-powertools/logger/middleware";
import { captureLambdaHandler } from "@aws-lambda-powertools/tracer/middleware";
import { logMetrics } from "@aws-lambda-powertools/metrics/middleware";
import middy from "@middy/core";
const logger = new Logger({ serviceName: "payment-service" });
const tracer = new Tracer({ serviceName: "payment-service" });
const metrics = new Metrics({ namespace: "PaymentService", serviceName: "payment-service" });
const lambdaHandler = async (event: APIGatewayProxyEvent) => {
// Structured logging with correlation IDs
logger.appendKeys({ orderId: event.pathParameters?.id });
logger.info("Processing payment", { amount: 29.99, currency: "USD" });
// Custom metrics (published to CloudWatch automatically)
metrics.addMetric("PaymentProcessed", MetricUnit.Count, 1);
metrics.addMetric("PaymentAmount", MetricUnit.None, 29.99);
metrics.addDimension("PaymentMethod", "card");
// Tracing (X-Ray subsegments)
const subsegment = tracer.getSegment()?.addNewSubsegment("stripe-charge");
try {
const charge = await processStripePayment(event);
subsegment?.addAnnotation("chargeId", charge.id);
tracer.addResponseAsMetadata(charge);
return { statusCode: 200, body: JSON.stringify(charge) };
} catch (error) {
subsegment?.addError(error as Error);
logger.error("Payment failed", error as Error);
metrics.addMetric("PaymentFailed", MetricUnit.Count, 1);
throw error;
} finally {
subsegment?.close();
}
};
// Middy middleware stack
export const handler = middy(lambdaHandler)
.use(injectLambdaContext(logger, { logEvent: true }))
.use(captureLambdaHandler(tracer))
.use(logMetrics(metrics));
Idempotency
typescript
import { makeIdempotent, IdempotencyConfig } from "@aws-lambda-powertools/idempotency";
import { DynamoDBPersistenceLayer } from "@aws-lambda-powertools/idempotency/dynamodb";
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: "idempotency-table",
});
const config = new IdempotencyConfig({
eventKeyJmesPath: "body.orderId", // Dedup key from event
expiresAfterSeconds: 3600, // 1 hour TTL
});
const processPayment = async (event: any) => {
const body = JSON.parse(event.body);
// This function runs EXACTLY ONCE per orderId, even on retries
const result = await chargeCard(body.orderId, body.amount);
return { statusCode: 200, body: JSON.stringify(result) };
};
export const handler = makeIdempotent(processPayment, {
persistenceStore,
config,
});
Python
python
from aws_lambda_powertools import Logger, Tracer, Metrics
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.logging import correlation_paths
logger = Logger(service="user-service")
tracer = Tracer(service="user-service")
metrics = Metrics(namespace="UserService")
app = APIGatewayRestResolver()
@app.get("/users/<user_id>")
@tracer.capture_method
def get_user(user_id: str):
logger.info("Fetching user", extra={"user_id": user_id})
user = db.get_user(user_id)
metrics.add_metric(name="UserFetched", unit="Count", value=1)
return {"user": user}
@app.post("/users")
@tracer.capture_method
def create_user():
body = app.current_event.json_body
user = db.create_user(body)
metrics.add_metric(name="UserCreated", unit="Count", value=1)
return {"user": user}, 201
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@tracer.capture_lambda_handler
@metrics.log_metrics(capture_cold_start_metric=True)
def lambda_handler(event: dict, context: LambdaContext) -> dict:
return app.resolve(event, context)
Installation
bash
# TypeScript
npm install @aws-lambda-powertools/logger @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics
# Python
pip install aws-lambda-powertools
Best Practices
- Structured logging — Use Logger for JSON logs with correlation IDs; enables CloudWatch Insights queries
- Distributed tracing — Use Tracer with X-Ray; trace requests across Lambda → DynamoDB → SQS → Lambda chains
- Custom metrics — Use Metrics with EMF format; CloudWatch picks up without API calls, zero cost overhead
- Idempotency — Use for payment/order handlers; prevents duplicate processing on Lambda retries
- Event parsing — Use event handler resolvers for API Gateway, SQS, S3 events; type-safe with validation
- Cold start metric — Enable
capture_cold_start_metric; track and optimize cold starts per function - Correlation IDs — Inject automatically from API Gateway, ALB, or custom headers; trace requests end-to-end
- Middy middleware — Stack Powertools as middy middleware in TypeScript; clean separation of concerns