使用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",
已尝试的方案(均无效)
- 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()
- 适配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)); });
- 提前构建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
相关产品推荐
相关产品推荐

