必知的技术知识:Java日志框架:logback详解

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 必知的技术知识:Java日志框架:logback详解

为什么使用logback


记得前几年工作的时候,公司使用的日志框架还是log4j,大约从16年中到现在,不管是我参与的别人已经搭建好的项目还是我自己主导的项目,日志框架基本都换成了logback,总结一下,logback大约有以下的一些优点:


内核重写、测试充分、初始化内存加载更小,这一切让logback性能和log4j相比有诸多倍的提升


logback非常自然地直接实现了slf4j,这个严格来说算不上优点,只是这样,再理解slf4j的前提下会很容易理解logback,也同时很容易用其他日志框架替换logback


logback有比较齐全的200多页的文档


logback当配置文件修改了,支持自动重新加载配置文件,扫描过程快且安全,它并不需要另外创建一个扫描线程


支持自动去除旧的日志文件,可以控制已经产生日志文件的最大数量


总而言之,如果大家的项目里面需要选择一个日志框架,那么我个人非常建议使用logback。


logback加载


我们简单分析一下logback加载过程,当我们使用logback-classic.jar时,应用启动,那么logback会按照如下顺序进行扫描:


在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value


在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)


在classpath下寻找是否有logback-test.xml


在classpath下寻找是否有logback.xml


以上任何一项找到了,就不进行后续扫描,按照对应的配置进行logback的初始化,具体代码实现可见ch.qos.logback.classic.util.ContextInitializer类的findURLOfDefaultConfigurationFile方法。


当所有以上四项都找不到的情况下,logback会调用ch.qos.logback.classic.BasicConfigurator的configure方法,构造一个ConsoleAppender用于向控制台输出日志,默认日志输出格式为"%d{HH:mm:ss.SSS} 【%thread】 %-5level %logger{36} - %msg%n"。


logback的configuration


logback的重点应当是Appender、Logger、Pattern,在这之前先简单了解一下logback的,只有三个属性:


scan:当scan被设置为true时,当配置文件发生改变,将会被重新加载,默认为true


scanPeriod:检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认为毫秒,当scan=true时这个值生效,默认时间间隔为1分钟


debug:当被设置为true时,将打印出logback内部日志信息,实时查看logback运行信息,默认为false



先从最基本的与开始。


用来设置某一个包或者具体某一个类的日志打印级别、以及指定。可以包含零个或者多个元素,标识这个appender将会添加到这个logger。仅有一个name属性、一个可选的level属性和一个可选的additivity属性:


name:用来指定受此logger约束的某一个包或者具体的某一个类


level:用来设置打印级别,五个常用打印级别从低至高依次为TRACE、DEBUG、INFO、WARN、ERROR,如果未设置此级别,那么当前logger会继承上级的级别


additivity:是否向上级logger传递打印信息,默认为true


1 public LoggerContext() {


2 super();


3 this.loggerCache = new ConcurrentHashMap();


4


5 this.loggerContextRemoteView = new LoggerContextVO(this);


6 this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);


7 this.root.setLevel(Level.DEBUG);


8 loggerCache.put(Logger.ROOT_LOGGER_NAME, root);


9 initEvaluatorMap();


10 size = 1;


11 this.frameworkPackages = new ArrayList();


12 }


Logger的构造函数为:


Logger(String name, Logger parent, LoggerContext loggerContext) {


this.name = name;


this.parent = parent;


this.loggerContext = loggerContext;


}


看到第一个参数就是Root的name,而这个Logger.ROOT_LOGGER_NAME的定义为final public String ROOT_LOGGER_NAME = "ROOT",由此可以看出节点的name就是"ROOT"。


接着写一段代码来测试一下:


1 public class Slf4jTest {


2


3 @Test


4 public void testSlf4j() {


5 Logger logger = LoggerFactory.getLogger(Object.class);


6 logger.trace("=====trace=====");


7 logger.debug("=====debug=====");


8 logger.info("=====info=====");


9 logger.warn("=====warn=====");


10 logger.error("=====error=====");


11 }


12


13 }


logback.xml的配置为:


1 <?xml version="1.0" encoding="UTF-8" ?>


