Skip to content

instrumentation.ts cannot be used for instrumenting native modules with OpenTelemetry when native modules are imported from instrumentation.ts #64879

Open
@lforst

Description

@lforst

Link to the code that reproduces this issue

https://github.com/lforst/nextjs-instrumentation-behaviour-repro

To Reproduce

npm i
npm run build
npm run start
curl http://localhost:3000/make-req

Afterward, take a look at the server logs.

Inside make-req/route.ts, we are making an http.get() call to check/route.ts.
This http.get() call should be instrumented, meaning tracing headers should be attached.
The check/route.ts endpoint simply returns the headers it receives, but no tracing headers are attached, meaning the instrumentation didn't work.

You might be inclined to say "yo the Sentry SDK is probably broken", but I don't think this is the case.
For the sake of simplicity, I used the Sentry SDK which uses the OpenTelemetry instrumentation under the hood.
You could probably also reproduce this by using the OpenTelemetry HTTP instrumentation + any OTEL HTTP propagator, and the propagation would also fail.

Current vs. Expected behavior

The expected behaviour is for the OpenTelemetry instrumentation to be able to patch the http module so that the http call will have tracing headers attached.

I suspect that other native modules like fs are also affected by this.

Provide environment information

Operating System:
  Platform: linux
  Arch: arm64
  Version: #15-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan  9 22:39:36 UTC 2024
  Available memory (MB): 15962
  Available CPU cores: 6
Binaries:
  Node: 18.18.2
  npm: 9.8.1
  Yarn: 1.22.22
  pnpm: 8.15.5
Relevant Packages:
  next: 14.2.2 // Latest available version is detected (14.2.2).
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Instrumentation, SWC

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local), next start (local), Vercel (Deployed)

Additional context

We noticed this from version 14.2.0 onwards which included a change to not externalize @sentry/nextjs anymore: #61194

However, we don't think this is the problem. In it's root I believe this is caused by the following, only surfaced by that change:

  • Next.js bundling produces code that "caches" require calls to native modules like http.
  • This usually looks like the following:
    13685: (e) => {
      "use strict";
      e.exports = require("http");
    },
    
  • When any code inside instrumentation.ts or any dependencies of instrumentation.ts depends on native modules like http, the require call to this module is cached before the register() hook is ran.
  • This means, when OpenTelemetry tries to patch require with require-in-the-middle, it is pointless because any usages of the native module will use the "cached" version.
  • When @sentry/nextjs wasn't externalized, we didn't notice this problem, because the dependency on http by @sentry/nextjs wasn't bundled into the output of instrumentation.ts, meaning http wasn't cached too early.

I think this is fixable by either not "caching" native modules inside instrumentation.ts, or simply externalizing everything imported inside instrumentation.ts. The latter would probably have a lot of bad consequences.

Let me know if this needs clarification.

Metadata

Metadata

Assignees

No one assigned

    Labels

    InstrumentationRelated to Next.js Instrumentation.SWCRelated to minification/transpilation in Next.js.bugIssue was opened via the bug report template.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions