使用布谷鸟过滤器对 logback 日志一定周期内重复异常堆栈打印进行压缩过滤

news/2024/7/5 8:12:36

前因

前阵子日子线上有个bug,但是由于触发过于频繁导致日志打印全是这个bug的堆栈,全是重复堆栈内容日志一下变得不友好起来,搜索有没有什么优化办法。

偶然看到[近期业务大量突增微服务性能优化总结-2.开发日志输出异常堆栈的过滤插件]这篇文章,试图将同样的日志堆栈过滤插件复刻到logback中

后来发现堆栈打印还是至少同样的堆栈一天内至少打印一次(日志文件按照天进行拆分)才方便排查,于是就需要判断一个异常的堆栈是否打印过,自然就想到了大名鼎鼎的布隆过滤器和布谷鸟过滤器,遂进行改造

思路

思路是改造ch.qos.logback.classic.pattern.ThrowableProxyConverter ,通过在logback配置中设置日志输出的PATTERNconverterClass 来取代默认的org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter

然后在isIgnoredStackTraceLine方法中加入自定义的过滤逻辑

判断是否重复使用布隆和布谷鸟过滤器

代码及演示Demo

因为有对logback类的copy改造,还涉及到spring环境变量加载和日志系统加载打印的先后关系、日志异常插件自己报异常怎么debug等一堆小坑,代码比较多,建议用的时候可以直接fork改改

这里只看核心的两个方法: 一个是判断该行是否应该被过滤

@Override
protected boolean isIgnoredStackTraceLine(String line) {
    //...
    try {
        // 具体判断逻辑委托给接口的实现类
        return predicate.isShouldSkipLine(line);
    } catch (Exception e) {
        //...
    }
    return super.isIgnoredStackTraceLine(line);
}

一个判断该异常堆栈是否第一次打印,这里使用弱引用key Cache是因为logback对于同一个异常会多次调用这个方法

//...
@Override
public boolean isShouldEnableSkip(IThrowableProxy throwableProxy) {
    //如果 mightContain false 则直接判断为打印完整堆栈(return false)
    return Boolean.TRUE.equals(RESULT_CACHE.get().get(throwableProxy, k -> autoRebuildCuckooFilter.isFull(k.getStackTraceElementProxyArray())));
}
//...
public boolean isFull(T item) {
    //判断是否是新的周期新建过滤器
    CuckooFilter<T> cuckooFilter = rebuildIfNecessary();
    //...
    //交给布谷鸟过滤器或者布隆过滤器来判断是否打印过
    if (cuckooFilter.approximateCount(item) >= times) {
        return true;
    }
    return !cuckooFilter.put(item);
}
//...

使用及效果

如下示例logback配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--  导入spring基础配置  -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 因为覆盖了属性,所以这里不能直接借用  -->
<!--    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>-->

    <!--  替换defaults.xml中定义的属性, 使用自己的噪音抑制异常转换器 -->
    <conversionRule conversionWord="wEx"
                    converterClass="com.muyuanjin.lognoiseless.NoiseLessThrowableProxyConverter"/>

    <!-- 控制台日志,因为覆盖了属性,所以这里不能直接借用 console-appender.xml ,copy一遍-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>${CONSOLE_LOG_CHARSET}</charset>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

然后在spring yaml配置文件配置: 白名单模式

logback:
  stackTrace:
    skipLine: com.example,java,org # 多个包名以逗号分隔
    skipLineMode: whitelist #白名单模式
    maxNumPerCycle: 1 # 每个周期内最大打印全栈的次数,=0时每次均跳过, =1时使用 布隆过滤器
    cycleDuration: 2h #计数周期

或者黑名单模式

logback:
  stackTrace:
    skipLine: com.example,java,org # 多个包名以逗号分隔
    skipLineMode: blacklist #黑名单模式
    maxNumPerCycle: 1 # 每个周期内最大打印全栈的次数,=0时每次均跳过, =1时使用 布隆过滤器
    cycleDuration: 2h #计数周期

或者 class 或 spring bean (需实现StackLineSkipPredicate)

logback:
  stackTrace:
    skipLine: com.example.TestStackLineSkipPredicate
    skipLineMode: predicate_class #谓词类
    maxNumPerCycle: 1 # 每个周期内最大打印全栈的次数,=0时每次均跳过, =1时使用 布隆过滤器
    cycleDuration: 2h #计数周期

