springboot项目非微服务日志链路追踪

2022-08-24 14:18:35

本次讲解非微服务日志链路追踪,下一篇文章在介绍微服务链路追踪

日志链路追踪主要用于定位请求日志
TraceID:TraceID标记了 浏览器发起的某个请求, 这个TraceID可在服务端从接收请求到 响应请求中流转,甚至接力传递给下游应用中流转,用于唯一标记和定外这次请求。

第一步:创建一个springcloud项目
创建一个过滤器,主要生成TraceID和销毁TraceID
代码如图所示:

import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.UUID;


@Component
@Order(1)
@WebFilter(urlPatterns = "/*", filterName = "logMdcFilter")
public class LogMdcFilter implements Filter {
    private static final String UNIQUE_ID = "traceId";

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        boolean bInsertMDC = insertMDC();
        try {
            chain.doFilter(request, response);
        } finally {
            if(bInsertMDC) {
                MDC.remove(UNIQUE_ID);
            }
        }
    }

    @Override
    public void destroy() {
    }

    private boolean insertMDC() {
        UUID uuid = UUID.randomUUID();
        String uniqueId = uuid.toString().replace("-", "");
        MDC.put(UNIQUE_ID, uniqueId);
        System.out.println("-----traceId-----"+ uniqueId);
        return true;
    }



}

第二步:在resource下创建logback-spring.xml文件,添加   [%X{traceId}]   即可
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>febs</contextName>
    <property name="log.path" value="D://mnt/log/server_log/docker/eureka"/>
    <property name="log.maxHistory" value="15"/>
    <property name="log.colorPattern"
              value="%magenta(%d{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %yellow(%thread) %green(%logger) %msg%n"/>
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level  %thread [%X{traceId}] %logger %msg%n"/>

    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 日志级别过滤INFO以下 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>${log.colorPattern}</pattern>
        </encoder>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!--输出到文件-->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/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>

    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern>
        </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>

    <appender name="file_debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/debug/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        </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>

    <appender name="file_warn" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/warn/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sql/mybatis-sql.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/sql/mybatis-sql.log.%d{yyyy-MM-dd}</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%thread|%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger{36}|%m%n</pattern>
        </encoder>
    </appender>



    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <!--显示日志-->
    <logger name="org.springframework.jdbc.core" additivity="false" level="DEBUG" >
        <appender-ref ref="console" />
    </logger>
    

    <root level="info">
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
        <appender-ref ref="file_debug"/>
        <appender-ref ref="file_warn"/>
    </root>
</configuration>
第四步:创建一个controller,编写一些接口,并打印info和error错误
@RequestMapping("/list")
@RequiresPermissions("spellwords:appspellwordstudentbook:list")
public R list(@RequestParam Map<String, Object> params){
    logger.info("=------test测试-----"+ MDC.get("traceId"));
    PageUtils page = appSpellWordStudentBookService.queryPage(params);
    logger.info("=------test测试结束002-----"+ MDC.get("traceId"));
    String str = null;
    System.out.println(str.getBytes());
    return R.ok().put("page", page);
}

此处有两行info打印日志和一行抛出异常的代码


第五步:启动查看日志

启动Application查看日志

info日志打印如下,红色标识的为traceId:027468ffe9464f7389e831238931f1be

2021-05-15 09:56:08 INFO   http-nio-8303-exec-2 [027468ffe9464f7389e831238931f1be] com.xxx.spellwords.controller.AppSpellWordStudentBookController =------test测试-----027468ffe9464f7389e831238931f1be
2021-05-15 09:56:08 INFO   http-nio-8303-exec-2 [027468ffe9464f7389e831238931f1be] com.xxx.spellwords.controller.AppSpellWordStudentBookController =------test测试结束002-----027468ffe9464f7389e831238931f1be

error日志打印如下,红色标识为traceId:027468ffe9464f7389e831238931f1be

2021-05-15 09:56:08 ERROR  http-nio-8303-exec-2 [027468ffe9464f7389e831238931f1be] com.xxx.exception.RRExceptionHandler null
java.lang.NullPointerException: null
	at com.xxx.spellwords.controller.AppSpellWordStudentBookController.list(AppSpellWordStudentBookController.java:58)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

可以看出配置完毕,最后使用阿里云日志服务采集日志或者使用elk,通过平台来查询日志,具体日志配置操作看我以前博客

对于异步线程问题后续继续写
  • 作者:工作QQ感冒的石头
  • 原文链接:https://blog.csdn.net/qq_39291929/article/details/116839510
    更新时间:2022-08-24 14:18:35