体系

日志系统.png

  • 使用时避免实现桥接和适配循环
    • 比如 log4j-over-slf4j 来实现 Log4j 桥接到 SLF4J, slf4j-log4j12 实现 SLF4J 适配到 Log4j,也把它们画到了一列,但是它不能同时使用它们,否则就会产生死循环。

配置问题

重复记录

  1. logger 配置继承关系导致日志重复记录
    1. 比如一个 Appender 挂载到多个 Logger 上
  2. 错误配置 LevelFilter 造成日志重复记录
    1. 需要配置 onMatch 和 onMismatch 属性,否则这个过滤器是无用的
      1. <!-- LevelFilter -->
      2. <filter class="ch.qos.logback.classic.filter.LevelFilter">
      3. <level>INFO</level>
      4. <!-- <level> 中指定的等级,接收 -->
      5. <onMatch>ACCEPT</onMatch>
      6. <!-- miss,即不在 <level> 中的等级,全部拒绝 -->
      7. <onMismatch>DENY</onMismatch>
      8. </filter>

AsyncAppender 异步日志

配置

  • includeCallerData 用于控制是否收集调用方数据,默认是 false,此时方法行号、方法名等信息将不能显示
  • queueSize 用于控制阻塞队列大小,使用的 ArrayBlockingQueue 阻塞队列,默认大小是 256,即内存中最多保存 256 条日志。
  • discardingThreshold 是控制丢弃日志的阈值,主要是防止队列满后阻塞。默认情况下,队列剩余量低于队列长度的 20%,就会丢弃 TRACE、DEBUG 和 INFO 级别的日志。
  • neverBlock 用于控制队列满的时候,加入的数据是否直接丢弃,不会阻塞等待,默认是false,使用 put 方法

    • neverBlock 为 true 时,使用 offer 方法
    • 这里需要注意一下 offer 方法和 put 方法的区别,当队列满的时候 offer 方法不阻塞,而 put 方法会阻塞

      出错场景

  • queueSize 设置得特别大,就可能会导致 OOM。

  • queueSize 设置得比较小(默认值就非常小),且 discardingThreshold 设置为大于 0 的值(或者为默认值),当队列剩余容量少于 discardingThreshold 的配置就会丢弃 <=INFO 的日志
    • 因为 discardingThreshold 的存在,即使设置 queueSize 很大,也容易丢日志
    • discardingThreshold 参数容易有歧义,它不是百分比,而是日志条数。对于总容量 10000 的队列,如果希望队列剩余容量少于 1000 条的时候丢弃,需要配置为 1000。
  • neverBlock 默认为 false,意味着总可能会出现阻塞

    • 如果 discardingThreshold 为 0,那么队列满时再有日志写入就会阻塞
    • 如果 discardingThreshold 不为 0,也不会丢弃 <=INFO 级别的日志,那么出现大量错误日志时,还是会阻塞程序

      取舍

    • 如果考虑绝对性能为先,那就设置 neverBlock 为 true,永不阻塞。

    • 如果考虑绝对不丢数据为先,那就设置 discardingThreshold 为 0,即使是 <=INFO 的级别日志也不会丢,但最好把 queueSize 设置大一点,毕竟默认的 queueSize 显然太小,太容易阻塞。
    • 如果希望兼顾两者,可以丢弃不重要的日志,把 queueSize 设置大一点,再设置一个合理的discardingThreshold

配置优化

  1. EvaluatorFilter + marker,过滤指定标记的日志
    1. <!-- EvaluatorFilter + marker -->
    2. <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    3. <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
    4. <marker>time</marker>
    5. </evaluator>
    6. <onMismatch>DENY</onMismatch>
    7. <onMatch>ACCEPT</onMatch>
    8. </filter>

进行日志级别判断

  • 日志框架提供的参数化日志记录方式不能完全取代日志级别的判断。
    • 使用{}占位符语法不能通过延迟参数值获取,来解决日志数据获取的性能问题
  • 如果你的日志量很大,获取日志参数代价也很大,就要进行相应日志级别的判断,避免不记录日志也要花费时间获取日志参数的问题

extra

打印 mapper 执行结果

  1. # 让目标 mapper 目录显示 trace 等级
  2. logging.level.<目录>.mapper: trace