全部产品
Search
文档中心

可观测链路 OpenTelemetry 版:将TraceId和SpanId写入日志

更新时间:Jul 10, 2024

本文介绍如何使用OpenTelemetry将TraceId和SpanId自动写入日志。TraceId与SpanId写入日志后,您可以将分布式链路数据与日志数据关联起来,实现更高效的故障诊断和性能分析。

Java

支持的日志库

更多信息,请参见 Logger MDC auto-instrumentation

日志框架

自动埋点支持的版本

手动埋点需要引入的依赖

Log4j1

1.2+

Log4j2

2.7+

opentelemetry-log4j-context-data-2.17-autoconfigure

Logback

1.0+

opentelemetry-logback-mdc-1.0

使用Log4j2

  1. 在pom.xml中添加OpenTelemetry Log4j2依赖。

    请将OPENTELEMETRY_VERSION替换为最新版本

    <dependencies>
      <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-log4j-context-data-2.17-autoconfigure</artifactId>
        <version>OPENTELEMETRY_VERSION</version>
        <scope>runtime</scope>
      </dependency>
    </dependencies>
  2. 修改log4j2.xml配置,在pattern中添加%X{trace_id} %X{span_id},可以将TraceId与SpanId自动写入日志。

    以下为日志格式示例:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
      <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
          <PatternLayout
              pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} trace_id=%X{trace_id} span_id=%X{span_id} trace_flags=%X{trace_flags} - %msg%n"/>
        </Console>
      </Appenders>
      <Loggers>
        <Root>
          <AppenderRef ref="Console" level="All"/>
        </Root>
      </Loggers>
    </Configuration>

使用Logback

  1. 在pom.xml中添加OpenTelemetry Logback依赖。

    请将OPENTELEMETRY_VERSION替换为最新版本

    <dependencies>
      <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
        <version>OPENTELEMETRY_VERSION</version>
      </dependency>
    </dependencies>
  2. 修改logback.xml配置,在pattern中添加%X{trace_id} %X{span_id},可以将TraceId与SpanId自动写入日志。

    以下为日志格式示例:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
      <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%d{HH:mm:ss.SSS} trace_id=%X{trace_id} span_id=%X{span_id} trace_flags=%X{trace_flags} %msg%n</pattern>
        </encoder>
      </appender>
    
      <!-- Just wrap your logging appender, for example ConsoleAppender, with OpenTelemetryAppender -->
      <appender name="OTEL" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
        <appender-ref ref="CONSOLE"/>
      </appender>
    
      <!-- Use the wrapped "OTEL" appender instead of the original "CONSOLE" one -->
      <root level="INFO">
        <appender-ref ref="OTEL"/>
      </root>
    
    </configuration>

使用Logback(SpringBoot项目)

Spring Boot项目内置了日志框架,并且默认使用Logback作为其日志实现。如果您的Java项目为SpringBoot项目,只需少量配置即可将TraceId写入日志。

在application.properties中设置logging.pattern.level,添加%mdc{trace_id}%mdc{span_id}到日志中。

logging.pattern.level=trace_id=%mdc{trace_id} span_id=%mdc{span_id} %5p

以下为日志示例:

2024-06-26 10:56:31.200 trace_id=8f7ebd8a73f9a8f50e6a00a87a20952a span_id=1b08f18b8858bb9a  INFO 53724 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-06-26 10:56:31.201 trace_id=8f7ebd8a73f9a8f50e6a00a87a20952a span_id=1b08f18b8858bb9a  INFO 53724 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-06-26 10:56:31.209 trace_id=8f7ebd8a73f9a8f50e6a00a87a20952a span_id=1b08f18b8858bb9a  INFO 53724 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
2024-06-26 10:56:31.296 trace_id=8f7ebd8a73f9a8f50e6a00a87a20952a span_id=5743699405074f4e  INFO 53724 --- [nio-8081-exec-1] com.example.httpserver.ot.OTServer       : hello world

Go

OpenTelemetry Go不支持自动注入,需要在记录日志时手动注入TraceId和SpanId。

