13 | 日志:日志记录真没你想象的那么简单
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了日志记录中可能遇到的问题及解决方案,并推荐了使用SLF4J和Logback。通过实际案例解释了日志配置中的常见问题,如logger配置继承关系和LevelFilter错误配置导致的日志重复记录。同时,还介绍了如何使用异步日志来改善性能,并探讨了异步日志可能遇到的问题,如记录异步日志撑爆内存、日志丢失和出现阻塞。通过详细分析和解决方案,读者能快速了解日志记录的技术特点,对开发人员具有实际指导意义。文章通过深入的技术分析和实际案例,帮助读者全面了解了日志记录中的常见问题和解决方案,以及如何使用异步日志来改善性能,对开发人员具有实际指导意义。 在使用SLF4J时,需要理清桥接API和绑定这两个模块。Logback是Java最常用的日志框架,其配置比较复杂,需要参考官方文档中关于Appender、Layout、Filter的配置。使用异步日志解决性能问题时,需要权衡阻塞等待和丢弃日志的选择。最后,强调日志框架提供的参数化日志记录方式不能完全取代日志级别的判断,尤其在日志量很大时。文章还提出了两个思考与讨论问题,涉及Logback的配置和生产级项目的文件日志处理。 总之,本文通过深入的技术分析和实际案例,帮助读者全面了解了日志记录中的常见问题和解决方案,以及如何使用异步日志来改善性能,对开发人员具有实际指导意义。
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
全部留言(32)
- 最新
- 精选
- Geek_58afe9置顶"其实,我们只是换成了 Log4j2 API,真正的日志记录还是走的 Logback 框架。没错,这就是 SLF4J 适配的一个好处。". Log4j2 和 LogBack 不是同质化产品吗, Log4j2 api 怎么会走到Logback?
作者回复: log4j2 API,并不是log4j 你在ch.qos.logback.classic.Logger.buildLoggingEventAndAppend设一个断点看一下整个过程就知道了,或者直接访问 https://github.com/JosephZhu1983/java-common-mistakes/blob/master/src/main/java/org/geekbang/time/commonmistakes/logging/placeholder/log4j2api_to_slf4j_to_logack.jpg 查看
2020-04-136 - Darren我们在线上的日志基本遇到的问题也不多,最多就是日志消费不及时问题,目前通过filebeat采集写入kafka,strom消费,写入es聚合,然后前端展示;现在有延迟问题,正在切flink。 回答下问题: 第一个问题采用了表达式; 第二个问题主要是SizeAndTimeBasedRollingPolicy的MaxHistory,MaxFileSize,totalSizeCap属性等,文件在github上,请老师指点 https://github.com/y645194203/geektime-java-100/blob/master/logback.xml
作者回复: 这个实现可以,我也更新了一个自定义Filter的例子 https://github.com/JosephZhu1983/java-common-mistakes/blob/master/src/main/java/org/geekbang/time/commonmistakes/logging/duplicate/multiplelevelsfilter.xml
2020-04-08614 - Husiun老师还有一点我补充一下,springboot默认使用starter日志依赖logback的时候,日志配置文件名应以-spring结尾,才会默认加入其上下文环境中。
作者回复: 是的,相关信息参考 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-logback-extensions
2020-04-0710 - Demon.Lee扎心,然后就是瑟瑟发抖,我一直以为:“SLF4J 的{}占位符语法,到真正记录日志时才会获取实际参数,因此解决了日志数据获取的性能问题” 是对的,也没有去验证,所以代码都是这么写的,虽然把函数作为入参的情况很少,但写了那么多用SLF4J 的{}占位符打印日志的代码,突然很恐慌。哎,小伙伴们,再没时间也要把每个例子动手去实验一下。
作者回复: :)
2020-04-1637 - 殿小二老师,你好 问一下: 1.在生产环境中需要info.warning,error分开文件记录吗? 2.如果日志分开,在后期查看调用过程时不借助第三方工具,需要按时间进行匹配感觉会很累,有必要额外单独一个文件记录所以日志吗? 3.我们项目一般会记录请求和响应信息到单独的一个文件中,这个日志级别设置为info和debug都无所谓吧?
作者回复: 1. 取决于你们是否希望针对不同的日志级别有不同的归档策略等等 2. 一般都会用ELK搜索,不会有这个问题 3. 你认为请求和响应属于开发用的调试信息那么放入debug,生产不开启debug,如果认为是用于审计等,那么放入info,我不好判断你的需求是什么
2020-04-0927 - Monday@Log4j2 @Slf4j 两个注解使用的区别是什么,前者使用了Log4j的框架记录日志,后者使用了默认的Logback框架吗?
作者回复: @Log4j Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @Log4j2和@Slf4j只是日志记录API,和实际日志记录框架没有关系,可以再看看文章一开始说的那段有关SLF4J结构的内容
2020-04-077 - alex老师,一个类只在指定类配置下才打印日志这个配置怎么做,比如系统有定时器调用DataSourceTransactionManager的情况下, 在DEBUG下会有大量日志不间断打出,基本没法看,但是又想看别的类调用DataSourceTransactionManager下的日志输出, 现在做法是定义filter,判断进程名 if (event != null && event.getLevel().equals(Level.DEBUG_INTEGER) && Objects.equals("flowable-bpmn-acquire-async-jobs", event.getThreadName())) { return FilterReply.DENY; } 不知道有没有更好的做法
作者回复: 嗯,对于这种需求,自定义Filter或直接使用GEventEvaluator/JaninoEventEvaluator都是比较好的解决方案。
2020-05-283 - boyxie日志格式化取代日志级别的判断,本地测试发现一个特列,如果只传对象(并重写了toString方法),是有日志级别判断的功能的。这对于大部分打印入参出参的日志还是合适的吧 log.debug("debug0:{}", user); // 有效果 log.debug("debug0:{}", user.toString()); // 无效果
作者回复: 传入的是Object,那么log.debug传入的是Object,只有输出日志的时候才会toString,如果你在toString里面打印了东西来判断是否有效果,就是这样的
2020-04-283 - J.Smile有一点 不是很明白,即便是“参数会延迟到真正需要记录日志时再获取“,但又不是异步的,凭什么说能提高性能呢
作者回复: 因为是info级别的时候就无需浪费时间去获取debug日志需要的参数了
2020-05-2122 - 天天向上虽然我们可以使用 log4j-over-slf4j 来实现 Log4j 桥接到 SLF4J,也可以使用 slf4j-log4j12 实现 SLF4J 适配到 Log4j,也把它们画到了一列,但是它不能同时使用它们,否则就会产生死循环。jcl 和 jul 也是同样的道理。 老师,这句话的意思是,对于那个图来讲,就是,直上直下(属于一列)不可以,但是可以拐弯。是这个意思吗?
作者回复: 是
2020-05-2022