-
-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Description
After upgrading from AdonisJS v6 to v7, OTEL logs are no longer exported to our collector (SigNoz), while traces and metrics continue to work normally. This affects both production apps using Node.js 24.
The root cause is that @adonisjs/logger@7.1.0 imports pino as a named export in its bundled file (logger-Btq_FVdY.js):
import { pino, multistream, destination, ... } from "pino";The @opentelemetry/instrumentation-pino patches the pino module via import-in-the-middle (IITM):
if (isESM) {
if (module.pino) module.pino = patchedPino;
module.default = patchedPino;
}However, IITM has a known, unfixed limitation where reassignments to named exports are not visible to importing modules (nodejs/import-in-the-middle#38, open since Nov 2023). This means the patched pino function never reaches @adonisjs/logger, so the OTelPinoStream (responsible for log sending) is never added to the logger's stream.
Why traces and metrics are unaffected
| Signal | Mechanism | Relies on IITM? |
|---|---|---|
| Traces | instrumentation-http uses Node.js native diagnostic_channel |
No |
| Metrics | Collected automatically by Node.js runtime instrumentations | No |
| Logs | instrumentation-pino patches pino module via IITM |
Yes |
Versions
| Package | Version |
|---|---|
@adonisjs/otel |
1.2.1 |
@adonisjs/logger |
7.1.0 |
@adonisjs/core |
7.0.1 |
import-in-the-middle |
3.0.0 |
@opentelemetry/instrumentation-pino |
0.58.0 |
@opentelemetry/auto-instrumentations-node |
0.70.1 |
pino |
10.3.1 |
| Node.js | 24 |
Approaches tried and discarded
1. pino-opentelemetry-transport as a pino transport target
Pino transports run in worker threads. The pino-opentelemetry-transport (via otlp-logger) creates a new LoggerProvider in the worker thread and calls logs.setGlobalLoggerProvider(), which overwrites the LoggerProvider configured by @adonisjs/otel in the main thread, breaking traces.
2. Explicit instrumentation-pino configuration
Setting disableLogSending: false explicitly doesn't help since the underlying IITM named export limitation prevents the patch from reaching the logger.
3. Downgrading @adonisjs/otel version
The IITM limitation (#38) affects all IITM versions (v2 and v3), so no version of @adonisjs/otel avoids this.
Suggested fix
The otel_provider could bypass IITM entirely by directly patching the pino logger instance after the app boots. Since the provider has access to the AdonisJS container, it can get the already-created pino instance and manually add the OTelPinoStream via pino.multistream:
// In otel_provider.js — during ready() hook
import { logs } from '@opentelemetry/api-logs'
import pino from 'pino'
const logger = app.container.make('logger')
const pinoInstance = logger.pino
const origStream = pinoInstance[pino.symbols.streamSym]
const otelStream = new OTelPinoStream({
messageKey: pinoInstance[pino.symbols.messageKeySym],
levels: pinoInstance.levels,
// ...
})
pinoInstance[pino.symbols.streamSym] = pino.multistream([
{ level: 0, stream: origStream },
{ level: 0, stream: otelStream },
], { levels: pinoInstance.levels.values })This would be transparent to users — no changes to config/logger.ts needed.
Alternatively, a simpler fix could be made in @adonisjs/logger: change the bundled import from import { pino } from "pino" (named) to import pino from "pino" (default), which IITM can patch correctly.
Reproduction
- Create an AdonisJS v7 app with
@adonisjs/otelconfigured withdestinations.otlp({ signals: ['traces', 'logs', 'metrics'] }) - Set
OTEL_ENABLED=trueand point to an OTLP collector - Make HTTP requests — traces and metrics appear in the collector, but no logs
Related issues
- nodejs/import-in-the-middle#38 — Root cause: named export reassignments not propagating
- nodejs/import-in-the-middle#171 — Related: duplicate exports incorrectly excluded