-
Notifications
You must be signed in to change notification settings - Fork 229
Description
Winston Instrumentation Race Condition in AWS Lambda Layer
Describe the bug
Winston instrumentation fails silently when enabled via OTEL_NODE_ENABLED_INSTRUMENTATIONS in AWS Lambda. Logs are not captured or exported to OTLP endpoints. The root cause is a race condition: Winston instrumentation registers synchronously during init() before LoggerProvider is created asynchronously in wrap(), causing logs.getGlobalLoggerProvider() to return undefined.
Steps to reproduce
-
Deploy AWS Lambda function with Node.js 20.x runtime
-
Attach AWS Lambda Layer:
arn:aws:lambda:us-east-1:901920570463:layer:aws-otel-nodejs-arm64-ver-1-30-2:1 -
Set environment variables:
AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
OTEL_NODE_ENABLED_INSTRUMENTATIONS=aws-sdk,http,winston
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net
OTEL_EXPORTER_OTLP_HEADERS=api-key=<your-license-key>
OTEL_TRACES_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp- Create a Winston logger in your function:
const logger = require('winston').createLogger({
level: 'info',
transports: [new winston.transports.Console()]
});-
Invoke the Lambda function with log statements
-
Check your OTLP backend (New Relic, DataDog, etc.)
What did you expect to see?
Winston logs should be automatically captured by the OpenTelemetry instrumentation and exported to the configured OTLP endpoint with proper trace context propagation.
What did you see instead?
Logs never reach the OTLP backend
No error messages or warnings in Lambda logs
Root Cause
Timing issue in nodejs/packages/layer/src/wrapper.ts:
┌─────────────────────────────────────────────────────┐
│ init() - SYNCHRONOUS │
├─────────────────────────────────────────────────────┤
│ createInstrumentations() │
│ → includes Winston instrumentation │
│ │
│ registerInstrumentations({ instrumentations }) │
│ → Winston calls logs.getGlobalLoggerProvider() │
│ → Returns UNDEFINED ❌ │
│ → Instrumentation becomes no-op │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ wrap() - ASYNCHRONOUS (too late) │
├─────────────────────────────────────────────────────┤
│ await initializeProvider() │
│ → await initializeLoggerProvider() │
│ → LoggerProvider created and registered ✅ │
│ → But Winston already failed above │
└─────────────────────────────────────────────────────┘Workaround
Disable Winston automatic instrumentation and use manual OpenTelemetryTransportV3:
1.Remove winston from OTEL_NODE_ENABLED_INSTRUMENTATIONS:
OTEL_NODE_ENABLED_INSTRUMENTATIONS=aws-sdk,http- Install the transport package:
npm install @opentelemetry/winston-transport- Use manual transport in your logger:
const winston = require('winston');
const { OpenTelemetryTransportV3 } = require('@opentelemetry/winston-transport');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.json(),
),
transports: [
new winston.transports.Console(),
new OpenTelemetryTransportV3() // Logs sent to OTLP ✅
]
});This works because the logger is instantiated after wrap() has already created the global LoggerProvider.
What version of collector/language SDK version did you use?
AWS Lambda Layer: aws-otel-nodejs-arm64-ver-1-30-2
OpenTelemetry Core: v1.30.0 (bundled in layer)
OpenTelemetry API: @opentelemetry/api@1.9.0
OpenTelemetry Winston Instrumentation: included in layer
Winston: ^3.13.0
What language layer did you use?
Node.js 20.x (ARM64 architecture)
Additional context
Environment: AWS Lambda Node.js 20.x, ARM64
OTLP Backend: New Relic (tested with https://otlp.nr-data.net)
Issue Location: opentelemetry-lambda/nodejs/packages/layer/src/wrapper.ts (lines ~100-150)
Related Repository: opentelemetry-js-contrib/packages/instrumentation-winston
Impact: Any Lambda function using Winston logger with automatic instrumentation cannot export logs to observability backends
Suggested Fix: Move Winston instrumentation registration to wrap() function after initializeLoggerProvider() completes