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

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;

问题原因分析

  1. 重复的TracerProvider和注册逻辑:手动创建NodeTracerProvider的同时又使用Vercel的registerOTel,两者产生冲突,导致手动配置的SpanProcessor无法生效。
  2. 重复注册WinstonInstrumentation:同时通过registerOTel和registerInstrumentations注册同一个Instrumentation,引发日志处理逻辑异常。
  3. Winston Transport与Instrumentation重复作用:OpenTelemetryTransportV3和WinstonInstrumentation都是用来将Winston日志接入OpenTelemetry的,同时使用会导致日志无法正确关联Trace上下文,或无法被导出到Traces表。
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 01:05:02