On Node 20, the wrong tracing stack adds tens of milliseconds to INIT and clutters X-Ray with noise while missing the one Dynamo span you needed. Choose ADOT when you need OpenTelemetry fan-out to multiple backends; choose Powertools Tracer when you want lean X-Ray annotations with minimal cold-start tax.
⚡ TL;DR: Powertools Tracer ≈ thin X-Ray SDK wrapper, fast INIT, great for Lambda-centric ops; ADOT layer ≈ OTel collector sidecar semantics, heavier INIT, better for multi-backend / existing OTel. Sample aggressively at high RPS; never trace every warm invoke at 100%. Pair with Lambda Powertools structured logs and Lambda Cold Starts on Node 20.
Cold-start and package cost
| Approach | Typical INIT impact | Bundle | Backends |
|---|---|---|---|
| Powertools Tracer | Low–moderate | npm @aws-lambda-powertools/tracer |
X-Ray |
| ADOT Lambda layer | Moderate–high | layer + OTel instrumentation | X-Ray / OTLP / others |
| Manual X-Ray SDK | Low | sdk package | X-Ray |
// powertools-tracer.ts — lean default for Node 20
import { Tracer } from "@aws-lambda-powertools/tracer";
import { captureLambdaHandler } from "@aws-lambda-powertools/tracer/middleware";
import middy from "@middy/core";
const tracer = new Tracer({ serviceName: "checkout" });
export const handler = middy(async (event) => {
const segment = tracer.getSegment();
const sub = segment?.addNewSubsegment("dynamo.PutItem");
try {
// AWS SDK v3 clients auto-capture when tracer.captureAWSv3Client(client)
await putOrder(event);
} finally {
sub?.close();
}
return { ok: true };
}).use(captureLambdaHandler(tracer));
// adot — only when you need OTLP export (illustrative env)
// Layers: arn:aws:lambda:region:account:layer:aws-otel-nodejs-*-ver-*:1
// Env: AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
// OTEL_PROPAGATORS=tracecontext,baggage,xray
// OTEL_NODE_DISABLED_INSTRUMENTATIONS=fs,dns
❌ Enabling every OTel instrumentation (fs, dns, net) on a 128 MB function — INIT balloons and spans become unreadable.
Sampling at high RPS
At thousands of RPS, 100% tracing is a tax. Use reservoir + rate sampling; keep errors and slow traces.
{
"version": 2,
"rules": [
{
"description": "checkout errors and p99",
"host": "*",
"http_method": "*",
"url_path": "/checkout*",
"fixed_target": 2,
"rate": 0.01
}
],
"default": { "fixed_target": 1, "rate": 0.005 }
}
Powertools: put annotations (orderId, tenant) not megabyte metadata. ADOT: drop high-cardinality attributes in a collector processor before export.
Decision guide
Need only X-Ray + Powertools Logger/Metrics already? → Powertools Tracer
Need Jaeger/Tempo/Honeycomb + Lambda? → ADOT / OTel
Sub-100ms p99 tool Lambda? → Prefer Powertools or no tracer on warm path
Multi-language estate with OTel conventions? → ADOT for consistency
Closing checklist
✅ Dos
– ✅ Measure INIT with and without the tracing layer before standardizing
– ✅ Sample; annotate IDs; capture AWS SDK clients explicitly
– ✅ Disable noisy OTel instrumentations
– ✅ Correlate with Powertools Logger correlationId
– ✅ Document the choice in the service README
❌ Don’ts
– ❌ Don’t run ADOT + full X-Ray SDK double instrumentation
– ❌ Don’t 100%-sample hot synchronous APIs
– ❌ Don’t put PII in annotations/metadata
– ❌ Don’t ignore layer version pins in IaC
Related reading
- Lambda Powertools Node Structured Logs
- Lambda Cold Starts Node 20
- AWS Lambda Extensions API Custom Monitoring
- Lambda Warm Pools
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