或者 janino表达式,使用 line 表示该行的字符串参数, 返回true时表示不打印改行

logback:
  stackTrace:
    skipLine: "!line.contains(\"com.example\")" #必须以com.example开头才打印
    skipLineMode: janino_expression #janino表达式
    maxNumPerCycle: 1 # 每个周期内最大打印全栈的次数,=0时每次均跳过, =1时使用 布隆过滤器
    cycleDuration: 2h #计数周期

运行 demo 里的 Test 查看效果:

@Slf4j
@SpringBootTest
class LogNoiseLessDemoApplicationTests {

    @Test
    void contextLoads() {
        for (int i = 0; i < 5; i++) {
            log.warn("test", new RuntimeException("test"));
        }
    }
}

OK,搞定

 


http://lihuaxi.xjx100.cn/news/109690.html

相关文章

网页批量下载图片-怎么一键快速下载网页图片的工具

网页批量下载图片&#xff0c;今天给大家分享一款免费图片批量下载软件&#xff0c;支持任意格式的图片批量下载&#xff0c;只需要输入关键词或批量导入网页链接即可批量下载图片。每个人都可以拥有各种高清图源。支持批量图片压缩/放大/添加水印等等处理/详细如图 这款免费图…

GBase8s jdbc 驱动url介绍

GBase8s jdbc 驱动url介绍 GBase8s jdbc驱动是一款实现jdbc3.0规范以及jdbc4.0绝大部分规范的标准通用接口&#xff0c;故在使用方式上同其它数据库驱动一样&#xff0c;并不存在学习成本。下面具体介绍一下使用方式 1、URL说明 标准格式如下&#xff1a; jdbc:gbasedbt-sqli:/…

渗透测试基础- - -windows网络安全常用dos命令

目录 一&#xff0c;DOS是什么 二&#xff0c;windows常用命令 1.查看系统分区 2.搜索指定文件&#xff1a;for 3.创建文件&#xff1a;echo 4.查看文件内容&#xff1a; 5.删除文件&#xff1a;del 6.隐藏文件命令&#xff1a;attrib 7.关机&#xff1a; 8.测试网络…

pytorch学习(五)——损失函数

文章目录1. 回归问题1.1 均方差1.2 L1损失1.3 平滑L1损失2. 分类问题2.1 合页损失2.2 二分类交叉熵2.3 交叉熵3. 相似度3.1 余弦相似度3.2 相对熵4. 不配合的使用1. 回归问题 1.1 均方差 均方差是回归问题中最常用的损失函数了&#xff0c;Pytorch中的均方差损失函数为 torc…

STM32L051之IWDG使用及注意事项

IWDG使用内部时钟LSI&#xff0c;频率约为37KHz。基本框图如下所示&#xff1a; 内部包含一个8位的分频器&#xff0c;支持4、8、16、32、64、128、256分频。 内部包含一个12位的向下计数器。 假如时钟频率为32K&#xff0c;那么&#xff0c;看门狗的最大计数时间为&#xff…

Safari Extension 扩展插件中关于权限的二三事(例如设置权限、权限类型等)

Safari Extension 扩展插件的权限相关的内容大致分为下面几个方面&#xff1a; 用户要掌控扩展&#xff08;Users are in control&#xff09; Safari Extension 扩展插件是要在用户控制下的&#xff0c;不能想干嘛就干嘛&#xff0c;因此扩展只有在用户进行互动之后才会运行…

linux内核中断实践4:软中断

前言 本次实验也是在定时器处理函数中进行的&#xff0c;修改了Interrupt.h (include\linux)和文件Softirq.c (kernel)文件&#xff0c;用来增加新的软中断。 实验结果可靠&#xff0c;没问题。 如果使用硬件中断的话需要配置设备树&#xff0c;然后从设备树获取irq号&…

Linux LCD屏幕驱动调参实操

Linux LCD屏幕驱动调参实操 初探 Linux下的 LCD 驱动文中提到过&#xff0c; IMX6ULL的 eLCDIF接口驱动程序已经有半导体厂家NXP编写好了&#xff0c;并且不同分辨率LCD屏的eLCDIF接口驱动代码都是一样的&#xff0c;因此LCD驱动部分无需修改。只需要根据所使用的LCD来调整设备…