TLog分布式日志追踪系统的实际应用

一、日志框架

使用slf4j作为日志门面,logback作为实际的日志框架。

在这里插入图片描述

二、logback文件的改造方案

1.重新设计日志格式:在原有的日志格式基础上,新增了颜色渲染,修改了不合理的格式。

 <property name="ENCODER_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%boldYellow(%thread)] %X{tl} [%highlight(%-5level)] [%boldCyan(%logger{50})] [%X{key1:-}] [%X{key2:-}] %.-2048msg %n" />

2.定义不同的Appender:

  • 使用日志规范中已有的Appender:APPLICATION_DEBUG、APPLICATION_INFO、APPLICATION_WARN、APPLICATION_ERROR、CONSOLE。
  • 统一身份认证服务无需使用异步日志。

**3.定义不同的Logger
**- 对ORM框架,定义新的Logger,打印sql语句,但不输出为日志文件。

  • 对三方包如:com.alibaba.nacos.client、org.hibernate 定义不同的logger

4.根据Profile的配置,选择日志输出的级别。

<springProfile name="dev"><!-- 根据application.yml配置指定级别 -->
        <root level="DEBUG">
            <!-- 控制台输出 -->
            <appender-ref ref="STDOUT"/>
            <!-- 文件输出 -->
            <appender-ref ref="ERROR"/>
            <appender-ref ref="INFO"/>
            <appender-ref ref="WARN"/>
            <appender-ref ref="DEBUG"/>
            <appender-ref ref="TRACE"/>
        </root>
    </springProfile>

    <springProfile name="prod">
        <root level="INFO">
            <!-- 控制台输出 -->
            <appender-ref ref="STDOUT"/>
            <!-- 文件输出 -->
            <appender-ref ref="ERROR"/>
            <appender-ref ref="INFO"/>
            <appender-ref ref="WARN"/>
        </root>
    </springProfile>

三、Getting Started(user-management-service)

1.引入依赖

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>tlog-all-spring-boot-starter</artifactId>
  <version>1.5.1</version>
</dependency>

