目 录CONTENT

文章目录

MDC控制接口不打印日志

gsh456
2024-06-22 / 0 评论 / 0 点赞 / 43 阅读 / 0 字

来说下最近的一个需求,一个接口,被用户调用,也被定时任务调用。因为定时任务调用频率太高,打印日志太多,导致服务器磁盘满了,于是提了一个要求,这个接口在定时任务调用的时候,不打印info级别的日志,我提出了以下方案都不行,最后用MDC搞定。

方案1:打印日志地方,log.info() 用一个boolean参数控制不就行了,

回复:接口逻辑太复杂,打印日志的地方太多,改动成本大。也不够优雅。

方案2:写一个统一LogUtils ,里面包含 info等接口,一键替换下。

回复:其实这个方案,有点问题,就是会丢失日志打印行等信息,虽然可以用其他方式解决,但是太麻烦了。

方案3:调整输出日志文件的策略,限制日志的文件的总体积。例如限制10G。

回复:有些日志必须要保留180天。大概就是我就是不要定时任务的执行日志就行了。

经过一顿的百度和gpt,找到一个方案,本人菜鸟一个,写本文章目的不是说我得技术好,方案多,而仅仅是记录,方便给路过这篇文章的大佬提供一丢丢的思路。

日志过滤器

package cn.gsh456.common.log;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;  
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.MDC;  
  
public class MdcHeaderFilter extends Filter<ILoggingEvent> {  
  	//代码框架比较老了,可能有些类导包没有,代码仅供参考。
    @Override  
    public FilterReply decide(ILoggingEvent event) {
        //ERROR 级别的日志应该记录
        if (event.getLevel().isGreaterOrEqual(Level.WARN)) {
            // 如果满足条件,接受日志事件
            return FilterReply.ACCEPT;
        }
        // 从 MDC 中获取 isLog 值
        // 检查 isLog 值是否满足条件
        if ("false".equals(MDC.get("isLog"))) {
            // 否则,拒绝日志事件
            return FilterReply.DENY;
        } else {
            // 如果满足条件,接受日志事件
            return FilterReply.ACCEPT;
        }  
    }  
}

LogBack.xml

日志logback文件,添加过滤器

 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}-[%-5level]-[%-20thread]-[%c.%M:%L]:%msg%n</pattern>
        </encoder>
<!-- 添加过滤器-->
        <filter class="cn.gsh456.common.log.MdcHeaderFilter"/>
    </appender>

    <!-- 系统服务日志-INFO-->
    <appender name="INFOFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${SRVNAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${LOG_BACK_HOME}/%d{yyyy-MM-dd}/${SRVNAME}.%i.log</fileNamePattern>
            <!-- 单个日志文件最多 100MB, 60天的日志周期,最大不能超过10GB -->
            <maxFileSize>${maxFileSize}</maxFileSize>
            <maxHistory>${maxHistory}</maxHistory>
            <totalSizeCap>${totalSizeCap}</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}-[%-5level]-[%-20thread]-[%c.%M:%L]:%msg%n</pattern>
        </encoder>
<!-- 添加过滤器-->
        <filter class="cn.gsh456.common.log.MdcHeaderFilter"/>
    </appender>

代码使用

//定时器调用
    @PostMapping(value = "requestByTimer")
    public ApiListResult<String> requestByTimer(){
        List<CategoryInfo> result;
        //标记所有调用,不记录info日志
        MDC.put("isLog","false");
        try {
            result = testService.method1();
        }finally {
            //一定要记得remove,1:线程是复用的,要不然其他线程也不打印日志了,2:减少内存泄露的可能
            MDC.remove("isLog");
        }
        return new ApiListResult<>(result);
    }
    //用户调用
    @PostMapping(value = "requestByUser")
    public ApiListResult<String> requestByUser(){
        List<String> result=testService.method1();
        return new ApiListResult<>(result);
    }

这里拿controller中 两个接口调用同一个接口来演示,如果是跨服务调用,也可以在header啥的添加变量,然后切面的系统

如果有更好的方案请在文章下留言交流。

0

评论区