You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用OpenTelemetry导致Next.js初始化速度极慢的问题求助

问题描述

通过node -r或node --require加载OpenTelemetry追踪脚本初始化Next.js时,应用启动耗时长达4-5分钟,但遥测脚本本身在5秒内即可完成加载。不使用--require参数时,启动仅需30秒。

启动脚本对比

  • 无--require的正常启动脚本:
"dev": "env-cmd -f environments/.env.development next dev",
  • 使用--require加载追踪脚本的启动脚本:
"dev": "env-cmd -f environments/.env.development node --require ./tracing.js ./node_modules/next/dist/bin/next dev",

已尝试的方案(均无效)

  1. OpenTelemetry官方默认tracing.js:
/* tracing.js */

// Require dependencies
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()
  1. 适配Jaeger的定制tracing.js:
const process = require('process');
const opentelemetry = require('@opentelemetry/sdk-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const { Resource } = require('@opentelemetry/resources');
const {
  SemanticResourceAttributes,
} = require('@opentelemetry/semantic-conventions');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

const hostName = process.env.OTEL_TRACE_HOST || 'localhost';

const options = {
  tags: [],
  endpoint: `http://${hostName}:1234/api/traces`,
};
const traceExporter = new JaegerExporter(options);

// configure the SDK to export telemetry data to the console
// enable all auto-instrumentations from the meta package
const sdk = new opentelemetry.NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'my_app',
  }),
  traceExporter,
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-http': {
        ignoreIncomingPaths: [
          '/ping',
          ...(process.env.NODE_ENV !== 'production'
            ? [/^\/_next\/static.*/]
            : []),
        ],
        requestHook: (span, request) => {
          span.setAttributes({
            name: `${request.method} ${request.url || request.path}`,
          });
        },
        startIncomingSpanHook: (request) => {
          return {
            name: `${request.method} ${request.url || request.path}`,
            'request.path': request.url || request.path,
          };
        },
      },
    }),
  ],
});

// initialize the SDK and register with the OpenTelemetry API
sdk
  .start()
  .then(() => console.log('Tracing initialized'))
  .catch((error) =>
    console.log('Error initializing tracing and starting server', error)
  );

// gracefully shut down the SDK on process exit
process.on('SIGTERM', () => {
  sdk
    .shutdown()
    .then(() => console.log('Tracing terminated'))
    .catch((error) => console.log('Error terminating tracing', error))
    .finally(() => process.exit(0));
});
  1. 提前构建Next.js应用后再启动,耗时无改善。

可能的优化方向

1. 缩小自动 instrumentation 范围

@opentelemetry/auto-instrumentations-node会自动注入所有支持的模块 instrumentation,部分模块可能与Next.js内部加载逻辑冲突导致启动变慢。可手动指定需要的instrumentation,避免全量注入:

// 替换全量auto-instrumentations为单个需要的模块
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express');

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new JaegerExporter(options),
  instrumentations: [
    new HttpInstrumentation({
      ignoreIncomingPaths: ['/ping', /^\/_next\/static.*/],
      requestHook: (span, request) => {
        span.setAttributes({
          name: `${request.method} ${request.url || request.path}`,
        });
      },
      startIncomingSpanHook: (request) => {
        return {
          name: `${request.method} ${request.url || request.path}`,
          'request.path': request.url || request.path,
        };
      },
    }),
    new ExpressInstrumentation()
  ],
});

2. 延迟追踪初始化(弃用--require方式)

放弃提前加载追踪脚本,改为在Next.js启动完成后初始化追踪。创建自定义入口server.js:

const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

// 先启动Next.js服务,再初始化追踪
app.prepare().then(() => {
  createServer((req, res) => {
    const parsedUrl = parse(req.url, true);
    handle(req, res, parsedUrl);
  }).listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
    
    // 启动OpenTelemetry追踪
    const opentelemetry = require('@opentelemetry/sdk-node');
    const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
    const sdk = new opentelemetry.NodeSDK({
      traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
      instrumentations: [getNodeAutoInstrumentations()]
    });
    sdk.start().then(() => console.log('Tracing initialized'));
  });
});

修改启动脚本:

"dev": "env-cmd -f environments/.env.development node server.js"

注意:此方式会丢失应用启动阶段的部分追踪数据,但能大幅缩短启动耗时。

3. 排查模块加载阻塞

使用node --trace-modules参数启动,导出加载日志分析阻塞点:

env-cmd -f environments/.env.development node --trace-modules --require ./tracing.js ./node_modules/next/dist/bin/next dev > module-load-log.txt

根据日志定位耗时最长的模块,针对性优化。

4. 升级依赖版本

检查Next.js、Node.js及OpenTelemetry相关包的版本,尝试升级到最新稳定版,部分版本兼容性问题可能导致启动缓慢。


内容的提问来源于stack exchange,提问作者Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:42:28