如何将Spring Cloud Sleuth日志消息发送至Zipkin?
实现SLF4J日志内容上报到Zipkin的方案
当然可行!你已经通过Spring Cloud Sleuth让日志带上了TraceId/SpanId,现在只需要把日志内容附加到Zipkin的Span中,或者生成独立Span来记录日志。下面给你几个实用的实现方式:
方式一:封装日志工具类,手动添加日志到当前Span
如果你愿意少量修改现有日志调用,可以封装一个自定义的日志工具,在打印日志的同时把内容添加到当前Span的Tag中:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brave.Tracer; import brave.Span; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TraceLogger { private final Logger LOG = LoggerFactory.getLogger(TraceLogger.class); private final Tracer tracer; @Autowired public TraceLogger(Tracer tracer) { this.tracer = tracer; } public void info(String message) { LOG.info(message); addLogToSpan("info", message); } public void error(String message, Throwable throwable) { LOG.error(message, throwable); addLogToSpan("error", message); // 可以额外添加异常信息到Span addLogToSpan("error.stack", throwable.getMessage()); } private void addLogToSpan(String level, String message) { Span currentSpan = tracer.currentSpan(); if (currentSpan != null) { // 用log.{level}作为Tag键,避免和其他Tag冲突 currentSpan.tag(String.format("log.%s", level), message); } } }
之后在业务代码中用traceLogger.info("something")替代原来的LOG.info,就能在Zipkin的Span详情里看到对应的日志Tag了。
方式二:监听日志事件,自动注入Span(无需修改现有代码)
如果不想改动现有日志调用,可以通过Spring的事件监听机制,自动捕获SLF4J日志并添加到当前Span:
import brave.Tracer; import brave.Span; import org.springframework.context.ApplicationListener; import org.springframework.context.event.LoggingEvent; import org.springframework.stereotype.Component; import ch.qos.logback.classic.Level; @Component public class LogToSpanInjector implements ApplicationListener<LoggingEvent> { private final Tracer tracer; public LogToSpanInjector(Tracer tracer) { this.tracer = tracer; } @Override public void onApplicationEvent(LoggingEvent event) { // 只处理INFO及以上级别的日志,避免日志量过大 if (event.getLevel().isGreaterOrEqual(Level.INFO)) { Span currentSpan = tracer.currentSpan(); if (currentSpan != null) { // 把日志级别作为Tag的一部分,方便在Zipkin中筛选 String tagKey = String.format("log.%s", event.getLevel().name().toLowerCase()); currentSpan.tag(tagKey, event.getFormattedMessage()); } } } }
这个类会自动监听应用内的所有日志事件,只要当前存在活跃的Span,就会把日志内容添加为Span的Tag。完全不用修改你原来的LOG.info()调用,非常便捷。
方式三:将单条日志作为独立Span上报(按需使用)
如果需要把每条日志作为独立的子Span上报到Zipkin(适合需要单独追踪日志场景),可以这样实现:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brave.Tracer; import brave.Span; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class LogAsSpanLogger { private final Logger LOG = LoggerFactory.getLogger(LogAsSpanLogger.class); private final Tracer tracer; @Autowired public LogAsSpanLogger(Tracer tracer) { this.tracer = tracer; } public void info(String message) { LOG.info(message); // 启动一个子Span,命名为log.info try (Span logSpan = tracer.nextSpan().name("log.info").start()) { logSpan.tag("message", message); // 可以添加更多自定义Tag,比如日志来源类 logSpan.tag("logger.class", LOG.getName()); } } }
这种方式会在原有请求Span下生成一个子Span,专门记录这条日志,不过会增加Zipkin中的Span数量,建议根据实际需求选择。
注意事项
- 确保你的
spring.sleuth.sampler.probability已经设置为1.0(100%采样),你已经配置过这点,没问题。 - 如果日志量很大,建议限制上报的日志级别(比如只上报ERROR和INFO,忽略DEBUG),避免Zipkin存储压力过大。
- Tag的长度在Zipkin中有默认限制,如果日志内容过长,可能会被截断,必要时可以配置Zipkin的存储参数调整长度。
内容的提问来源于stack exchange,提问作者cmatsuoka
相关产品推荐
相关产品推荐