2 [/span>configuration scan="false" scanPeriod="60000" debug="false"

3 [/span>appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"

4 [/span>layout class="ch.qos.logback.classic.PatternLayout"

5 [/span>pattern

6

7

8


9 [/span>root level="info"

10 [/span>appender-ref ref="STDOUT" />


11

12


13

root将打印级别设置为"info"级别,暂时不管,控制台的输出为:


2018-03-26 22:57:48.779 【main】 INFO java.lang.Object - =====info=====


2018-03-26 22:57:48.782 【main】 WARN java.lang.Object - =====warn=====


2018-03-26 22:57:48.782 【main】 ERROR java.lang.Object - =====error=====


logback.xml的意思是,当Test方法运行时,root节点将日志级别大于等于info的交给已经配置好的名为"STDOUT"的进行处理,"STDOUT"将信息打印到控制台上。


接着理解一下节点的作用,logback.xml修改一下,加入一个只有name属性的:


1 <?xml version="1.0" encoding="UTF-8" ?>


2 [/span>configuration scan="false" scanPeriod="60000" debug="false"

3


4 [/span>appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"

5 [/span>layout class="ch.qos.logback.classic.PatternLayout"

6 [/span>pattern

7

8

9


10 [/span>logger name="java" />


11


12 [/span>root level="debug"

13 [/span>appender-ref ref="STDOUT" />


14

15


16

注意这个name表示的是LoggerFactory.getLogger(XXX.class),XXX的包路径,包路径越少越是父级,我们测试代码里面是Object.class,即name="java"是name="java.lang"的父级,root是所有的父级。看一下输出为:


2018-03-27 23:02:02.963//代码效果参考:http://www.jhylw.com.cn/061022475.html

【main】 DEBUG java.lang.Object - =====debug=====

2018-03-27 23:02:02.965 【main】 INFO java.lang.Object - =====info=====


2018-03-27 23:02:02.966 【main】 WARN java.lang.Object - =====warn=====


2018-03-27 23:02:02.966 【main】 ERROR java.lang.Object - =====error=====


出现这样的结果是因为:


中没有配置level,即继承父级的level,的父级为,那么level=debug


没有配置additivity,那么additivity=true,表示此的打印信息向父级传递


没有配置,表示此不会打印出任何信息


由此可知,的打印信息向传递,使用"STDOUT"这个打印出所有大于等于debug级别的日志。举一反三,我们将的additivity配置为false,那么控制台应该不会打印出任何日志,因为的打印信息不会向父级传递且没有配置任何,大家可以自己试验一下。


接着,我们再配置一个:


1 <?xml version="1.0" encoding="UTF-8" ?>


2 [/span>configuration scan="false" scanPeriod="60000" debug="false"

3


4 [/span>appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"


5 [/span>layout class="ch.qos.logback.classic.PatternLayout"

6 [/span>pattern

7

8

9


10 [/span>logger name="java" additivity="false" />


11 [/span>logger name="java.lang" level="warn"

12 [/span>appender-ref ref="STDOUT" />


13

14


15 [/span>root level="debug"

16 [/span>appender-ref ref="STDOUT" />


17

18


19

如果读懂了上面的例子,那么这个例子应当很好理解:


LoggerFactory.getLogger(Object.class),首先找到name="java.lang"这个,将日志级别大于等于warn的使用"STDOUT"这个打印出来


name="java.lang"这个没有配置additivity,那么additivity=true,打印信息向上传递,传递给父级name="java"这个


name="java"这个的additivity=false且不关联任何,那么name="java"这个不会打印任何信息


由此分析,得出最终的打印结果为:


2018-03-27 23:12:16.147 【main】 WARN java.lang.Object - =====warn=====


2018-03-27 23:12:16.150 【main】 ERROR java.lang.Object - =====error=====


举一反三,上面的name="java"这个可以把additivity设置为true试试看是什么结果,如果对前面的分析理解的朋友应该很容易想到,有两部分日志输出,一部分是日志级别大于等于warn的、一部分是日志级别大于等于debug的。


接着看一下,是的子节点,是负责写日志的组件。有两个必要属性name和class:


name指定的名称


class指定的全限定名


有好几种,上面我们演示过的是ConsoleAppender,ConsoleAppender的作用是将日志输出到控制台,配置示例为:


1 [/span>appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"

2 [/span>encoder

3 [/span>pattern

4

5

其中,encoder表示对参数进行格式化。我们和上一部分的例子对比一下,发现这里是有所区别的,上面使用了定义


,这里使用了定义


,简单说一下:


是0.9.19版本之后引进的,以前的版本使用,logback极力推荐的是使用而不是


最常用的FileAppender和它的子类的期望是使用而不再使用


关于中的格式下一部分再说。接着我们看一下FileAppender,FileAppender的作用是将日志写到文件中,配置示例为:


1 [/span>appender name="FILE" class="ch.qos.logback.core.FileAppender"

2 [/span>file

3 [/span>append

4 [/span>encoder

5 [/span>pattern

6

7

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
相关文章
|
12天前
|
前端开发 Java API
Java入门教程:掌握Spring MVC的双向数据绑定技术
以上步骤展示了如何利用 Spring MVC 实现双向数据绑定:从显示表单、提交表单、验证输入、直至返回结果页面都涉及到不同层次间交互过程,在整个过程都无需手动去编写繁琐代码去手动获取或设置每一项值。
96 20
|
22天前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
213 1
|
23天前
|
安全 Cloud Native Java
Java 模块化系统(JPMS)技术详解与实践指南
本文档全面介绍 Java 平台模块系统(JPMS)的核心概念、架构设计和实践应用。作为 Java 9 引入的最重要特性之一,JPMS 为 Java 应用程序提供了强大的模块化支持,解决了长期存在的 JAR 地狱问题,并改善了应用的安全性和可维护性。本文将深入探讨模块声明、模块路径、访问控制、服务绑定等核心机制,帮助开发者构建更加健壮和可维护的 Java 应用。
125 0
|
23天前
|
人工智能 Java 开发者
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
JManus是阿里开源的Java版OpenManus,基于Spring AI Alibaba框架,助力Java开发者便捷应用AI技术。支持多Agent框架、网页配置、MCP协议及PLAN-ACT模式,可集成多模型,适配阿里云百炼平台与本地ollama。提供Docker与源码部署方式,具备无限上下文处理能力,适用于复杂AI场景。当前仍在完善模型配置等功能,欢迎参与开源共建。
641 58
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
|
29天前
|
监控 Cloud Native Java
Quarkus 云原生Java框架技术详解与实践指南
本文档全面介绍 Quarkus 框架的核心概念、架构特性和实践应用。作为新一代的云原生 Java 框架,Quarkus 旨在为 OpenJDK HotSpot 和 GraalVM 量身定制,显著提升 Java 在容器化环境中的运行效率。本文将深入探讨其响应式编程模型、原生编译能力、扩展机制以及与微服务架构的深度集成,帮助开发者构建高效、轻量的云原生应用。
140 44
|
1月前
|
SQL Java 数据库连接
区分iBatis与MyBatis:两个Java数据库框架的比较
总结起来:虽然从技术角度看,iBATIS已经停止更新但仍然可用;然而考虑到长期项目健康度及未来可能需求变化情况下MYBATISS无疑会是一个更佳选择因其具备良好生命周期管理机制同时也因为社区力量背书确保问题修复新特征添加速度快捷有效.
89 12
|
2月前
|
Java 测试技术 API
2025 年 Java 开发者必知的最新技术实操指南全览
本指南涵盖Java 21+核心实操,详解虚拟线程、Spring Boot 3.3+GraalVM、Jakarta EE 10+MicroProfile 6微服务开发,并提供现代Java开发最佳实践,助力开发者高效构建高性能应用。
316 4
|
2月前
|
安全 Java 编译器
new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析
逃逸分析是一种静态程序分析技术,用于判断对象的可见性与生命周期。它帮助即时编译器优化内存使用、降低同步开销。根据对象是否逃逸出方法或线程,分析结果分为未逃逸、方法逃逸和线程逃逸三种。基于分析结果,编译器可进行同步锁消除、标量替换和栈上分配等优化,从而提升程序性能。尽管逃逸分析计算复杂度较高,但其在热点代码中的应用为Java虚拟机带来了显著的优化效果。
64 4
|
2月前
|
存储 缓存 安全
Java集合框架(三):Map体系与ConcurrentHashMap
本文深入解析Java中Map接口体系及其实现类,包括HashMap、ConcurrentHashMap等的工作原理与线程安全机制。内容涵盖哈希冲突解决、扩容策略、并发优化,以及不同Map实现的适用场景,助你掌握高并发编程核心技巧。
|
2月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
165 23

热门文章

最新文章