在实际项目中,你可能会遇到被大量重复日志刷屏的苦恼,这种短时间内重复的日志不仅浪费服务器内存,而且影响你看其他日志。那么你可能会想,有没有一种方法能够让我既打印出这些日志,但又不想重复日志太多的方法。最近我就遇到了这种需求,有些日志不能不打,不然出了问题回过头来发现没有日志也不好定位问题,但是有时候因为定时任务的原因,这种日志又比较多,于是我就想着怎么去减少这种重复的日志。

最朴素的想法就是给重复的日志计数,达到指定的数目就打一条日志,这样就达到了减少重复日志输出的目的。这样做的好处是不用借助其他框架,直接修改打印日志的代码就好了。坏处就是可能会对代码有一定的侵入性。

于是顺着这个思路,我写了一个减少打印重复日志的工具类,在原来打印日志的地方调用这个工具类就可以起到减少重复日志的效果。代码如下:

import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 用来减少打印重复日志
 * @author https://www.chuckfang.com
 * @date Created on 2020/7/20 15:38
 */
public class LogLess {
    /**
     * 每种日志的参数拼接成key,打印之前计算key的数目,达到指定数目才打印日志
     */
    private static Map<String, AtomicInteger> logNum = new HashMap<>(100);

    public static void info(Class currentClass, Integer maxNum, String message, Object... args) {
        String key = getKey(currentClass, args);

        AtomicInteger count = logNum.get(key);
        if (count == null){
            logNum.put(key, new AtomicInteger(1));
        } else if (count.incrementAndGet() >= maxNum){
            LoggerFactory.getLogger(currentClass).info(message, args);
            logNum.remove(key);
        }
    }

    public static void warn(Class currentClass, Integer maxNum, String message, Object... args) {
        String key = getKey(currentClass, args);

        AtomicInteger count = logNum.get(key);
        if (count == null){
            logNum.put(key, new AtomicInteger(1));
        } else if (count.incrementAndGet() >= maxNum){
            LoggerFactory.getLogger(currentClass).warn(message, args);
            logNum.remove(key);
        }
    }

    private static String getKey(Class currentClass, Object[] args) {
        StringBuilder stringBuffer = new StringBuilder(currentClass.getSimpleName());
        for (Object arg : args) {
            stringBuffer.append(arg);
        }
        return stringBuffer.toString();
    }
}

这里直接把日志中的参数和打印日志所在类的类名拼接起来来唯一标识一条可能重复的日志,因为在一个类中,日志参数不同,日志的内容很大可能不同,所以就可以用这种简单的方法来唯一标识一条日志。这样就可以对这个唯一标识,也就是key,去计算它出现的次数。这里用AtomicInteger去统计次数,天然能够满足高并发情况下的数据准确性。这里只写了info和warn级别的减少重复日志的方法,因为我觉得只有这两个是要在生产环境中看到且不能有太多重复的日志,其他级别的你也可以根据实际情况模仿我的写一个静态方法。

最后我们来看看怎么使用:

for (int i = 0; ; i++) {
    LogLess.info(this.getClass(), 100, "log info {}", i);
}

这样就可以每100次打印一条日志,既不会错过日志,也不会产生太多重复日志。



技术分享     

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!