大家好,我是晓凡
一、日记概念
日记的紧张性不用我多说了,日记,简朴来说就是记录。
用来记录程序运行时发生的事变。比如,程序启动了、执行了某个操作、遇到了题目等等,这些都可以通过日记记录下来。
想象一下,你开了一家店,每天的业务额、顾客的反馈、商品的进出、库存等等,你都会记录下来。这就像是程序的日记。比如:
- 电商网站:记任命户的登录、浏览、购买举动,监控生意业务过程,实时发现异常生意业务;通过日记分析你的浏览记录,实现精准推送等等
- 服务器:记录服务器的启动、运行、关闭状态,以及发生的各种错误,资助管理员实时发现并解决题目。
1.1 日记的作用
- 调试资助:当程序出现题目时,通过检察日记,可以快速定位题目发生的地方和缘故因由。
- 监控运行状态:通过日记可以了解程序的运行状态,比如用户的操作、系统的性能等。
- 安全审计:在需要记任命户举动或系统操作的场合,日记可以作为审计的依据。
1.2 具体示例
- public class SimpleApp {
- public static void main(String[] args) {
- System.out.println("程序启动");
- // 假设这里是用户输入数据
- String userInput = "Hello, World!";
- System.out.println("用户输入了: " + userInput);
- // 处理数据
- String result = processInput(userInput);
- System.out.println("处理结果: " + result);
- try {
- //可能异常的逻辑代码
- }catch(Exception e){
- e.printStackTrace()
- }
- // 程序结束
- System.out.println("程序结束");
- }
- private static String processInput(String input) {
- // 这里是处理逻辑
- return "Processed: " + input;
- }
- }
复制代码 上面的代码我们不陌生了吧,我们使用System.out.println来打印程序的运行状态,使用e.printStackTrace()来打印信息和错误
这就是没有日记框架时,最简朴直接的日记打印方式
这种方式简朴直接,但也有一些缺点:
- 机动性差:不能方便地控制日记的输出格式、级别等。
- 性能题目:大量日记输出大概会影响程序性能。
- 不易管理:日记信息混在标准输出中,不易于查找和分析。
以是我们要引入各种功能强大的日记框架进行日记管理
二、主流日记框架
日记框架由日记门面和日记实现构成,具体如下图所示
2.1 日记门面
顾名思义,日记门面,就像是一个团队的领导者一样,只负责制定规则,安排任务,而具体干活的则交给苦逼的打工人(日记具体实现)即可。
日记门面提供了一套标准的日记记录接口,而具体的日记记录工作则由不同的日记框架来完成。
这样做的好处是,可以在不修改代码的情况下,通过配置来切换不同的日记框架。
正如职场中,一个打工人跑路了,在不需要太多本钱,不用做太多改变的情况下,新招一个更便宜的打工人也可完成同样的任务实现快速切换,好像有点扯远了
主流的日记门面框架主要有:
- SLF4J:这是一个非常流行的日记门面,它提供了一套简朴的日记记录接口,并且可以与多种日记框架(如Log4j、Logback等)配合使用。
- JCL:这是早期的一个日记门面
2.2 日记实现
通过是实现日记门面接口来完成日记记录,实实在在的打工人无疑了
主流的日记实现框架有:
- JUL
Java自带的日记框架 ,功能相对底子,性能一样平常,但对于简朴的日记需求来说足够用了。
- Log4j
个非常老牌的日记框架,功能非常强大,可以自定义很多日记的细节,比如日记级别、输出格式、输出目标地等。现由Apache软件基金会维护
也是Apache软件基金会开发,相比Log4j,Log4j2在性能上有明显提拔,同时保持了丰富的功能,支持异步日记处置惩罚,得当高性能需求的场景
由Log4j的原开发者之一主导开发,Spring Boot默认日记,轻量级,性能优秀,功能也比力全面
三、JUL日记框架
3.1 主要组件
- Logger:日记记录器,是日记系统的焦点,用来生成日记记录。
- Handler:日记处置惩罚器,负责将日记信息输出到不同的目标地,比如控制台、文件等。可以为每个Logger配置一个或多个Handler
- Formatter:日记格式化器,负责定义日记的输出格式。比如时间戳、日记级别、消息等。
- Level:设置日记级别,常见的级别有SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST等。
- Filter: 这个组件用来过滤日记记录。你可以设置一些规则,只有满足这些规则的日记才会被记录。
- Log Record:这是日记记录本身,包含了日记的所有信息,比如时间、日记级别、消息等
3.2 使用步骤
- 获取Logger实例。
- 添加Handler
- 为上一步添加的Handler 设置日记级别(Level)和格式输出(Formatter)
- 创建Filter过滤器
- 为Logger实例添加日记处置惩罚器(Handler)和日记过滤器(Filter)
- 记录日记。
3.3 入门案例
- public class LogQuickTest {
- @Test
- public void testLogQuick(){
- //创建日志记录对象
- Logger logger = Logger.getLogger("com.xiezhr");
- //日志记录输出
- logger.info("这是一个info日志");
- logger.log(Level.INFO,"这是一个info日志");
- String name="程序员晓凡";
- Integer age=18;
- logger.log(Level.INFO,"姓名:{0},年龄:{1}",new Object[]{name,age});
- }
- }
复制代码
3.4 日记级别
日记级别系统,用来区分日记的紧张性
3.4.1 日记级别
- SEVERE(严峻):这是最高级别的日记,用来记录严峻错误,比如系统崩溃、数据丢失等。这类日记通常需要立刻关注和处置惩罚。
- WARNING(告诫):用来记录大概不会立刻影响系统运行,但大概表明潜在题目标信息。比如,某个操作没有达到预期效果,大概系统资源靠近耗尽。
- INFO(信息):用来记录一样平常性的信息,比如程序运行的状态、紧张的操作步骤等。这类信息对于了解程序的运行情况很有资助,但通常不需要立刻处置惩罚。
- CONFIG(配置):用来记录配置信息,比如程序启动时加载的配置文件、初始化的参数等。这类日记有助于调试和验证程序的配置是否正确。
- FINE(具体):用来记录更具体的信息,比如程序内部的执行细节、变量的值等。这类日记对于开发者在调试程序时了解程序的内部状态非常有用。
- FINER(更具体):比FINE级别更细的日记,记录更深入的执行细节。通常用于深入分析程序的运行情况。
- FINEST(最具体):这是最低级别的日记,记录最具体的信息,包括程序的每一步执行细节。这类日记大概会产生大量的输出,通常只在需要非常具体的调试信息时使用。
3.4.2 级别关系
SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST
日记级别越高,记录的信息越紧张。当你设置一个日记级别时,比如INFO,那么INFO级别以及以上的日记(SEVERE和WARNING)都会被记录,而FINE、FINER和FINEST级别的日记则会被忽略
3.5 具体使用案例(硬编码)
这里我们按照上面的步骤创建一个日记记录器,将日记文件分别输出到控制台和文件中
- public class LoggingExampleTest {
- @Test
- public void testLogging() {
- // 获取日志记录器
- Logger logger = Logger.getLogger("LoggingExample");
- // 设置日志级别为INFO,这意味着INFO级别及以上的日志会被记录
- logger.setLevel(Level.INFO);
- // 创建控制台Handler 将日志输出到控制台
- // 并设置其日志级别和Formatter
- ConsoleHandler consoleHandler = new ConsoleHandler();
- consoleHandler.setLevel(Level.WARNING); // 控制台只输出WARNING及以上级别的日志
- consoleHandler.setFormatter(new SimpleFormatter() {
- @Override
- public synchronized String format(LogRecord record) {
- // 自定义日志格式
- return String.format("%1$tF %1$tT [%2$s] %3$s %n", record.getMillis(), record.getLevel(), record.getMessage());
- }
- });
- logger.addHandler(consoleHandler);
- // 创建文件Handler 将日志输出到文件
- // 并设置其日志级别和Formatter
- try {
- FileHandler fileHandler = new FileHandler("app.log", true);
- fileHandler.setLevel(Level.ALL); // 文件将记录所有级别的日志
- fileHandler.setFormatter(new SimpleFormatter() {
- @Override
- public synchronized String format(LogRecord record) {
- // 自定义日志格式
- return String.format("%1$tF %1$tT [%2$s] %3$s %n", record.getMillis(), record.getLevel(), record.getMessage());
- }
- });
- logger.addHandler(fileHandler);
- } catch (IOException e) {
- e.printStackTrace();
- }
- // 创建并设置Filter
- Filter filter = new Filter() {
- @Override
- public boolean isLoggable(LogRecord record) {
- // 这里可以添加过滤逻辑,例如只记录包含特定字符串的日志
- return record.getMessage().contains("important");
- }
- };
- // 将Filter应用到Logger
- //logger.setFilter(filter);
- // 记录不同级别的日志
- logger.severe("严重错误信息 - 应记录到控制台和文件");
- logger.warning("警告信息 - 应记录到控制台和文件");
- logger.info("常规信息 - 只记录到文件");
- logger.config("配置信息 - 只记录到文件");
- logger.fine("详细日志 - 只记录到文件");
- // 这条日志将被Filter过滤掉,不会记录
- logger.info("这条信息不重要,将被过滤");
- // 这条日志将被记录,因为消息中包含"important"
- logger.info("这条信息很重要,将被记录到控制台和文件");
- }
- }
复制代码 ① 控制台日记输出