2.logback-spring.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!--       %d{yyyy-MM-dd HH:mm:ss.SSS}: 输出日期时间,使用指定的格式。-->
<!--       %thread: 输出线程名。-->
<!--       %X{tl}: 输出MDC(Mapped Diagnostic Context)中名为 tl 的值。-->
<!--       %highlight(%-5level): 输出日志级别,并通过 highlight 进行着色。-->
<!--       %logger{50}: 输出日志记录器名称,最多显示50个字符。-->
<!--       %X{key1:-}: 输出MDC中名为 key1 的值,如果没有找到则显示 -。-->
<!--       %X{key2:-}: 输出MDC中名为 key2 的值,如果没有找到则显示 -。-->
<!--       %.-2048msg: 输出日志消息,最多显示2048个字符。-->
<!--       %n: 输出换行符。&ndash;>-->
<configuration>

    <!-- 排除不需要打印的日志 -->
    <logger name="com.xxl.job.core.thread.ExecutorRegistryThread" level="OFF" />
    <logger name="com.xxl.job.core.thread.ExecutorRegistryThread.run" level="OFF" />

    <!-- 增加TLog MDC Listener -->
    <contextListener class="com.yomahub.tlog.core.enhance.logback.TLogLogbackTTLMdcListener"/>

    <!--定义参数,后面可引用使用-->
    <property name="LOG_PATH" value="./logs" />
    <property name="LOG_FILE_DEBUG" value="${LOG_PATH}/debug" />
    <property name="LOG_FILE_INFO" value="${LOG_PATH}/info" />
    <property name="LOG_FILE_WARN" value="${LOG_PATH}/warn" />
    <property name="LOG_FILE_ERROR" value="${LOG_PATH}/error" />
    <property name="ENCODER_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%boldYellow(%thread)] %X{tl} [%highlight(%-5level)] [%boldCyan(%logger{50})] [%X{key1:-}] [%X{key2:-}] %.-2048msg %n" />

    <property name="ROLLING_SUFFIX" value=".%d{yyyy-MM-dd}.%i.log" />
    <property name="ROLLING_MAX_HISTORY" value="15" />
    <property name="ROLLING_MAX_FILE_SIZE" value="50MB" />
    <property name="ROLLING_TOTAL_SIZE_CAP" value="20GB" />

    <!--异步appender-->
    <property name="ASYNC_QUEUE_SIZE" value="256" />
    <!--设为0表示队列达到80%,也不丢弃任务-->
    <property name="ASYNC_DISCARDING_THRESHOLD" value="0" />
    <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒-->
    <property name="ASYNC_MAX_FLUSH_TIME" value="1000" />
    <!--队列满了直接丢弃要写的消息-->
    <property name="ASYNC_NEVER_BLOCK" value="true" />
    <!--是否包含调用方的信息,false则无法打印类名方法名行号等-->
    <property name="ASYNC_CALLER_DATA" value="true" />
    <!--异步appender-->

        <!--输出日志到文件中-->
    <appender name="APPLICATION_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--不输出ERROR级别的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>

        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <!--根据日期滚动输出日志策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/debug-log-%i.log</fileNamePattern>
            <maxHistory>${ROLLING_MAX_HISTORY}</maxHistory>
            <maxFileSize>${ROLLING_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${ROLLING_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!--输出日志到文件中-->
    <appender name="APPLICATION_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--不输出ERROR级别的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>

        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <!--根据日期滚动输出日志策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/info-log-%i.log</fileNamePattern>
            <maxHistory>${ROLLING_MAX_HISTORY}</maxHistory>
            <maxFileSize>${ROLLING_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${ROLLING_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!--输出日志到文件中-->
    <appender name="APPLICATION_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--不输出ERROR级别的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>

        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <!--根据日期滚动输出日志策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/warn-log-%i.log</fileNamePattern>
            <maxHistory>${ROLLING_MAX_HISTORY}</maxHistory>
            <maxFileSize>${ROLLING_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${ROLLING_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!--错误日志输出文件-->
    <appender name="APPLICATION_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--只输出ERROR级别的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <!--根据日期滚动输出日志策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/error-log-%i.log</fileNamePattern>
            <maxHistory>${ROLLING_MAX_HISTORY}</maxHistory>
            <maxFileSize>${ROLLING_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${ROLLING_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!--异步打印日志,任务放在阻塞队列中,如果队列达到80%,将会丢弃TRACE,DEBUG,INFO级别的日志任务,对性能要求不是太高的话不用启用-->
    <appender name="ASYNC_APPLICATION_DEBUG" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
        <!--队列的深度,该值会影响性能,默认256-->
        <queueSize>${ASYNC_QUEUE_SIZE}</queueSize>
        <!--设为0表示队列达到80%,也不丢弃任务-->
        <discardingThreshold>${ASYNC_DISCARDING_THRESHOLD}</discardingThreshold>
        <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒-->
        <maxFlushTime>${ASYNC_MAX_FLUSH_TIME}</maxFlushTime>
        <!--队列满了直接丢弃要写的消息-->
        <neverBlock>${ASYNC_NEVER_BLOCK}</neverBlock>
        <!--是否包含调用方的信息,false则无法打印类名方法名行号等-->
        <includeCallerData>${ASYNC_CALLER_DATA}</includeCallerData>
        <!--One and only one appender may be attached to AsyncAppender,添加多个的话后面的会被忽略-->
        <appender-ref ref="APPLICATION_INFO"/>
    </appender>

    <!--异步打印日志,任务放在阻塞队列中,如果队列达到80%,将会丢弃TRACE,DEBUG,INFO级别的日志任务,对性能要求不是太高的话不用启用-->
    <appender name="ASYNC_APPLICATION_INFO" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
        <!--队列的深度,该值会影响性能,默认256-->
        <queueSize>512</queueSize>
        <!--设为0表示队列达到80%,也不丢弃任务-->
        <discardingThreshold>${ASYNC_DISCARDING_THRESHOLD}</discardingThreshold>
        <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒-->
        <maxFlushTime>${ASYNC_MAX_FLUSH_TIME}</maxFlushTime>
        <!--队列满了直接丢弃要写的消息-->
        <neverBlock>${ASYNC_NEVER_BLOCK}</neverBlock>
        <!--是否包含调用方的信息,false则无法打印类名方法名行号等-->
        <includeCallerData>${ASYNC_CALLER_DATA}</includeCallerData>
        <!--One and only one appender may be attached to AsyncAppender,添加多个的话后面的会被忽略-->
        <appender-ref ref="APPLICATION_INFO"/>
    </appender>

    <appender name="ASYNC_APPLICATION_WARN" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
        <!--队列的深度,该值会影响性能,默认256-->
        <queueSize>${ASYNC_QUEUE_SIZE}</queueSize>
        <!--设为0表示队列达到80%,也不丢弃任务-->
        <discardingThreshold>${ASYNC_DISCARDING_THRESHOLD}</discardingThreshold>
        <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒-->
        <maxFlushTime>${ASYNC_MAX_FLUSH_TIME}</maxFlushTime>
        <!--队列满了直接丢弃要写的消息-->
        <neverBlock>${ASYNC_NEVER_BLOCK}</neverBlock>
        <!--是否包含调用方的信息,false则无法打印类名方法名行号等-->
        <includeCallerData>${ASYNC_CALLER_DATA}</includeCallerData>
        <includeCallerData>true</includeCallerData>
        <!--One and only one appender may be attached to AsyncAppender,添加多个的话后面的会被忽略-->
        <appender-ref ref="APPLICATION_WARN"/>
    </appender>

    <appender name="ASYNC_APPLICATION_ERROR" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
        <!--队列的深度,该值会影响性能,默认256-->
        <queueSize>${ASYNC_QUEUE_SIZE}</queueSize>
        <!--设为0表示队列达到80%,也不丢弃任务-->
        <discardingThreshold>${ASYNC_DISCARDING_THRESHOLD}</discardingThreshold>
        <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒-->
        <maxFlushTime>${ASYNC_MAX_FLUSH_TIME}</maxFlushTime>
        <!--队列满了直接丢弃要写的消息-->
        <neverBlock>${ASYNC_NEVER_BLOCK}</neverBlock>
        <!--是否包含调用方的信息,false则无法打印类名方法名行号等-->
        <includeCallerData>${ASYNC_CALLER_DATA}</includeCallerData>
        <includeCallerData>true</includeCallerData>
        <!--One and only one appender may be attached to AsyncAppender,添加多个的话后面的会被忽略-->
        <appender-ref ref="APPLICATION_ERROR"/>
    </appender>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>

    <logger name="org.mybatis" value="INFO" level="INFO"/>
    <logger name="org.springframework" value="INFO" level="INFO"/>
    <logger name="org.apache" value="INFO" level="INFO"/>
    <logger name="org.hibernate" value="INFO" level="INFO"/>
    <logger name="sun.rmi" value="ERROR" level="ERROR"/>
    <logger name="java.io" value="ERROR" level="ERROR"/>
    <logger name="javax.management" value="ERROR" level="ERROR"/>
    <logger name="javax.activation" value="ERROR" level="ERROR"/>
    <logger name="com.alibaba.nacos.client" value="ERROR" level="ERROR"/>
    <logger name="sun.net.www.protocol" value="ERROR" level="ERROR"/>
    <logger name="com.netflix.loadbalancer" value="ERROR" level="ERROR"/>
    <logger name="com.baomidou.mybatisplus.core.MybatisConfiguration.addMappedStatement" value="ERROR" level="ERROR"/>
    <logger name="com.netflix.zuul.http" value="INFO" level="INFO"/>
    <logger name="com.alibaba.cloud.seata" value="INFO" level="INFO"/>
    <logger name="com.baomidou.mybatisplus" value="ERROR" level="ERROR"/>
    <logger name="io.lettuce.core" value="ERROR" level="ERROR"/>
    <logger name="com.apache.ibatis" level="ERROR"/>
    <logger name="springfox" level="ERROR"/>
    <logger name="Validator" level="ERROR"/>
    <logger name="io.netty" level="ERROR"/>
    <logger name="io.seata" value="ERROR" level="ERROR"/>
    <logger name="org.redisson" value="ERROR" level="ERROR"/>



    <springProfile name="dev"><!-- 根据application.yml配置指定级别 -->
        <!--rootLogger是默认的logger-->
        <root level="DEBUG">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="APPLICATION_DEBUG" />
            <appender-ref ref="APPLICATION_INFO" />
            <appender-ref ref="APPLICATION_WARN" />
            <appender-ref ref="APPLICATION_ERROR" />
        </root>
    </springProfile>

    <springProfile name="prod">
        <!--rootLogger是默认的logger-->
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="APPLICATION_INFO" />
            <appender-ref ref="APPLICATION_WARN" />
            <appender-ref ref="APPLICATION_ERROR" />
        </root>
    </springProfile>
</configuration>

###3.添加入参打印过滤器

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<LoggingFilter> loggingFilter() {
        FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LoggingFilter());
        registrationBean.addUrlPatterns("/*"); // 设置过滤路径
        return registrationBean;
    }
}

@WebFilter(urlPatterns = "/*")
@Slf4j
public class LoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        String url = request.getRequestURI();
        // 打印请求参数
        if (isJson(request)) {
            RequestWrapper bodyRequestWrapper = new RequestWrapper(request);
            String body = bodyRequestWrapper.getBodyString();
            log.info("[TLOG]开始请求URL[{}],参数为:{}", url, body);
        } else {
            String parameters = JSONUtil.toJsonStr(request.getParameterMap());
            log.info("[TLOG]开始请求URL[{}],参数为:{}", url, parameters);
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }

    /**
     * 判断本次请求的数据类型是否为json
     *
     * @param request request
     * @return boolean
     */
    private boolean isJson(HttpServletRequest request) {
        if (request.getContentType() != null) {
            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
        }
        return false;
    }
}

项目使用中的日志规范

//如非必要,不要打印error级别的日志,避免频繁报警
        log.error("这个错误很严重,程序阻断了,要记监控,发到报警");
        //可以使用warn记录用户输入参数错误的情况,避免在用户投诉时无所适从
        log.warn("这个错误不应该有,程序可以继续往下走,但是太没面子了");
        //对于经常访问的接口,记录info日志时思考:这些日志真的有人看吗?看到这些日志你能做什么?能不能给问题排查带来好处?
        log.info("就是想记录一下业务走到哪里了");
        //主要包含运行时的细节问题,无需考虑访问流量,发生在联调阶段,输出一些关键的变量、函数调用、以及程序执行的流程等信息
        //某些三方传入的变量推荐使用info
        log.debug("作为一个经常写bug的程序员,多打点日志很合理");
        log.trace("从来没用过,他们说,是为了追踪");

        //进行日志打印预判断,提升代码运行效率,提高系统性能。
        if(log.isDebugEnabled()){
            log.debug("别嫌麻烦,不想被开除最好这样做");
        }

TLog的基本使用

  • 自定义业务标签打印:使用 TLog 记录方法的入参信息,以便在日志中了解方法调用时传入的参数值。
 	@GetMapping("/test")
    @TLogAspect(value = "id")
    public void test(Integer id){
        log.error("我是错误日志");
        List<UcUserSoleCode> userSoleCodeList = ucUserSoleCodeService.selectUserSoleCodes(Arrays.asList(1, 2, 3, 4, 5, 748374));
        System.out.println(userSoleCodeList.toString());
    }

id传入11时:

[2023-10-11 13:19:51.119] [http-nio-16100-exec-4] <0> [id:11] [INFO ] [c.y.t.web.interceptor.TLogWebInvokeTimeInterceptor] [TLOG]结束URL[/BigData/test]的调用,耗时为:57316毫秒

  • MDC模式:logback的xml中预留了业务标签。可以用于业务追踪。
 	@GetMapping("/test")
    public void test(Integer id){
		//会占位logback文件中ENCODER_PATTERN中的key1和key2占位符
		//
        MDC.put("key1","我是key1");
        MDC.put("key2","我是key2");
        MDC.put("key3","我是key3");
        log.error("我是错误日志");
    }

示例:[2023-10-11 13:19:51.119] [http-nio-16100-exec-4] [DEBUG] [org.springframework.web.servlet.DispatcherServlet] [我是key1] [我是key2] Completed 200 OK

  • 线程池打印:如果需要在多线程环境中记录日志,可以配置 TLog 使用线程池来执行日志记录操作,确保线程安全。

  • 远程调用打印:如果需要将日志信息发送到远程日志服务器或集中式日志系统,可以配置 TLog 实现远程日志记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值