Search code examples
javascriptnode.jstraceopen-telemetry

Adding opentelemetry tracing to Node.js app breaks `require("fs").realpathSync.native`


Using the following tracing enabling script from OpenTelemetry docs:

const opentelemetry = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');

// For troubleshooting, set the log level to DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start()

running my Next.js server as I thought is required, I get an error:

$ node --require './tracing/opentelemetry.js' ./node_modules/next/dist/bin/next start -p 3000
No modules instrumentation has been defined, nothing will be patched
@opentelemetry/instrumentation-grpc Module @grpc/grpc-js has been loaded before @opentelemetry/instrumentation-grpc so it might not work, please initialize it before requiring @grpc/grpc-js
Exporter "otlp" requested through environment variable is unavailable.
/mnt/vol/.local/share/pnpm/global/5/.pnpm/next@12.1.5_zpnidt7m3osuk7shl3s4oenomq/node_modules/next/dist/lib/get-project-dir.js:40
        const realDir = _fs.default.realpathSync.native(resolvedDir);
                                                 ^

TypeError: _fs.default.realpathSync.native is not a function
    at Object.getProjectDir (/mnt/vol/.local/share/pnpm/global/5/.pnpm/next@12.1.5_zpnidt7m3osuk7shl3s4oenomq/node_modules/next/dist/lib/get-project-dir.js:40:50)
    at nextStart (/mnt/vol/.local/share/pnpm/global/5/.pnpm/next@12.1.5_zpnidt7m3osuk7shl3s4oenomq/node_modules/next/dist/cli/next-start.js:80:37)
    at /mnt/vol/.local/share/pnpm/global/5/.pnpm/next@12.1.5_zpnidt7m3osuk7shl3s4oenomq/node_modules/next/dist/bin/next:141:34
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Node.js v17.8.0

Now this can be simplified to a minimal reproduction as follows. This has the fs.realpathSync.native function:

$ node -e 'console.log(require("fs").realpathSync)'
[Function: realpathSync] { native: [Function (anonymous)] }

This doesn't have fs.realpathSync.native:

$ node --require ./tracing/opentelemetry.js -e 'console.log(require("fs").realpathSync)'
No modules instrumentation has been defined, nothing will be patched
@opentelemetry/instrumentation-grpc Module @grpc/grpc-js has been loaded before @opentelemetry/instrumentation-grpc so it might not work, please initialize it before requiring @grpc/grpc-js
[Function (anonymous)]
Exporter "otlp" requested through environment variable is unavailable.

My Node's --require is working correctly (noop.js is an empty file):

$ node --require ./tracing/noop.js -e 'console.log(require("fs").realpathSync)'
[Function: realpathSync] { native: [Function (anonymous)] }

Why would the OpenTelemetry setup script break the fs module?

$ node --version
v17.8.0
//package.json dependencies
    "@opentelemetry/api": "^1.3.0",
    "@opentelemetry/auto-instrumentations-node": "^0.35.0",
    "@opentelemetry/sdk-node": "^0.34.0",
$ uname -a
Linux code-server 5.15.0-1025-oracle #31~20.04.2-Ubuntu SMP Tue Nov 29 13:01:56 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux

Does my ARM machine have something to do with it?

I can reproduce the same on x86_64 on https://replit.com/@JakubKoralewski/opentelemetry-repro with the same behavior.


Solution

  • The reason this error occurs is due to a bug in @opentelemetry/instrumentation-fs introduced as a new dependency to @opentelemetry/auto-instrumentations-node in PR #981 which got released with version 0.34.0. The issue was reported but at the time of writing is still open. However, as also already linked above a PR to address the issue is being reviewed.

    Fow now, I see three ways to address the problem:

    1. As suggested in a comment above downgrade @opentelemetry/auto-instrumentations-node to next lower version 0.33.1.
    2. Disable the file system instrumentation when configuring the node instrumentation. For that simply replace getNodeAutoInstrumentations() with getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-fs': { enabled: false } }) in your code. Given that your project is in Next.js and you likely have little file system activity aside from maybe public files this is likely the best option for now.
    3. Remove @opentelemetry/auto-instrumentations-node altogether and simply instrument the libraries you actually use. Using the auto instrumentation for Node.js pulls in a lot of transitive dependencies. Say you have a Next.js app, connect to a Postgres database and use winston for logging your instrumentation setup could look something like this:
    const sdk = new opentelemetry.NodeSDK({
      traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
      instrumentations: [
        // new FsInstrumentation(), TODO: re-enable once bug is fixed
        new HttpInstrumentation(),
        new PgInstrumentation(),
        new WinstonInstrumentation(),
      ]
    });