②日记文件输出 app.log内容

代码表明:
- Logger获取:起首获取一个名为LoggingExample的Logger实例。
- 设置日记级别:将Logger的日记级别设置为INFO,这意味着INFO及以上级别的日记将被记录。
- 控制台Handler:创建一个ConsoleHandler实例,设置其日记级别为WARNING,并且自定义了日记的输出格式。
- 文件Handler:尝试创建一个FileHandler实例,将日记写入到app.log文件中,并设置其日记级别为ALL,意味着所有级别的日记都将被记录到文件。
- 自定义Formatter:为Handler创建自定义的SimpleFormatter,用于定义日记的输出格式。
- Filter设置:创建一个实现了Filter接口的匿名内部类,并重写isLoggable方法,实现过滤逻辑,这里只记录消息中包含"important"字符串的日记。
- 应用Filter:将创建的Filter应用到Logger上。
- 记录日记:记录不同级别的日记,展示不同级别的日记如何被Handler和Filter处置惩罚。
- 日记记录:一些日记将根据设置的日记级别、Handler和Filter的规则被记录到控制台或文件,大概被忽略。
3.6 日记配置文件
以上3.4小节通过硬编码的方式打印输出日记,这样的方式很不利于后期的管理与维护,这小节我们将使用配置文件的方式进行日记输出
① 在resources下面新建logconfig.properties文件,内容如下- # 指定日志处理器为:ConsoleHandler,FileHandler 表示同时使用控制台和文件处理器
- handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
- #设置默认的日志级别为:ALL
- .level= ALL
- # 配置自定义 Logger
- com.xiezhr.handlers = com.xiezhr.DefConsoleHandler
- com.xiezhr.level = CONFIG
- # 如果想要使用自定义配置,需要关闭默认配置
- com.xiezhr.useParentHanlders =true
- # 向日志文件输出的 handler 对象
- # 指定日志文件路径 当文件数为1时 日志为/logs/java0.log
- java.util.logging.FileHandler.pattern = /logs/java%u.log
- # 指定日志文件内容大小,下面配置表示日志文件达到 50000 字节时,自动创建新的日志文件
- java.util.logging.FileHandler.limit = 50000
- # 指定日志文件数量,下面配置表示只保留 1 个日志文件
- java.util.logging.FileHandler.count = 1
- # 指定 handler 对象日志消息格式对象
- java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
- # 指定 handler 对象的字符集为 UTF-8 ,防止出现乱码
- java.util.logging.FileHandler.encoding = UTF-8
- # 指定向文件中写入日志消息时,是否追加到文件末尾,true 表示追加,false 表示覆盖
- java.util.logging.FileHandler.append = true
- # 向控制台输出的 handler 对象
- # 指定 handler 对象的日志级别
- java.util.logging.ConsoleHandler.level =WARNING
- # 指定 handler 对象的日志消息格式对象
- java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
- # 指定 handler 对象的字符集
- java.util.logging.ConsoleHandler.encoding = UTF-8
- # 指定日志消息格式
- java.util.logging.SimpleFormatter.format = [%1$tF %1$tT] %4$s: %5$s %n
复制代码 注意: 设置日记消息格式中(背面一小节会具体讲解)
- %1$tF:这个占位符表示日记记录的时间,格式为 yyyy-MM-dd,此中 1$ 表示这是第一个参数tF 是日期的格式化代码
- %1$tT:这个占位符表示日记记录的时间,格式为 HH:mm:ss.SSS,即小时:分钟:秒.毫秒 1$ 表示这是第一个参数,tT 是时间的格式化代码
- %4$s: 表示日记级别,level =WARNING 输出告诫 level =INFO 输出消息
- %5$s: 表示日记消息
- %n:这个占位符表示换行符,每条日记记录之后会有一个换行,以便在检察日记时能够清晰地区分每条记录。
② 日记测试- @Test
- public void testLogProperties()throws Exception{
- // 1、读取配置文件,通过类加载器
- InputStream ins = LoggingExampleTest.class.getClassLoader().getResourceAsStream("logconfig.properties");
- // 2、创建LogManager
- LogManager logManager = LogManager.getLogManager();
- // 3、通过LogManager加载配置文件
- logManager.readConfiguration(ins);
- // 4、创建日志记录器
- Logger logger = Logger.getLogger("com.xiezhr");
- // 5、记录不同级别的日志
- logger.severe("这是一条severe级别信息");
- logger.warning("这是一条warning级别信息");
- }
复制代码 执行上面代码后
控制台输出:
java0.log文件输出:
3.7 日记格式化
上面两个小节中,不管是通过编码大概配置文件 都对日记进行了格式化
① 编码设置日记格式- fileHandler.setFormatter(new SimpleFormatter() {
- @Override
- public synchronized String format(LogRecord record) {
- // 自定义日志格式
- return String.format("%1$tF %1$tT [%2$s] %3$s %n", record.getMillis(), record.getLevel(), record.getMessage());
- }
- });
复制代码 ② 配置文件指定日记格式- # 指定日志消息格式
- java.util.logging.SimpleFormatter.format = [%1$tF %1$tT] %4$s: %5$s %n
复制代码 上面设置的日记格式设置你看懂了么?
不管是哪种方式设置日记格式,我们看源码最终都是通过String.format函数来实现的,所有我们有必要学一学String类提供的format这个方法的使用
3.7.1 String的format方法
String的format方法用来格式化字符串。
format方法就像是一个模板,你可以在这个模板里插入你想要的数据,然后它就会帮你生成一个格式化好的字符串。
我们先来看看下面这个简朴例子- @Test
- public void testStringFormatter()throws Exception{
- String name = "晓凡";
- Integer age = 18;
- // 使用String.format()方法格式化字符串
- String xiaofan = String.format("%s今年%d岁", name, age);
- System.out.println(xiaofan);
- }
- //输出
- 晓凡今年18岁
复制代码 3.7.2 常用占位符
%s和%d 为占位符,不同范例需要不同占位符,那么另有哪些常用转换符呢?
占位符具体说明示例%s字符串范例****“喜欢晓凡请关注”%c字符范例‘x’%b布尔范例true%d整数范例(十进制)666%x整数范例(十六进制)FF%o整数范例(八进制)77%f浮点范例8.88%a十六进制浮点范例FF.34%e指数范例1.28e+5%n换行符%tx日期和时间范例(x代表不同的日期与时间转换符)3.7.3 特殊符号搭配使用
符号说明示例效果0指定数字、字符前面补0,用于对齐("%04d",6)0006空格指定数字、字符前面补空格,用于对齐("[% 4s]",x)[ x],以“,”对数字分组显示(常用于金额)("%,f,666666.66")666,666.6600注意: 默认情况下,可变参数是按照次序依次更换,但是我们可以通过“数字$”来重复使用可变参数- @Test
- public void testStringFormatter()throws Exception{
- String name = "晓凡";
- Integer age = 18;
- // 使用String.format()方法格式化字符串
- String xiaofan = String.format("%s今年%d岁", name, age);
- System.out.println(xiaofan);
- //
- String xiaofan1 = String.format("%s今年%d岁,%1$s的公众号是:程序员晓凡", name, age);
- System.out.println(xiaofan1);
- }
- //输出
- 晓凡今年18岁
- 晓凡今年18岁,晓凡的公众号是:程序员晓凡
复制代码 上面例子中我们通过%1$s重复使用第一个参数name
3.7.4 日期格式化
上面我们说到%tx,x代表日期转换符,其具体含义如下
符号描述示例c包含全部日期和时间信息周六 8月 03 17:16:37 CST 2024F"年-月-日" 格式2024-08-03D"月/日/年"格式08/03/24d日03r“HH:MM:SS PM”格式(12小时制)05:16:37 下午R“HH:MM”格式(24小时制)17:16T“HH:MM:SS ”格式(24小时制)17:16:37b月份本地化8月y两位年24Y四位年2024m月08H时(24小时制)17I时(12小时制)05M分16S秒37s秒为单位的时间戳1722677530p上午照旧下午下午四、Log4j日记框架
Log4j 是Apache软件基金组织旗下的一款开源日记框架,是一款比力老的日记框架,目前已出log4j2,它在log4j上做了很大改动,性能提拔了不少。但是有些老项目还会在使用,以是我们也来说一说
官网:https://logging.apache.org/log4j/1.x/
注意: 从官网,我们可以看到项目管理委员会公布Log4j 1. x已制止使用。建议用户升级到 Log4j 2
4.1 快速入门
4.1.1 添加依靠
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13.2</version>
- <scope>test</scope>
- </dependency>
复制代码 4.1.2 log4j入门代码
- @Test
- public void testLog4jQuick(){
- //初始化日志配置信息,不需要配置文件
- BasicConfigurator.configure();
- //获取日志记录器
- Logger logger = Logger.getLogger(Log4jTest.class);
- //通过各种日志级别打印日志
- logger.fatal("这是一条致命的信息"); // 严重错误,一般会造成系统崩溃
- logger.error("这是一条错误的信息"); // 出现错误时,比如出错了但是不影响系统继续运行
- logger.warn("这是一条警告的信息"); // 警告级别,比如要告警的时候
- logger.info("这是一条普通的信息"); // 一般信息,比如记录普通的方法执行
- logger.debug("这是一条调试的信息"); // 调试信息,比如调试的时候打印的信息
- logger.trace("这是一条追踪的信息"); // 追踪信息,比如追踪程序运行路径
- }
- //输出
- 0 [main] FATAL Log4jTest - 这是一条致命的信息
- 0 [main] ERROR Log4jTest - 这是一条错误的信息
- 0 [main] WARN Log4jTest - 这是一条警告的信息
- 0 [main] INFO Log4jTest - 这是一条普通的信息
- 0 [main] DEBUG Log4jTest - 这是一条调试的信息
复制代码 注意: BasicConfigurator.configure(); 为log4j在不添加配置文件的情况下初始化默认日记配置信息,如果既没有默认配置信息,也没有配置文件
会报下面错误
4.2 日记级别
日记级别,就比如是日记本里的不同标记,用来区分信息的紧张性。在log4j中,日记级别从低到高分为以下几种:
- TRACE:追踪级别,通常用来记录程序运行的具体轨迹,比如方法调用的次序等。这个级别非常具体,一样平常在开发阶段大概调试时用得比力多。
- DEBUG:调试级别,用来记录程序的运行状态,比如变量的值、程序的流程等。当你需要深入了解程序的内部工作时,DEBUG级别就非常有用。
- INFO:信息级别,用来记录程序的正常运行状态,比如程序启动、配置信息、正常结束等。INFO级别的日记对用户和开发者了解程序的运行情况很有资助。
- WARN:告诫级别,用来记录一些大概引起题目标情况,但程序仍旧可以继续运行。比如,程序遇到了一个不常见的情况,大概某个操作失败了但不影响大局。
- ERROR:错误级别,用来记录程序运行中的错误,这些错误通常会影响程序的正常功能,但程序大概还能继续运行。
- FATAL:致命级别,用来记录非常严峻的错误,这些错误会导致程序完全无法继续运行。比如,程序的某个关键部分失败了,整个应用大概需要重启。
出了上面的,另有以下两个特殊级别- 1. **OFF**: 用来关闭日志记录
- 1. **ALL**: 启用所有消息的日志记录
复制代码 4.3 Log4j组件
- Logger:这个组件就像是日记的大脑,负责记录日记信息。你可以想象它是一个日记本的主人,决定哪些事变值得记录,哪些事变可以忽略。
- Appender:Appender就像是日记本的笔,它决定了日记信息要写到哪里。可以是控制台、文件、数据库,乃至是通过网络发送到远程服务器。每种Appender都有不同的用途和特点。
- Layout:Layout决定了日记的表面,也就是日记的格式。比如,你可以选择日记中包含时间、日记级别、发生日记的类名和方法名,以及日记的具体内容等。Layout就像是给日记本设计表面样式。
4.3.1 Logger
Log4j中有一个特殊的logger叫做root,它是logger的根,其他的logger都会直接大概间接的继续自root。
入门示例中,我们通过Logger.getLogger(Log4jTest.class); 获取的就是root logger
name为org.apache.commons 的logger会继续name为org.apache的logger
4.3.2 Appender
用来指定日记记录到哪儿,主要有以下几种
Appender范例作用ConsoleAppender将日记输出到控制台FileAppender将日记输出到文件中DailyRollingFileAppender将日记输出到文件中,并且每天输出到一个日记文件中RollingFileAppender将日记输出到文件中,并且指定文件的大小,当文件大于指定大小,会生成一个新的日记文件JDBCAppender将日记生存到数据库中4.3.3 Layout
用于控制日记内容输出格式,Log4j常用的有以下几种输出格式
日记格式器说明HTMLLayout将日记以html表格形式输出SimpleLayout简朴的日记格式输出,例如(info-message)PatternLayout最强大的格式化器,也是我们使用最多的一种,我们可以自定义输出格式示例:下面我们通过PatternLayout 格式化日记- @Test
- public void testLog4jLayout(){
- //初始化日志配置信息,不需要配置文件
- BasicConfigurator.configure();
- //获取日志记录器
- Logger logger = Logger.getLogger(Log4jTest.class);
- Layout patternLayout = new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n");// 将自定义的Layout应用到控制台Appender上
- ConsoleAppender consoleAppender = new ConsoleAppender(patternLayout);
- logger.addAppender(consoleAppender);
- // 记录日志
- logger.info("这是一条自定义格式的日志信息");
- }
- //输出
- 2024-08-04 13:55:35 [INFO] - Log4jTest.testLog4jLayout(Log4jTest.java:44) - 这是一条自定义格式的日志信息
复制代码 占位符说明%m输出代码中指定的日记信息%p输出优先级%n换行符%r输出自应用启用到输出log信息消耗的毫秒数%c输出语句所属的类全名%t输出线程全名%d输出服务器当前时间,%d%l输出日记时间发生的位置,包括类名、线程、及在代码中的函数 例如: Log4jTest.testLog4jLayout(Log4jTest.java:44)%F输出日记消息产生时所在的文件夹名称%L输出代码中的行号%5ccategory名称不足5位时,左边增补空格,即右对齐%-5ccategory名称不足5位时,右边增补空格,即左对齐.5ccategory名称大于5位时,会将左边多出的字符截取掉,小于5位时,以空格增补4.4 通过配置文件配置日记
BasicConfigurator.configure(); 上面代码中通过这段代码初始化日记配置信息,这一小节,我们通过配置文件来配置
通过看LogManager日记管理器源码,我们知道可以默认加载如下几种格式的配置文件(此中log4j.xml 和log4j.properties 是我们最常用的)
- log4j.properties
- log4j.xml
- og4j.configuration
等等
- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,Console
- # 指定控制台日志输出appender
- log4j.appender.Console = org.apache.log4j.ConsoleAppender
- # 指定消息格式器 layout
- log4j.appender.Console.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.Console.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
复制代码 大概- <?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration>
复制代码 4.5 各种日记输出示例
上面小节中已经说了控制台输出配置,由于篇幅缘故因由,这里不再赘述
① 文件输出配置- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,File
- # 指定文件日志输出appender
- log4j.appender.File = org.apache.log4j.FileAppender
- # 指定日志文件名
- log4j.appender.File.File=D:/logs/testxiezhr.log
- # 指定是否在原有日志的基础添加新日志
- log4j.appender.File.Append=true
- # 指定消息格式器 layout
- log4j.appender.File.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.File.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
- # 指定日志文件编码格式
- log4j.appender.File.encoding=UTF-8
复制代码 ②日记文件根据大小分割输出- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,RollingFile
- # 指定文件日志根据大小分割输出appender
- log4j.appender.RollingFile = org.apache.log4j.RollingFileAppender
- # 指定日志文件名
- log4j.appender.RollingFile.File=D:/logs/testxiezhr.log
- # 设置是否在重新启动服务时,在原有日志的基础添加新日志
- log4j.appender.RollingFile.Append=true
- # 设置最多保存的日志文件个数
- log4j.appender.RollingFile.MaxBackupIndex=5
- # 设置文件大小,超过这个值,就会再产生一个文件
- log4j.appender.RollingFile.maximumFileSize=1
- # 指定消息格式器 layout
- log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.RollingFile.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
- # 指定日志文件编码格式
- log4j.appender.RollingFile.encoding=UTF-8
复制代码 最终生成日记效果如下所示