使用logrus

  1. 引入依赖。

    import (
        oteltrace "go.opentelemetry.io/otel/trace"
        "github.com/sirupsen/logrus"
    )
  2. 创建工具函数,从Span中提取SpanID和TraceID,并添加到Logrus日志字段。

    func LogrusFields(span oteltrace.Span) logrus.Fields {
        return logrus.Fields{
            "trace_id": span.SpanContext().TraceID().String(),
            "span_id": span.SpanContext().SpanID().String(),
        }
    }
  3. 创建一个Span后,即可记录带有TraceId和SpanId的日志。

    ...
    _, span := tracer.Start(ctx, "spanName")
    defer span.End()
    logEntry := logrus.WithFields(LogrusFields(span))
    logEntry.Info("This is an info message with trace and span ID")
    ...

Python

OpenTelemetry Python支持自动将TraceId与SpanId注入到日志中。更多信息,请参见 OpenTelemetry Logging Instrumentation

使用logging

  1. 设置环境变量OTEL_PYTHON_LOG_CORRELATION为true,开启自动将TraceId与SpanId添加到日志条目中的能力。

    export OTEL_PYTHON_LOG_CORRELATION=true
  2. 在日志格式中添加%(otelTraceID)s%(otelSpanID)s,OpenTelemetry会自动将日志中的 %(otelTraceID)s%(otelSpanID)s替换为TraceId和SpanId。

    • 以下为logging.Formatter示例。

      formatter = logging.Formatter(
          '%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s resource.service.name=%(otelServiceName)s] - %(message)s'
      )
    • 以下为日志示例。

      2024-06-25 10:00:55,494 INFO [app] [app.py:80] [trace_id=70561de6a164ac991bfff2281b7f**** span_id=4b7f3c798460**** resource.service.name=ot-python-demo] - hello world

操作示例

下文将以Demo应用Helloword为例,说明如何在Python中自动将TraceId与SpanId注入到日志中。

请确保您已安装Python和pip。本示例使用的是Python 3.12.4、pip 24.1.1。

  1. 安装本示例使用到的Flask框架。

    pip3 install flask

    预期输出:

    Successfully installed Jinja2-3.1.4 MarkupSafe-2.1.5 Werkzeug-3.0.3 blinker-1.8.2 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0

    输出类似信息,说明Flask框架安装成功。

  2. 安装OpenTelemetry Python相关的包和工具。

    pip3 install opentelemetry-distro \
            opentelemetry-exporter-otlp
    opentelemetry-bootstrap -a install

    预期输出:

    Successfully installed opentelemetry-instrumentation-dbapi-0.46b0
    Successfully installed opentelemetry-instrumentation-requests-0.46b0
    Successfully installed opentelemetry-instrumentation-urllib3-0.46b0

    输出类似信息,说明OpenTelemetry Python相关的包和工具安装成功。

  3. 创建Demo应用。

    import logging
    from logging.handlers import RotatingFileHandler
    from flask import Flask
    
    import os
    
    app = Flask(__name__)
    
    # 创建格式化器,其中包含OpenTelemetry上下文信息
    formatter = logging.Formatter(
        '%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s resource.service.name=%(otelServiceName)s] - %(message)s'
    )
    
    # 创建一个RotatingFileHandler来记录日志到文件
    # 注意将/path/to/app.log替换为您的日志文件存放的实际路径
    file_handler = RotatingFileHandler('/path/to/app.log', maxBytes=10000, backupCount=1)  
    file_handler.setLevel(logging.INFO)
    file_handler.setFormatter(formatter)
    app.logger.addHandler(file_handler)
    
    @app.route('/')
    def hello_world():
        app.logger.info('Hello World!!!')
        return 'Hello, World!'
    
    if __name__ == '__main__':
        app.run()
    

    运行Demo应用,预期输出:

    * Running on http://127.0.**.**:5000

    说明应用部署成功。

  4. 设置环境变量,开启自动将TraceId与SpanId添加到日志条目中的能力,并自动将OpenTelemetry的仪器化代码植入应用程序中。

    export OTEL_PYTHON_LOG_CORRELATION=true
    export OTEL_SERVICE_NAME=ot-python-demo
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_METRICS_EXPORTER=none
    
    # 请将test.py替换为实际文件名称
    opentelemetry-instrument python3 test.py
  5. 查看日志文件,日志样例如下。

    2024-06-25 10:00:55,494 INFO [app] [test.py:80] [trace_id=70561de6a164ac991bfff2281b7f**** span_id=4b7f3c798460**** resource.service.name=ot-python-demo] - hello world

    说明TraceId与SpanId已被添加到日志条目中。

调用链与日志服务SLS双向关联

在调用链里关联日志

在日志服务SLS中关联调用链