zoukankan      html  css  js  c++  java
  • logback 按照业务主键分文件打印日志,使用SiftingAppender结合MDC. 实现项目中定时任务的日志单独打印,使用FilterReplay.NEUTRAL. 线程池和MDC

    1. 需求: 按照业务主键划分日志分件,可以使用siftingAppender结合MDC 实现。 
    <
    appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator> <key>sysUUID</key> // 代码使用使用MDC.put("sysUUID",value)即可 <defaultValue>unknown</defaultValue> </discriminator> <sift> <appender name="filexx" class="ch.qos.logback.core.FileAppender"> <Encoding>UTF-8</Encoding> <file>${log.base}/wtposp_${sysUUID}.log</file> // 按天生成文件夹为实现(%d{yyyyMMdd}这样写不行。) <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n</pattern> </layout> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>TRACE</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> </appender> </sift> </appender>

    2. 需求: 项目中有多个定时任务,每10s 执行一次,如果和其他业务日志打印到同一个文件,会造成日志文件很乱,业务日志中插着定时日志,

    日志不连贯,不易拍错,不易查看。于是想要把定时的相关日志打印到一个单独的文件,其他日志另外打印。 

    (1)最先想到的解决办法是运用 logger 属性 additivity=false ,可以让日志不再往下传递。

      这种方式需要指定哪些包或文件 使用这个logger , 但是我项目的定时任务调用的方法和其他业务是同一个方法(比如查询,我定时要用,其他业务也用啊),

      没法区分开。 

     (2)使用一个标记,不如定时的日志内容都以 "JOB-" 开头,我想办法过滤下,使用appender 的 filter .  符合条件就打印,不符合就忽略或拒绝。

      需要定义两个appender ,一个只接受JOB- 内容开头的日志。 另一个不接受JOB- 内容开头的日志。 

    知识点:一个appender 可以包含多个filter ,常见的filter 是LevelFilter ,可以按日志级别过滤日志。  FilterReply的三个枚举: 

       DENY: 放弃该日志,不再往下一个filter 传递

      ACCEPT:  处理该条日志,不再往下传递。

      NEUTRAL: 不处理,交给下一个filter 处理。 

    <appender name="job-rollout" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <Encoding>UTF-8</Encoding>
            <file>${log.base}/job_xx.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.base}/job-xx-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
                <MaxHistory>30</MaxHistory>
            </rollingPolicy>
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%date{HH:mm:ss} %-5level %c{0}:%L - %msg%n</pattern>
            </layout>
            <filter class="com.pp.filter.JobLogFilter">
                <onMatch>ACCEPT</onMatch>
                <onMismatch>NEUTRAL</onMismatch>
            </filter>
        </appender>
    
        <appender name="rollout" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <Encoding>UTF-8</Encoding>   
            <file>${log.base}/xx.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
                <fileNamePattern>${log.base}/xx-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
                <MaxHistory>30</MaxHistory> 
            </rollingPolicy>  
            <layout class="ch.qos.logback.classic.PatternLayout">  
                <pattern>%date{HH:mm:ss}   %-5level %c{0}:%L - %msg%n</pattern>
            </layout>
            <filter class="com.pp.filter.JobLogFilter">
                <onMatch>DENY</onMatch>
                <onMismatch>NEUTRAL</onMismatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">  
                <level>TRACE</level>  
                <onMatch>DENY</onMatch>  
                <onMismatch>ACCEPT</onMismatch>  
            </filter>
            <!-- <encoder>  
                <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{5} - %msg%n</pattern>  
            </encoder>  --> 
        </appender>  

    <root>  
    <level value="DEBUG" />
    <appender-ref ref="job-rollout"/>
    <appender-ref ref="rollout" />
    </root>
    /**
     * @Author zc
     * @Date 2019/8/16
    * 继承AbstractMatcherFilter是为了使用其属下onMatch 和 onMisMatch 。 也可以直接继承Filter,但是没法传递参数进去,不能灵活区分是否打印日志。
    */ public class JobLogFilter extends AbstractMatcherFilter<ILoggingEvent> { @Override public FilterReply decide(ILoggingEvent event) { String uuid = event.getMDCPropertyMap().get("sysUUID"); if (uuid != null && uuid.contains("job")){ return this.onMatch; }else { return this.onMismatch; } } }

    20190906 补充: 

    结合MDC 知识,我使用MDC 存放业务主键,然后把业务主键在日志中打印,这样我就能比较清晰的查看一个请求的所有日志了。

    在请求入口

    MDC.put("sysUUID",xx业务唯一);
    然后
    <layout class="ch.qos.logback.classic.PatternLayout">  
    <pattern>%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n</pattern>
    </layout>

    打印出来的每行日志都有业务唯一标识。 

    但是最近看日志发现一个问题: 日志中夹杂着多行sysUUID 不一样的行,很是奇怪,排查发现都是使用线程池中的代码打印的日志。

    跟踪代码发现MDC中有ThreadLocal , 看来问题就是线程池使用的是单独的线程,自然不一样。 

    解决办法: 自定义线程,把主线程的上下文发给子线程,并保存到MDC.

    Map _cm = MDC.getCopyOfContextMap();
    pool.submit(new Runnable() {
    @Override
    public void run() {
    MDC.setContextMap(_cm);
    //业务代码
    //.....................
         //
    MDC.clear();
    }
    });

    封装线程写法样例:
    public abstract class A implments Runable{
    private Map x ;
      public A(Map a){
        x = a;
    }
      public abstract void cm();
      public void run(){
        
        MDC.setContextMap(x);
    //业务代码
    //.....................
          cm();
         //
    MDC.clear();

      }
    }
  • 相关阅读:
    从一个程序的Bug解析C语言的类型转换
    Expression Blend使用笔刷
    Expression Blend入门
    C#生成CHM文件(中级篇)
    C#生成CHM文件(应用篇)
    C#创建不规则窗体的几种方式
    Web Service学习笔记(2)
    C#生成CHM文件(应用篇)之代码库编辑器(1)
    ASP.NET实际项目演练(1)
    Web Service学习笔记(4)
  • 原文地址:https://www.cnblogs.com/zhangchenglzhao/p/11339180.html
Copyright © 2011-2022 走看看