③ 日记文件根据日期分割- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,DailyRollingFile
- # 指定文件日志根据日期分割输出appender
- log4j.appender.DailyRollingFile = org.apache.log4j.DailyRollingFileAppender
- # 指定日志文件名
- log4j.appender.DailyRollingFile.File=D:/logs/testxiezhr.log
- # 设置是否在重新启动服务时,在原有日志的基础添加新日志
- log4j.appender.DailyRollingFile.Append=true
- # 指定消息格式器 layout
- log4j.appender.DailyRollingFile.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.DailyRollingFile.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
- # 指定日志文件编码格式
- log4j.appender.DailyRollingFile.encoding=UTF-8
复制代码 最终生成日记效果如下所示
④ 自定义日记配置
当我们想定义自己的日记配置时,可以按照如下配置添加.例如:添加com.xiezhr,它也是继续自rootLogger,以是我们必须要添加
log4j.additivity.com.xiezhr=false 避免日记打印重复
- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,DailyRollingFile
- # 自定义日志配置
- log4j.logger.com.xiezhr=DEBUG,Console
- # 设置日志叠加,这一句配置一定要添加,否则日志会重复输出
- log4j.additivity.com.xiezhr=false
复制代码 ⑤ 将日记信息存入数据库
起首,我们新建一个testlog数据库,并在数据库下新建log日记表- CREATE TABLE `log` (
- `log_id` int(11) NOT NULL AUTO_INCREMENT,
- `project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
- `create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
- `level` varchar(255) DEFAULT NULL COMMENT '优先级',
- `category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
- `file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
- `thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
- `line` varchar(255) DEFAULT NULL COMMENT '号行',
- `all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
- `message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
- PRIMARY KEY (`log_id`)
- );
复制代码 其次,新建JDBCAppender,并且为JDBCAppender 设置数据库连接信息,具体代码如下- @Test
- public void testLog4j2db(){
- //初始化日志配置信息,不需要配置文件
- BasicConfigurator.configure();
- //获取日志记录器
- Logger logger = Logger.getLogger(Log4jTest.class);
- // 新建JDBCAppender
- JDBCAppender jdbcAppender = new JDBCAppender();
- jdbcAppender.setDriver("com.mysql.cj.jdbc.Driver");
- jdbcAppender.setURL("jdbc:mysql://localhost:3308/testlog?useSSL=false&serverTimezone=UTC");
- jdbcAppender.setUser("root");
- jdbcAppender.setPassword("123456");
- jdbcAppender.setSql("INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('晓凡日志测试','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')");
- logger.addAppender(jdbcAppender);
- // 记录日志
- logger.info("这是一条自定义格式的日志信息");
- logger.error("这是一条自定义格式的错误日志信息");
- }
复制代码 最后,运行代码,来看一下效果
五、JCL日记门面
作甚日记门面,我们在第二小节中已经介绍过了,这里就不多说了。
日记门面的引入,使得我们可以面向接口开发,不再依靠具体的实现类,减小代码耦合。
JCL全称Jakarta Commons Logging是Apache提供的一个通用日记API,JCL中自带一个日记实现simplelog,不过这个功能非常简朴
5.1 JCL快速入门
① LCL的两个抽象类
- Log: 根本日记记录器
- LogFactory: 负责创建Log具体实例,如果时log4j,则创建log4j的实例,如果时jul则创建jul实例
② 示例代码
引入依靠- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- </dependency>
复制代码 根本代码
我们没有导入任何日记实现,以是这里默认使用jdk自带JUL来实现日记
- @Test
- public void test(){
- Log log = LogFactory.getLog(JclTest.class);
- log.error("这是一条error");
- log.warn("这是一条warn");
- log.info("这是一条info");
- log.debug("这是一条debug");
- log.trace("这是一条trace");
- }
复制代码
5.2 快速切换Log4j日记框架
① 导入log4j日记依靠- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
复制代码 ② 添加log4j.properties配置文件- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,Console
- # 指定控制台日志输出appender
- log4j.appender.Console = org.apache.log4j.ConsoleAppender
- # 指定消息格式器 layout
- log4j.appender.Console.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.Console.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
复制代码 ③ 测试日记输出- @Test
- public void testJclLog4j(){
- Log log = LogFactory.getLog(JclLog4jTest.class);
- log.error("这是一条error");
- log.warn("这是一条warn");
- log.info("这是一条info");
- log.debug("这是一条debug");
- log.trace("这是一条trace");
- }
复制代码 日记输出如下:
我们可以看到,使用了JCL日记门面之后,我们从simplelog日记框架切换到log4j日记框架,没有改过代码。
六、SLF4j日记门面
SLF4j 全称是Simple Logging Facade For JavaJava简朴的日记门面 和上一小节说到的JCL干的一样的活。
在现目前的大多数Java项目中,日记框架根本上会选择slf4j-api 作为门面,配上具体实现框架logback、log4j 等使用
SLF4j是目前市面上最流行的日记门面,主要提供了以下两个功能
6.1 快速入门
① 添加依靠- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>2.0.13</version>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>2.0.13</version>
- </dependency>
复制代码 ②日记输出- //申明日志对象
- public final static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
- @Test
- public void testSlf4j(){
- //打印日志
- logger.error("这是error日志");
- logger.warn("这是warn日志");
- logger.info("这是info日志");
- logger.debug("这是debug日志");
- logger.trace("这是trace日志");
- //使用占位符输出日志信息
- String name = "晓凡";
- Integer age = 18;
- logger.info("{},今年{}岁", name, age);
- //将系统异常写入日志
- try {
- int i = 1/0;
- }catch (Exception e){
- logger.error("执行出错", e);
- }
- }
复制代码 上面代码输出日记如下
6.2 SLF4j 日记绑定功能
6.2.1 日记绑定原理
下图是从官网薅下来的slf4j 日记绑定图,对了,官网在这https://www.slf4j.org/
小同伴看到上图大概会有点懵,满是英文,看不懂。
于是乎,晓凡简朴翻译了一下,如下如所示
- 只导入日记门面,没导入日记实现,不会进行日记输出
- logback、simplelog、no-operation 框架遵循SLF4j规范 导入jar包即可使用
- log4j 、JUL 属于比力古老日记框架,不遵循SLF4j规范,需要引入适配器才气使用
- 当我们导入slf4j-nop后将不会使用任何日记框架
6.2.2 绑定logback日记框架
① 引入logback依靠- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>1.4.14</version>
- </dependency>
复制代码 ② 日记输出
快速入门中代码不变,运行后,采用logback日记框架输入日记如下所示
6.2.3 绑定slf4j-nop
① 引入依靠- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-nop</artifactId>
- <version>2.0.13</version>
- </dependency>
复制代码 ② 此时控制台将不会输出任何日记
6.2.4 使用适配器绑定log4j日记框架
① 导入依靠- org.slf4j slf4j-log4j12 2.0.13<dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
复制代码 ② 添加log4j.properties配置文件- # 指定RootLogger顶级父元素默认配置信息
- # 指定日志级别位INFO,使用的appender 位Console
- log4j.rootLogger=INFO,Console
- # 指定控制台日志输出appender
- log4j.appender.Console = org.apache.log4j.ConsoleAppender
- # 指定消息格式器 layout
- log4j.appender.Console.layout=org.apache.log4j.PatternLayout
- # 指定消息内容格式
- log4j.appender.Console.layout.conversionPattern =%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n
复制代码 ③ 代码不变,日记输出如下
6.2.5 使用适配器绑定JUL日记框架
① 引入依靠- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-jdk14</artifactId>
- <version>2.0.13</version>
- </dependency>
复制代码 ② 代码不变,日记输出如下
6.3 SLF4j日记桥接
6.3.1 使用场景
如果你的项目中已经使用了Log4j 1.x等老的日记框架,但你想迁移到使用SLF4J的API,这时间你可以使用SLF4J的Log4j 1.x桥接器来平滑过渡
6.3.2 桥接原理
上图为SLF4j官网提供的桥接原理图,从图中,我们可以看到,只需要引入不同的桥接器log4j-over-slf4j、jul-to-slf4j 、jcl-over-slf4j
就可以实现在不改变原有代码的情况下,将日记从log4j、jul、jcl迁移到slf4j+logback日记组合
6.3.3 桥接步骤
下面以Log4j 1.x迁移到slf4j+logback日记组合为例
- 添加SLF4J提供的桥接组件
- 为项目添加SLF4J的具体实现
七、Logback日记框架
官网:https://logback.qos.ch/index.html
7.1 快速入门
① 添加依靠- org.slf4j slf4j-api 2.0.13<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>1.4.14</version>
- </dependency>
复制代码 ② 打印日记代码- public class LogbackTest {
- private static final Logger logger = LoggerFactory.getLogger(LogbackTest.class);
- @Test
- public void testLogbackQuick(){
- logger.error("这是一个错误日志");
- logger.warn("这是一个警告日志");
- logger.info("这是一个信息日志");
- logger.debug("这是一个调试日志");
- logger.trace("这是一个跟踪日志");
- }
- }
复制代码
7.2 Logback配置
Logback可以通过编程式配置(添加配置类的方式),也可以通过配置文件配置。
配置文件是日常开发中最常用的,我们这里就以这种方式配置,如果对配置文件感兴趣的小同伴可自行到官网检察
7.2.1 Logback 包含哪些组件?
- Logger:日记记录器,用来记录不同级别的日记信息,比如错误、告诫、信息、调试和追踪。
- Appender:指定日记信息输出到不同的地方。比如,你可以设置一个Appender将日记输出到控制台,另一个Appender将日记写入文件,大概发送到远程服务器。
- Encoder:如果你使用的是文件Appender,Encoder就是用来定义日记文件内容格式的。比如,你可以选择日记的格式是简朴文本照旧XML。
- Layout:老版本的Logback中用来定义日记格式的组件。在新版本中,Encoder已经取代了Layout的功能。
- Filter:指定特定的规则来过滤日记信息,比如只记录错误以上的日记,大概只记录包含特定关键字的日记。
- Configuration:用来配置Logback的设置,比如设置日记级别、Appender的范例和参数等。配置可以通过XML、JSON大概Groovy脚本来完成。
7.2.2 可以有哪些文件格式进行配置?
Logback会依次读取以下范例配置文件
- logback.groovy
- logback-test.xml
- logback.xml(最常用的)
如果均不存在会采用默认配置
7.2.3 添加一个ConsoleAppender控制台日记输出配置
配置文件- <?xml version="1.0" encoding="UTF-8"?>
- <configuration>
-
-
- <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread]%-5level %msg%n" />
-
- <appender name="console" >
- <encoder>
-
- <pattern>${pattern}</pattern>
- </encoder>
- </appender>
-
- <root level="ALL">
-
- <appender-ref ref="console" />
- </root>
- </configuration>
复制代码 日记输入如下
日记输出格式:在前面几个日记框架中我们已经介绍过,大同小异。这里简朴说下常用的几种
符号含义%d{pattern}格式化日期%m大概%msg日记信息%Mmethod(方法)%L行号%c完整类名称%thread线程名称%n换行%-5level日记级别,并且左对齐7.2.4 添加一个FileAppender将日记输出到文件
配置文件- <?xml version="1.0" encoding="UTF-8"?>
- <configuration>
-
-
- <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread]%-5level %msg%n" />
-
- <property name="log_file" value="d:/logs"></property>
-
- <appender name="file" >
- <encoder>
-
- <pattern>${pattern}</pattern>
- </encoder>
-
- <file>${log_file}/logback.log</file>
- </appender>
-
- <root level="ALL">
-
- <appender-ref ref="file" />
- </root>
- </configuration>
复制代码 日记输出如下
7.2.5 生成html格式appender对象
- <?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration> ${pattern}<appender name="async" >
- <appender-ref ref="console" />
- </appender>
- <root level="ALL">
-
- <appender-ref ref="console" />
-
- <appender-ref ref="async" />
- </root> ${log_file}/logback.html<appender name="async" >
- <appender-ref ref="console" />
- </appender>
- <root level="ALL">
-
- <appender-ref ref="console" />
-
- <appender-ref ref="async" />
- </root>
复制代码 日记输出: 在d:/logs目录下生成一个logback.html 文件
7.3 Logback 日记拆分压缩 ⭐
在生产情况中对日记进行按时间、日记大小拆分 且压缩日记非常非常紧张,以是单独拿出来说一说
配置文件- <?xml version="1.0" encoding="UTF-8"?>
- <configuration>
-
-
- <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n" />
-
- <property name="log_file" value="d:/logs"></property>
-
- <appender name="rollFile" >
- <encoder >
-
- <pattern>${pattern}</pattern>
- </encoder>
-
- <file>${log_file}/roll_logback.log</file>
-
- <rollingPolicy >
-
- <fileNamePattern>${log_file}/roll_logback.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
-
- <maxFileSize>1MB</maxFileSize>
-
- <MaxHistory>3</MaxHistory>
- </rollingPolicy>
- </appender>
-
- <root level="ALL">
-
- <appender-ref ref="rollFile" />
- </root>
- </configuration>
复制代码 日记滚动输出: 按照日期和文件大小进行拆分
7.4 异步日记
我们先来表明下什么是异步日记?
我们将日记输出到文件中,这样会涉及到大量io操作,非常耗时,如果需要输出大量的日记,就大概影响正常的主线程业务逻辑。
为了解决这题目,异步日记就出现了。日记信息不是直接写入到日记文件大概控制台,而是先发送到一个队列里,
然后由一个专门的线程去处置惩罚这些日记信息的写入工作。
这样做的好处是可以减少日记记录对主程序运行的影响,提高程序的服从。
7.4.1 不加异步日记
- private static final Logger logger = LoggerFactory.getLogger(LogbackTest.class);
- @Test
- public void testLogbackQuick(){
- //日志输出
- logger.error("这是一个错误日志");
- logger.warn("这是一个警告日志");
- logger.info("这是一个信息日志");
- logger.debug("这是一个调试日志");
- logger.trace("这是一个跟踪日志");
- //这里模拟业务逻辑
- System.out.println("晓凡今年18岁了");
- System.out.println("晓凡的个人博客是:www.xiezhrspace.cn");
- System.out.println("晓凡的个人公众号是:程序员晓凡");
- System.out.println("晓凡的个人微信是:xie_zhr");
- System.out.println("欢迎关注晓凡,持续输出干货!!!!!");
- }
复制代码 输出效果:
从上面控制台输出看,只有当日记输出完成之后我们的业务逻辑代码才被执行。如果日记耗时比力长,非常影响服从
7.4.2 添加异步日记
我们只需在原来的配置文件中添加如下关键配置- <appender name="async" >
- <appender-ref ref="console" />
- </appender>
- <root level="ALL">
-
- <appender-ref ref="console" />
-
- <appender-ref ref="async" />
- </root>
复制代码 日记输出效果:
从上面日记日记输出看,不再是日记输出完再进行业务逻辑代码执行,而是异步执行了
八、Log4j2日记框架
官网:https://logging.apache.org/log4j/2.x/
Log4j2是Log4j的升级版,参考了Logback的一些优秀设计,修复了一些bug,性能和功能都带来了极大提拔
主要体现在以下几个方面
- 性能提拔: Log4j2 在多线程情况下表现出更高的吞吐量,比 Log4j 1.x 和 Logback 高出10倍
- 异步日记:Log4j2 支持异步日记记录,可以通过 AsyncAppender 或 AsyncLogger 实现。异步日记可以减少日记记录对主程序性能的影响,尤其是在高并发场景下
- 自动重载配置:Log4j2 支持动态修改日记级别而不需要重启应用,这是借鉴了 Logback 的设计
- 无垃圾机制:Log4j2 大部分情况下使用无垃圾机制,避免因频繁的日记收集导致的 JVM GC2。
- 异常处置惩罚:Log4j2 提供了异常处置惩罚机制,Appender 中的异常可以被应用感知到,而 Logback 中的异常不会被应用感知
Log4j2有这么多优势,以是在将来SLF4j+Log4j2组合
8.1 快速入门
Log4j2不光仅是日记实现,同时也是日记门面。在快速入门中,我们就使用Log4j2作为日记门面和日记实现来快速入门
8.1.1 添加依靠
- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-api</artifactId>
- <version>2.23.1</version>
- </dependency>
- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-core</artifactId>
- <version>2.23.1</version>
- </dependency>
复制代码 8.1.2 添加日记实当代码
- public class Log4j2Test {
- private static final Logger logger = LogManager.getLogger(Log4j2Test.class);
- @Test
- public void Log4j2Test(){
- logger.fatal("这是一条致命信息");
- logger.error("这是一条错误信息");
- logger.warn("这是一条警告信息");
- logger.info("这是一条一般信息");
- logger.debug("这是一条调试信息");
- logger.trace("这是一条追踪信息");
- }
- }
复制代码 日记输出效果如下
8.2 使用slf4j+log4j2组合
前面我们提到SLF4j+Log4j2组合会是将来日记发展的大趋势,以是接下来我们就使用这个组合来输出日记
导入依靠- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-api</artifactId>
- <version>2.23.1</version>
- </dependency>
- <dependency>
- <groupId>org.apache.logging.log4j</groupId>
- <artifactId>log4j-core</artifactId>
- <version>2.23.1</version>
- </dependency> org.slf4j slf4j-api 2.0.13 org.apache.logging.log4j log4j-slf4j-impl 2.23.1
复制代码 日记输出代码- public class Log4j2Test {
- //这里我们换成了slf4j的门面接口
- private static final Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
- @Test
- public void Log4j2Test(){
- logger.error("这是一条错误信息");
- logger.warn("这是一条警告信息");
- logger.info("这是一条一般信息");
- logger.debug("这是一条调试信息");
- logger.trace("这是一条追踪信息");
- }
- }
复制代码 日记输出效果
8.3 Log4j2配置
log4j2 默认加载classpath 下的 log4j2.xml 文件中的配置。
下面通过log4j2.xml 配置文件进行测试,配置大同小异,这里就不一一说明白,给出完整的配置- D:/logs<?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration>
复制代码 日记输出如下

下面的截图为2024-08-11的日记按日记文件大小1MB拆分成10个并进行压缩,拆分满10个文件后新日记会覆盖旧日记,其他天的类似

8.4 Log4j2 异步日记
Log4j2最大的特点就是异步日记,就因为异步日记的存在,将性能提拔了好多。
下图是官网给的性能对比图,从图中我们可以看出在全局异步模式(Loggers all async) 和混淆异步模式(Loggers mixed sync/async)
性能简直将Logback和Log4j日记框架甩了一条街。
至于什么时全局异步模式和混淆异步模式?我们会在背面具体说明
8.4.1 陌生名词表明
- 同步日记:想象一下你手里有一堆信件要写,每写一封信你都得亲自动手,写完后才气去做别的事变。在这个过程中,你得一封一封地写,不能同时干其他事,这就类似于同步日记。在程序中,同步日记意味着每次记录日记时,程序都得停下来,等待日记写完了才气继续执行其他任务。这样做的好处是不会丢信(日记),但弊端是写信(记录日记)这个过程如果太慢,就会延长你做其他事变(程序运行)
- 异步日记:如果你特殊忙,你大概会找个助手来帮你写信。你只需要告诉他要写什么,然后就可以继续忙自己的事变,而助手会帮你把信写好并寄出去。这个过程就像是异步日记。在程序中,异步日记意味着程序可以把要记录的日记信息交给一个专门的“助手”(通常是别的的线程或历程),然后程序就可以继续执行其他任务,而不需要等待日记写完。这样做的好处是可以更快地处置惩罚任务,不会延长正事儿,但偶尔大概会有一两封信(日记)因为意外情况没有寄出去。
- 全局异步: 所有日记记录都采用异步的方式记录
- 混淆异步:以在应用中同时使用同步日记和异步日记,这使得日记配置更加机动
8.4.2 同步日记与异步日记
2、异步日记流程
8.5 异步日记配置
异步日记的实现一共有两种方式
- AsyncAppender [生产上险些不使用,因为性能低下]
- AsyncLogger [生产上用得多,因为性能高]
第一种方式因为用的不多性能也不够好,以是这里就不说了,我们以第二种配置来具体说一说
不管采用哪种方式,起首都要引入异步依靠- <dependency>
- <groupId>com.lmax</groupId>
- <artifactId>disruptor</artifactId>
- <version>3.4.4</version>
- </dependency>
复制代码 ① 全局异步
只需在resources下添加log4j2.component.properties,具体内容如下- Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
复制代码
日记输出效果

② 混淆异步配置
起首,我们需要关闭全局异步配置,将上面添加的log4j2.component.properties 内容解释即可
log4j2.xml配置- D:/logs<?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><?xml version="1.0" encoding="UTF-8"?>
- <Configuration>
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%p] - %l - %m%n"/>
- </Console>
- </Appenders>
- <Loggers>
- <Root level="INFO">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
- </Configuration><appender name="async" >
- <appender-ref ref="console" />
- </appender>
- <root level="ALL">
-
- <appender-ref ref="console" />
-
- <appender-ref ref="async" />
- </root>
复制代码 输出效果:

注意事项:
- 上面配置AsyncAppender 、全局配置、混淆配置 不能同时出现,否则将影响日记性能
- includeLocation="false" 关闭日记记录的行号信息 配置肯定要加上,否则会降低日记性能
九、阿里巴巴日记规约
通过上面八小节我们对Java日记框架应该非常熟悉了,并且也知道怎么使用了。但在日记开发中,使用日记照旧有写规约需要我们去遵守。
下面式阿里巴巴Java开发手册中的日记规约
❶【强制】应用中不可直接使用日记系统(Log4j、Logback)中的 API,而应依靠使用日记框架(SLF4J、JCL--Jakarta Commons Logging)中的 API,使用门面模式的日记框架,有利于维护和各个类的日记处置惩罚方式统一。
说明:日记框架(SLF4J、JCL--Jakarta Commons Logging)的使用方式(推荐使用 SLF4J)
1)使用SLF4J- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- private static final Logger logger = LoggerFactory.getLogger(Test.class);
复制代码- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- private static final Log log = LogFactory.getLog(Test.class);
复制代码 ❷【强制】所有日记文件至少生存 15 天,因为有些异常具备以“周”为频次发生的特点。对于当天日记,以“应用名.log”来生存,
生存在”/home/admin/应用名/logs/“目录下,过往日记格式为: {logname}.log.{生存日期},日期格式:yyyy-MM-dd
正例:以 aap 应用为例,日记生存在/home/admin/aapserver/logs/aap.log,历史日记名称为aap.log.2021-03-23
❸【强制】根据国家法律,网络运行状态、网络安全变乱、个人敏感信息操作等相关记录,留存的日记不少于六个月,并且进行网络多机备份。
❹【强制】应用中的扩展日记(如打点、暂时监控、访问日记等)定名方式:appName_logType_logName.log。
- logType:日记范例,如 stats/monitor/access等;
- logName:日记描述。
这种定名的好处:通过文件名就可知道日记文件属于什么应用,什么范例,什么目标,也有利于归类查找。
说明:推荐对日记进行分类,如将错误日记和业务日记分开存放,便于开发人员检察,也便于通过日记对系统进行实时监控。
正例:mppserver 应用中单独监控时区转换异常,如:
mppserver_monitor_timeZoneConvert.log
❺ 【强制】在日记输出时,字符串变量之间的拼接使用占位符的方式。
说明:因为 String 字符串的拼接会使用 StringBuilder 的append()方式,有肯定的性能消耗。使用占位符仅是更换动作,可以有效提拔性能。
正例:- logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
复制代码 ❻【强制】对于 trace/debug/info 级别的日记输出,必须进行日记级别的开关判定。
说明:虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时(Slf4j 的常见实现Log4j 和 Logback),就直接 return,但是参数大概会进行字符串拼接运算。此外,如果 debug(getName())这种参数内有 getName() 方法调用,无谓浪费方法调用的开销。
正例:- // 如果判断为真,那么可以输出 trace 和 debug 级别的日志
- if (logger.isDebugEnabled()) {
- logger.debug("Current ID is: {} and name is: {}", id, getName());
- }
复制代码 ❼【强制】避免重复打印日记,浪费磁盘空间,务必在日记配置文件中设置 additivity=false。
正例:- [/code]❽ 【强制】生产情况禁止直接使用 System.out 或 System.err 输出日记或使用e.printStackTrace() 打印异常堆栈 。
- [indent]说明:标准日记输出与标准错误输出文件每次Jboss重启时才滚动,如果大量输出送往这两个文件,轻易造成文件大小超过操作系统大小限定。
- [/indent]❾ 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处置惩罚,那么通过关键字 throws 往上抛出。
- [b]正例:[/b]
- [code]logger.error("inputParams:{} and errorMessage:{}", 各类参数或者对象 toString(), e.getMessage(), e);
复制代码 ❿ 【强制】日记打印时禁止直接用 JSON 工具将对象转换成 String。
说明:如果对象里某些 get 方法被覆写,存在抛出异常的情况,则大概会因为打印日记而影响正常业务流程的执行。
正例:
打印日记时仅打印出业务相关属性值大概调用其对象的 toString() 方法。
⓫ 【推荐】谨慎地记录日记。生产情况禁止输出 debug 日记;有选择地输出 info 日记;
如果使用 warn 来记录刚上线时的业务举动信息,肯定要注意日记输出量的题目,避免把服务器磁盘撑爆,并记得实时删除这些观察日记。
说明:大量地输出无效日记,不利于系统性能提拔,也不利于快速定位错误点。记录日记时请思考:这些日记真的有人看吗?看到这条日记你能做什么?能不能给题目排查带来好处?
⓬ 【推荐】可以使用 warn 日记级别来记任命户输入参数错误的情况,避免用户投诉时,无所适从。
说明:如非必要,请不要在此场景打出 error 级别,避免频繁报警。 注意日记输出的级别,error 级别只记录系统逻辑出错、异常大概紧张的错误信息。
⓭ 【推荐】尽量用英文来描述日记错误信息,如果日记中的错误信息用英文描述不清楚的话使用中文描述即可,否则轻易产生歧义。
说明:国际化团队或海外摆设的服务器由于字符集题目,使用全英文来解释和描述日记错误信息。
本期内容到这儿就结束了 ★,°:.☆( ̄▽ ̄)/$:.°★ 。 希望对您有所资助
我们下期再见 ヾ(•ω•`)o (●'◡'●)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |