Springboot+MDC实现全链路日志追踪

本文介绍如何使用SkyWalking进行分布式链路追踪,包括配置日志拦截器、整合SkyWalking定位性能瓶颈及实现日志链路追踪的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景需求:

有时候某个接口涉及的逻辑很多,比如:查数据库、查redis、远程调用接口,发mq消息,执行业务代码等等。

该接口一次请求的链路很长,如果逐一排查,需要花费大量的时间,这时候,我们已经没法用传统的办法定位问题了。

有没有办法解决这问题呢?

用分布式链路跟踪系统:skywalking。

架构图如下:通过skywalking定位性能问题:在skywalking中可以通过traceId(全局唯一的id),串联一个接口请求的完整链路。可以看到整个接口的耗时,调用的远程服务的耗时,访问数据库或者redis的耗时等等,功能非常强大。

之前没有这个功能的时候,为了定位线上接口性能问题,我们还需要在代码中加日志,手动打印出链路中各个环节的耗时情况,然后再逐一排查。

如果你用过skywalking排查接口性能问题,不自觉的会爱上它的。如果你想了解更多功能,可以访问skywalking的官网:https://skywalking.apache.org/

除了这个,项目中也简单做了日志链路的追踪:

1.定义日志拦截器

package com.icon.config;
    
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.MDC;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.UUID;
    
    /**
     * 定义拦截器拦截日志链路唯一标识traceid的值
     */
    @Component
    public class LogInterceptor implements HandlerInterceptor {
    
        @Value(value = "${trace.id:traceid}")
        private String TRACE_ID;
    
        @Override
        public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) {
            MDC.remove(TRACE_ID);
        }
    
        @Override
        public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) {
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 定义日志链路追踪唯一标识的key=traceid 前端UUID没有传递过来,则后台UUID生成
            String traceId = request.getHeader(TRACE_ID);
            if (StringUtils.isBlank(traceId)) {
                traceId = UUID.randomUUID().toString().replace("-", "");
            }
            MDC.put(TRACE_ID, traceId);
            return true;
        }
    }

2.配置拦截器

  package com.hermes.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {
        @Autowired
        private LogInterceptor logInterceptor;
        //配置拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //指定拦截器,指定拦截路径
            registry.addInterceptor(logInterceptor).addPathPatterns("/**")
                    .excludePathPatterns("/index.html","/css/**","/fonts/**","/img/**","/js/**");
        }
    }

3.配置logback日志,输出traceid,完成日志链路追踪

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration scan="true" scanPeriod="2 seconds" debug="false">
        <!--定义参数常量-->
        <property name="log.level" value="debug"/>
        <property name="log.maxHistory" value="15"/>
    
        <!--    test   -->
            <property name="log.filePath" value="/opt/backend.log"/>
    
        <!--    pro    -->
    <!--    <property name="log.filePath" value="/opt/backend.log/"/>-->
    
    <!--    <property name="log.pattern"-->
    <!--              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50} >> %msg%n"/>-->
        <!--配置logback日志,输出traceid,完成日志链路追踪-->
        <property name="log.pattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%X{traceid}] [%thread] %logger{50}:%L >> %msg%n"/>
    
        <!--控制台设置-->
        <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
        </appender>
        <!--    设置DEBUG级别的日志-->
        <appender name="debugAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--<file>${log.filePath}/debug.log</file>-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.filePath}/debug/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>${log.maxHistory}</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>DEBUG</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!--    设置INFO级别的日志-->
        <appender name="infoAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--<file>${log.filePath}/info.log</file>-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.filePath}/info/info.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>${log.maxHistory}</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!--    设置ERROR级别的日志-->
        <appender name="errorAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--<file>${log.filePath}/error.log</file>-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.filePath}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>${log.maxHistory}</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!--    绑定到logger-->
        <!--    <logger name="com.tory.shop" level="${log.level}" additivity="true">-->
        <!--        <appender-ref ref="debugAppender"/>-->
        <!--        <appender-ref ref="infoAppender"/>-->
        <!--        <appender-ref ref="errorAppender"/>-->
        <!--    </logger>-->
    
        <!-- 1. 输出SQL 到控制台和文件-->
        <logger name="org.hibernate.SQL" additivity="false" >
            <level value="info" />
            <appender-ref ref="debugAppender"/>
            <appender-ref ref="infoAppender"/>
            <appender-ref ref="errorAppender"/>
        </logger>
    
        <!-- 2. 输出SQL 的参数到控制台和文件-->
        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" additivity="false" level="info" >
            <level value="info" />
            <appender-ref ref="debugAppender"/>
            <appender-ref ref="infoAppender"/>
            <appender-ref ref="errorAppender"/>
        </logger>
    
        <root level="info">
            <appender-ref ref="consoleAppender"/>
            <appender-ref ref="debugAppender"/>
            <appender-ref ref="infoAppender"/>
            <appender-ref ref="errorAppender"/>
        </root>
    </configuration>

测试:

在这里插入图片描述

End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值