NextJS配置OpenTelemetry+Winston向Azure Application Insights导日志失败
问题:NextJS中Winston日志无法通过OpenTelemetry导出到Azure Application Insights的Traces表
我在NextJS应用里用Winston记录日志,想通过OpenTelemetry把日志收集到Azure Application Insights里,按微软建议配置后,能看到请求时间信息,但Traces表始终是空的。我的配置如下:
instrumentation.ts 原配置
import { env } from "~/env"; import { logs } from "@opentelemetry/api-logs"; import { LoggerProvider, SimpleLogRecordProcessor, ConsoleLogRecordExporter } from "@opentelemetry/sdk-logs"; import { BatchSpanProcessor, NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; import { Resource } from "@opentelemetry/resources"; import { registerOTel } from "@vercel/otel"; import { WinstonInstrumentation } from "@opentelemetry/instrumentation-winston"; import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions"; import { registerInstrumentations } from "@opentelemetry/instrumentation"; export async function register() { if ( process.env.NEXT_RUNTIME == "nodejs" && env.APPLICATIONINSIGHTS_CONNECTION_STRING != null ) { console.log("Registering Application Insights"); const { AzureMonitorTraceExporter, AzureMonitorLogExporter } = await import("@azure/monitor-opentelemetry-exporter"); const tracerProvider = new NodeTracerProvider({ resource: new Resource({ [ATTR_SERVICE_NAME]: env.APPLICATIONINSIGHTS_SERVICE_NAME, }), }); tracerProvider.register(); const traceExporter = new AzureMonitorTraceExporter({ connectionString: env.APPLICATIONINSIGHTS_CONNECTION_STRING, }); tracerProvider.addSpanProcessor( new BatchSpanProcessor(traceExporter, { maxQueueSize: 10, scheduledDelayMillis: 5000, }), ); const logExporter = new AzureMonitorLogExporter({ connectionString: env.APPLICATIONINSIGHTS_CONNECTION_STRING, }); // Set up OpenTelemetry logging provider const loggerProvider = new LoggerProvider(); // Add a log processor that will export the logs loggerProvider.addLogRecordProcessor( new SimpleLogRecordProcessor(logExporter), ); // Add a processor to export log record loggerProvider.addLogRecordProcessor( new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()), ); logs.setGlobalLoggerProvider(loggerProvider); const instrumentation = new WinstonInstrumentation({ disableLogSending: false, enabled: true, disableLogCorrelation: false, }); registerOTel({ serviceName: env.APPLICATIONINSIGHTS_SERVICE_NAME, traceExporter, instrumentations: [instrumentation], }); registerInstrumentations({ instrumentations: [instrumentation], }); } else { } }
Winston 原配置
import { env } from "~/env"; import winston, { type Logger } from "winston"; import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport"; // Create a logger with custom format const logger: Logger = winston.createLogger({ transports: [ new winston.transports.Console({ level: env.NODE_ENV == "test" ? "error" : "debug", format: winston.format.simple(), }), new OpenTelemetryTransportV3(), ], }); export default logger;
问题原因分析
- 重复的TracerProvider和注册逻辑:手动创建
NodeTracerProvider的同时又使用Vercel的registerOTel,两者产生冲突,导致手动配置的SpanProcessor无法生效。 - 重复注册WinstonInstrumentation:同时通过
registerOTel和registerInstrumentations注册同一个Instrumentation,引发日志处理逻辑异常。 - Winston Transport与Instrumentation重复作用:
OpenTelemetryTransportV3和WinstonInstrumentation都是用来将Winston日志接入OpenTelemetry的,同时使用会导致日志无法正确关联Trace上下文,或无法被导出到Traces表。 - BatchSpanProcessor配置不合理:
maxQueueSize:10阈值太小,日志量不足时不会触发导出;scheduledDelayMillis:5000的延迟可能导致应用退出前未完成导出。
修复后的配置
1. 更新 instrumentation.ts
移除手动创建的NodeTracerProvider,完全依赖Vercel的registerOTel管理Trace和Log导出,只保留一次Instrumentation注册:
import { env } from "~/env"; import { logs } from "@opentelemetry/api-logs"; import { LoggerProvider, SimpleLogRecordProcessor, ConsoleLogRecordExporter } from "@opentelemetry/sdk-logs"; import { registerOTel } from "@vercel/otel"; import { WinstonInstrumentation } from "@opentelemetry/instrumentation-winston"; import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions"; export async function register() { if ( process.env.NEXT_RUNTIME === "nodejs" && env.APPLICATIONINSIGHTS_CONNECTION_STRING ) { console.log("Registering Application Insights"); const { AzureMonitorTraceExporter, AzureMonitorLogExporter } = await import("@azure/monitor-opentelemetry-exporter"); const traceExporter = new AzureMonitorTraceExporter({ connectionString: env.APPLICATIONINSIGHTS_CONNECTION_STRING, }); const logExporter = new AzureMonitorLogExporter({ connectionString: env.APPLICATIONINSIGHTS_CONNECTION_STRING, }); // 配置OpenTelemetry日志提供者 const loggerProvider = new LoggerProvider({ resource: { [ATTR_SERVICE_NAME]: env.APPLICATIONINSIGHTS_SERVICE_NAME, }, }); loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(logExporter)); loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(new ConsoleLogRecordExporter())); logs.setGlobalLoggerProvider(loggerProvider); const winstonInstrumentation = new WinstonInstrumentation({ disableLogSending: false, enabled: true, disableLogCorrelation: false, }); // 仅通过Vercel的registerOTel注册所有组件,避免冲突 await registerOTel({ serviceName: env.APPLICATIONINSIGHTS_SERVICE_NAME, traceExporter, logExporter, instrumentations: [winstonInstrumentation], }); } }
2. 更新 Winston 配置
移除OpenTelemetryTransportV3,WinstonInstrumentation会自动拦截Winston日志并注入到OpenTelemetry日志流中:
import { env } from "~/env"; import winston, { type Logger } from "winston"; // Create a logger with custom format const logger: Logger = winston.createLogger({ transports: [ new winston.transports.Console({ level: env.NODE_ENV === "test" ? "error" : "debug", format: winston.format.simple(), }), // 移除OpenTelemetryTransportV3,由Instrumentation处理 ], }); export default logger;
3. 额外优化建议
- 调整
BatchSpanProcessor配置(在registerOTel中通过traceExporterConfig传入):await registerOTel({ // ...其他配置 traceExporterConfig: { maxQueueSize: 100, scheduledDelayMillis: 1000, }, }); - 在NextJS的API路由或Server Components中手动生成Trace上下文,确保日志能关联到Span:
import { trace } from "@opentelemetry/api"; import logger from "~/lib/logger"; export async function GET() { const tracer = trace.getTracer("my-api-tracer"); const span = tracer.startSpan("GET /api/test"); try { logger.info("Processing API request"); // 业务逻辑 return new Response("OK"); } finally { span.end(); } }
内容的提问来源于stack exchange,提问作者user274595
相关产品推荐
相关产品推荐

