2021/12/14
-
距离过年还有 44天想家,这两天有一件事闹的 沸沸扬扬的~
-
某个日志框架,出现了严重的bug ,听说好多,大厂大佬加班加点的改bug~
然后,本人想好像本人对日志框架还不怎么了了解,只知道导入依赖,配置文件,正常的使用…更深入的就不太清除了...
赶紧,学习总结了一波笔记~
日志文件是用于记录系统操作事件的文件集合
-
在计算机领域,日志文件logfile
是一个记录了发生在运行中的操作系统或其他软件中的事件的文件,或者记录了在网络聊天软件的用户之间发送的消息。
为什么需要日志:
-
对于一个应用程序来说日志记录是必不可少的一部分
可以帮助,开发者快速的定位问题,找到问题,进行解决…
出现日期排序:
Java 日志框架发展了很多年,已经出现了很多个版本~发展过程也比较混乱~
- log4j → JUL → JUCCommons Logging → Slf4j → Logback → log4j2
log4j 1999
-
它是 Apache 软件基金会的一个项目,在 jdk1.3 之前,还没有现成的日志框架:
Java 工程师只能使用原始的 System.out.println() System.err.println() 或者 e.printStackTrace()
把 debug 日志写到 StdOut 流,错误日志写到 ErrOut 流,以此记录应用程序的运行状态
-
这种原始的日志记录方式缺陷明显,不仅无法实现定制化,而且日志的输出粒度不够细.
-
1999 年,大牛 Ceki Gülcü 切基·居尔库 创建了 Log4j 项目
JUL 2002
-
Log4j 作为 Apache 基金会的一员,Apache 希望将 Log4j 引入 jdk 不过被 sun 公司拒绝了
-
随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL
sun公司真傲娇,终于知道怎么破产的了~
JCL 2002
-
因为,当时时面上出现了两种日志框架 不同的使用方式,不同日志级别~ 对开发者不是很友好~
-
于是,2002年 阿帕奇 推出了 JCL日志门面 定义一套接口,具体实现由Log4j 或 JUL 来实现
程序运行时会使用 ClassLoader类加载器 寻找和载入底层的日志库,因此可以自由选择由 log4j 或 JUL 来实现日志功能
-
类似于JDBC
定义了一组数据库的 增删改查接口, Mysql Oracle...数据库都实现类这些接口可以做到,无需更换代码切换数据库~
Slf4j 2005 & Logback 2006
-
两个原因,JCL的实现方式太过麻烦,后期不方便扩展新的日志框架
Ceki Gülcü 大佬,后面离开了阿帕奇,独自开发了一个新的 日志门面 Slf4j Logback是其的实现~
-
它相较于 log4j 有更快的执行速度和更完善的功能大佬牛逼呀!
log4j2 2014
-
为了维护在 Java 日志江湖的地位
防止 JCL、Log4j 被 Slf4j、Logback 组合取代 ,2014 年 Apache 推出了 Log4j2 来自老东家的'制裁'
-
Log4j 2 与 log4j 不兼容不存在管来,经过大量深度优化,其性能显著提升
经过上面,我们已经知道常用的日志框架有:Log4j JUL JCL Slf4j Logback Log4j2
这些日志框架可以分为两种类型:门面日志和日志系统
日志门面:设计模式 外观模式
JCL、slf4j
-
只提供日志相关的接口定义,即相应的 API 提供简单实现
-
为了,方便不同的日志, 实现, 不会对代码进行大改动~提高开发者的使用~
日志系统:
JUL、logback、log4j、log4j2
- 与日志门面相对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的功能
JUL全称Java.util.Logging
- 是java原生的日志框架,使用时不需要另外引用第三方类库
- 相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用
Logger
- 记录器,应用程序通过 getLogger(); 获取 Logger 对象,调用其 API 来发布日志信息
- Logger 通常被认为是访问日志系统的入口程序
Handler
-
处理器,每个 Logger 都会关联一个或者是一组 Handler,Console File
-
Logger 会将日志交给关联的 Handler 去做处理,由 Handler 负责将日志做记录.
-
Filter
过滤器,根据需要定制哪些信息会被记录,哪些信息会被略过
-
Formatter
格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们输出日志最终的形式
-
Level
日志的输出级别,每条日志消息都有一个关联的级别。根据输出级别的设置,用来展现最终所呈现的日志信息
日志记录器logger 有自己默认的,Filter Formatter Level,可以与一个 或 多个Hanlder关联进行日志输出~
入门Demo:创建一个JUL Maven工程
JUL 是Java 本身提供的,所以,不需要引入任何依赖…
JULTest.Java
import org.junit.Test;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JULTest {
//入门案例:
@Test
public void Test(){
//1.获取日志记录器对象 Logger.getLogger("参数是一个唯一的字符串,一般来说使用类的'全路径名'");
Logger logger = Logger.getLogger("com.wsm.JULTest");
//2.日志记录输出
logger.info("输出日志,info 级别~");
//通过 .log(); 方法,指定日志的输出级别
logger.log(Level.INFO,"指定输出级别: info~");
logger.log(Level.WARNING,"指定输出级别: WARNING~");
//通过占位符形式,输出日志, 类似于 printf();
String name = "WSM";
Integer age = 3;
logger.log(Level.INFO,"我叫: “{0},今年{1}岁",new Object[]{name,age});
}
}
控制台输出:
十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test 信息: 输出日志,info 级别~ 十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test 信息: 指定输出级别: info~ 十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test 警告: 指定输出级别: WARNING~ 十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test 信息: 我叫: “WSM,今年3岁JUL 日志级别说明及展示
JUL 一个有七个日志级别,两个特殊的 off All
| 日志级别 | 数值 | 说明 |
|---|---|---|
| OFF | Integer.MAX_VALUE 最大整数 | 关闭所有消息的日志记录 |
| SEVERE中: 色歪啊~ | 1000 | 错误信息最高级的日志级别 |
| WARNING | 900 | 警告信息 |
| INFO | 800 | 默认信息默认级别 |
| CONFIG | 700 | 配置信息 |
| FINE | 500 | 详细信息(少) |
| FINER | 400 | 详细信息(中) |
| FINEST | 300 | 详细信息(多) 最低级的日志级别 |
| ALL | Integer.MIN_VALUE最小整数 | 启用所有消息的日志记录 |
数值的意义在于: 设置指定了日志级别,最终展示的信息,必须大于> 指定级别的数值!
- OFF 和 ALL 是特殊的日志级别 全部关闭 全部启动
//检查JUL默认输出级别
@Test
public void Test2(){
// 1.获取日志记录器对象
Logger logger = Logger.getLogger("com.itheima.JULTest"); //参数,保证唯一即可,一般填写 类路径名;
// 2.日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info"); //默认日志输出级别
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
//自定义日志级别1.0
@Test
public void Test3(){
// 1.获取日志记录器对象
Logger logger = Logger.getLogger("com.itheima.JULTest"); //参数,保证唯一即可,一般填写 类路径名;
// 2.设置日志输出级别: 改变Logger 日志对象
// 首先关闭系统默认配置
logger.setUseParentHandlers(false);
// 重新,配置日志具体级别 (你以为这样就行了吗? 执行发现啥也没有~ 因为,现在日志配置还不完整,缺少了 Handler 还有 Formatter )
logger.setLevel(Level.ALL);
// 3.日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
//自定义日志级别2.0
@Test
public void Test4(){
// 1.获取日志记录器对象
Logger logger = Logger.getLogger("com.itheima.JULTest");
// 2.设置日志输出级别: 改变Logger 日志对象
// 首先关闭系统默认配置
logger.setUseParentHandlers(false);
// 重新,配置日志具体级别
logger.setLevel(Level.ALL);
// 创建ConsolHhandler 控制台输出
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 将Handler 和 Formatter进行关联: logger ——关联—— Handler ——关联—— Formatter
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
// 设consoleHandler 控制台输出的级别~
consoleHandler.setLevel(Level.ALL);
// 3.日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
Test2 十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel 严重: severe 十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel 警告: warning 十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel 信息: info Test3 CMD输出空!因为,日志的配置不完整,缺少了 Handler Formatter Test4 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 严重: severe 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 警告: warning 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 信息: info 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 配置: config 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 详细: fine 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 较详细: finer 十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4 非常详细: finest
Test2
- 发现输出的日志之后三行… 其它的都没有输出出来… 因为,JUL的默认级别就是 info 小于info的级别数值都输出不出来
Test3
- 没有一行数据,因为:日志的配置不完整,缺少了 Handler Formatter
Test4
-
终于,完整的输出了所有 级别的日志
-
创建了一个控制台的 ConsoleHandler 简单的输出格式 SimpleFormatter 并进行关联~ 最重要的是,设置了Console的输出级别,这个是你控制台输出的日志级别!
-
注意:
consoleHandler.setLevel(Level.ALL); 设置的级别 ,取绝于 logger.setLevel(Level.ALL);
logger 是真正设置输入的日志级别,但它因为配置不完整需要,consoleHandler输出. consoleHandler的级别在大 logger级别放的小,输出的数据也不多~
两者关系就像是两个水桶 logger小桶 放在consoleHandler大桶
大桶的出水口在大,小桶一次出一滴水,大桶也只有一滴水流速
小桶出水口很大,每次流很多水,大桶的口小,每次流出的水也不大!
//日志文件的输出:
@Test
public void Test5() throws IOException {
// 1.获取日志记录器对象
Logger logger = Logger.getLogger("com.itheima.JULTest");
// 2.设置日志输出级别: 改变Logger 日志对象
// 首先关闭系统默认配置
logger.setUseParentHandlers(false);
// 重新,配置日志具体级别
logger.setLevel(Level.ALL); //设置小桶流速!
// 创建格式
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 创建ConsolHhandler 控制台输出: 关联 输出级别
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
consoleHandler.setLevel(Level.ALL);
// 创建文件输出 Handler
FileHandler fileHandler = new FileHandler("C:\Users\王斯明\Desktop\jul.log");
fileHandler.setFormatter(simpleFormatter);
logger.addHandler(fileHandler);
// 不设置,日志级别,输出日志~, 发现默认输出了全部, (继承了小桶logger的流量~)
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
-
注意⚠:
Logger 可以持有多个处理器 Handler
FileHandler 来设置,日志的输出文件,地址目录要事先存在!
如果不指定,输出日志级别,则默认,根据logger设置的级别Logger对象的父子关系~
// Logger对象父子关系
@Test
public void Test6(){
Logger logger1 = Logger.getLogger("com.wsm");
Logger logger2 = Logger.getLogger("com");
// 测试
System.out.println("logger1上级 与 logger2比较:"+(logger1.getParent() == logger2));
// 所有日志记录器的顶级父元素 LogManager$RootLogger,name ""
System.out.println("logger2 Parent:"+logger2.getParent() + ",name:" + logger2.getParent().getName());
System.out.println("下级会默认继承上级的配置,改变logger2 logger1也会发生改变~");
// 关闭默认配置
logger2.setUseParentHandlers(false);
// 设置logger2日志级别
// 自定义配置日志级别
// 创建ConsolHhandler 控制台输出
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
consoleHandler.setFormatter(simpleFormatter);
logger2.addHandler(consoleHandler);
// 配置日志具体级别
logger2.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
System.out.println();
System.out.println("输出logger1 发现,输出级别变成了 info");
logger1.severe("severe");
logger1.warning("warning");
logger1.info("info");
logger1.config("config");
logger1.fine("fine");
logger1.finer("finer");
logger1.finest("finest");
}
logger1上级 与 logger2比较:true logger2 Parent:java.util.logging.LogManager$RootLogger@61e4705b,name: 下级会默认继承上级的配置,改变logger2 logger1也会发生改变~ 输出logger1 发现,输出级别变成了 info 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 严重: severe 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 警告: warning 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 信息: info 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 配置: config 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 详细: fine 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 较详细: finer 十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6 非常详细: finest
-
JUL 的logger 对象具有父子关系
下降会默认具有,上级的属性配置…`默认的上级是 LogManager$RootLogger@61e4705b,name
上面的,硬编码形式,可以完成简单的 日志记录输出,但是实际开发中,并不方便 开发中使用
- 上面我们知道,logger对象对象具有 父子关系 默认的上级是, LogManager$RootLogger@61e4705b,name
- 那这个 LogManager$RootLogger@61e4705b,name 又是哪里来的,又为什么是默认 info 配置類?
-
Logger.getLogger(""); 方法,获取一个日志记录器对象,ctrl+右击 进入方法~
-
发现只有一个 demandLogger(); 进入!,
发现LogManager manager = LogManager.getLogManager(); 获取一个LogManager日志管理器对象!LogManager是一个单例对象,负责记录和管理日志对象
-
**进入 getLogManager(); 进入 ensureLogManagerInitialized(); 发现:owner.readPrimordialConfiguration();加载配置文件! **
加载配置文件之后,就会执行 owner.rootLogger = owner.new RootLogger(); 设置顶级的,rootLogger 对象
-
进入readPrimordialConfiguration(); 方法查看,加载配置文件流程:发现readConfiguration(); 读取配置文件,继续进入
首先:String cname = System.getProperty("java.util.logging.config.class"); 当前系统是否存在一个配置类!没有则往下!
判断 String fname = System.getProperty("java.util.logging.config.file"); 当然系统下 有没有 自定义的配置文件!
如果没有则:fname = System.getProperty("java.home");获取Java安装目录,
File f = new File(fname, "lib"); f = new File(f, "logging.properties"); 读取,JDK目录中的lib目录下的 logging.properties
logging.properties
- 本人移除了,官方的英文注释换成了,正常注释!
# 设置一个控制台输出的 Handler handlers= java.util.logging.ConsoleHandler # 设置默认的日志级别 Info .level= INFO # 配置默认的 文件处理器 # 指定日志文件默认的输出路径, (%h输出当前设备的用户目录) (%u输出日志的文件后缀1 2 3...) java.util.logging.FileHandler.pattern = %h/java%u.log # 当前文件最大存储多少条记录... java.util.logging.FileHandler.limit = 50000 # 当前文件的数量,输出日志文件的个数~ java.util.logging.FileHandler.count = 1 # 当前 处理器输出文件的格式, 默认 xml格式输出! java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter # 控制台处理器的 默认级别INfo java.util.logging.ConsoleHandler.level = INFO # 日志输出的格式 java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # Example to customize the SimpleFormatter output format com.xyz.foo.level = SEVERE
因此,我们只需要,负责这个配置文件保证名字不变,到项目的 resources资源目录下即可通过配置文件形式,配置日志信息!
log配置文件形式进行操作:resources资源目录下,添加 logging.properties
# RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler,FileHandler (设置RootLogger 最高价关联的处理器)
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# RootLogger 顶级父元素默认的日志级别为:ALL
.level= ALL
# 关闭默认配置
com.itheima.useParentHanlders = false
# 向日志文件输出的 handler 对象
# 指定日志文件路径 /logs/java0.log 要确保安装目录存在!
java.util.logging.FileHandler.pattern = /java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1
# 指定 handler 对象日志消息格式对象
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加方式添加日志内容,(如果不添加,每一次添加的数据,都会替换之前的数据)
java.util.logging.FileHandler.append = true
# 向控制台输出的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 对象的日志消息格式对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
JULConfigTest.Java
public class JULConfigTest {
@Test
public void Test() throws Exception{
// 读取配置文件,通过类加载器
InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
// 创建LogManager
LogManager logManager = LogManager.getLogManager();
// 通过LogManager加载配置文件
logManager.readConfiguration(ins);
// 创建日志记录器
Logger logger = Logger.getLogger("com.wsm");
logger.info("--------------------------------------------------------------------------------------------");
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
System.out.println("");
// // 创建日志记录器2
// Logger logger2 = Logger.getLogger("www");
// logger2.severe("severe");
// logger2.warning("warning");
// logger2.info("info");
// logger2.config("config");
// logger2.fine("fine");
// logger2.finer("finer");
// logger2.finest("finest");
}
}
自定义配置:日志对象级别:
除了通过 logging.properties配置文件,设置大众的配置,也可以单独对于某一种的 日志对象进行设置!
## 自定义Logger使用,单独设置自定义的, 日志元素 www.handler = java.util.logging.ConsoleHandler www.level = INFO
解除上面 Logger.getLogger("www"); 注释!
ok, JUL就了解这么多了,现在用的也很少了…几乎没有公司在使用了...
Log4jLog4j是Apache下的一款开源的日志框架:官方网站
- 通过在Log4J,我们可以控制日志信息输出到:控制台、文件、甚至是数据库中
- 我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以 更灵活的控制日志的输出过程
Log4J 主要由:Loggers日志记录器 Appenders输出端 Layout日志格式化器
Loggers日志记录器控制日志的输出级别与日志是否输出
-
Logger.getLogger(类的全限定名/类对象~);
-
Logger的名字大小写敏感,其命名有继承机制
com.wsm 会继承,com限定名的日志属性~
-
Log4J中有一个特殊的logger叫做“root”
他是所有logger的根,也就意味着其他所有的logger都会直接 或者间接地继承自rootroot logger可以用Logger.getRootLogger()方法获取
指定日志的输出地方输出到控制台、文件
| 输出端类型 | 作用 |
|---|---|
| ConsoleAppender | 将日志输出到控制台 |
| FileAppender | 将日志输出到文件中 |
| DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
| RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件 |
| JDBCAppender | 把日志信息保存到数据库中 |
控制日志信息的输出格式
| 格式化器类型 | 作用 |
|---|---|
| HTMLLayout | 格式化日志输出为HTML表格形式 |
| SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message 输入什么打印什么. |
| PatternLayout | 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式, 就是用默认的转换格式 |
Layout的格式:
log4j 采用类似 C 语言的 printf 函数的打印格式格式化日志信息,具体的占位符及其含义如下:
%m 输出代码中指定的日志信息
%p 输出优先级,及 DEBUG、INFO 等
%n 换行符(Windows平台的换行符为 "n",Unix 平台为 "n")
%r 输出自应用启动到输出该 log 信息耗费的毫秒数
%c 输出打印语句所属的类的全名
%t 输出产生该日志的线程全名
%d 输出服务器当前时间,默认为 ISO8601,也可以指定格式, 如:%d{yyyy年MM月dd日 HH:mm:ss}
%F 输出日志消息产生时所在的文件名称
%L 输出代码中的行号
%% 输出一个 "%" 字符
%l 输出日志时间发生的位置,包括类名%c、线程%t、及在代码中的行数%L 如:Test.main(Test.java:10)
可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式. 如:
%5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐
%-5c 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
%.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格
%20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉
Log4j 入门Demo
建立maven工程
添加依赖 因为,不是Java本身提供的需要引入对应的依赖~
log4j log4j 1.2.17 junit junit 4.12 mysql mysql-connector-java 5.1.47
Log4jTest.Java
//阿帕奇 log4j 的包哦~
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.Test;
public class Log4jTest {
@Test
public void test(){
//1. 使用前需要初始化配置信息,不然会报错! (实际开发是通过,配置文件; 入门案例需要引入默认的)
BasicConfigurator.configure();
//2. 创建日志对象
Logger logger = Logger.getLogger(Log4jTest.class);//不仅支持 唯一字符 还有 类对象
//3. 输出日志信息!
System.out.println("log4j 和 JUL 的日志级别,有些不同..");
logger.fatal("fatal 严重错误,一般会造成系统崩溃并终止运行");
logger.error("error 错误信息,不会影响系统运行");
logger.warn("warn 警告信息,可能会发生问题");
logger.info("info 追踪信息,记录程序所有的流程信息");
logger.debug("debug 调试信息,一般在开发中使用,记录程序变量参数传递信息等等");
logger.trace("trace 追踪信息,记录程序所有的流程信息");
}
}
Log4j 配置文件使用:
源码分析:
-
Logger.getLogger() 进入,发现Logger 对象是又,一个LogManager对象.getLogger();创建
进入LogManage类 发现 static{ 代码块中 } url = Loader.getResource("log4j.properties");
类扫描读取log4j.properties 配置文件,只要 resource 资源目录下存在 log4j.properties文件,就不需要初始化配置信息了!
static{ 代码块中 } 内部有一个OptionConverter.selectAndConfigure(..) 读取配置文件…根据文件类型,动态读取 xml properties 获取Configurator对象
-
Configurator对象用于加载,初始化 root 对象,可以通过它来,查看配置文件如何编写
selectAndConfigure方法中, new PropertyConfigurator(); 进入
发现:APPENDER_PREFIX RENDERER_PREFIX等很多的配置文件,属性…
log4j.properties resources 资源文件下,添加配置文件;⚙
# 设置默认的日志对象 rootLogger # 日志级别 和 绑定的appender(可以多个) 逗号,分隔 log4j.rootLogger = trace,console # 指定控制台日志输出的 appender log4j.appender.console = org.apache.log4j.ConsoleAppender # 指定控制台日志输出的 layout 输出格式默认 SimpleLayout log4j.appender.console.layout = org.apache.log4j.SimpleLayout
Log4jTest.Java
@Test
public void test1(){
//resources 目录下添加一个, log4j.properties
//这样就不需要 BasicConfigurator.configure(); 加载默认配置文件了;
// 开启 log4j 内置日志记录 (随便,开启日志输出,日志框架的日志信息~
LogLog.setInternalDebugging(true);
//创建日志对象
Logger logger = Logger.getLogger(Log4jTest.class);
//输出日志信息!
System.out.println("log4j 和 JUL 的日志级别,有些不同..");
logger.fatal("fatal 严重错误,一般会造成系统崩溃并终止运行");
logger.error("error 错误信息,不会影响系统运行");
logger.warn("warn 警告信息,可能会发生问题");
logger.info("info 追踪信息,记录程序所有的流程信息");
logger.debug("debug 调试信息,一般在开发中使用,记录程序变量参数传递信息等等");
logger.trace("trace 追踪信息,记录程序所有的流程信息");
}
log4j 和 JUL 的日志级别,有些不同.. FATAL - fatal 严重错误,一般会造成系统崩溃并终止运行 ERROR - error 错误信息,不会影响系统运行 WARN - warn 警告信息,可能会发生问题 INFO - info 追踪信息,记录程序所有的流程信息 DEBUG - debug 调试信息,一般在开发中使用,记录程序变量参数传递信息等等 TRACE - trace 追踪信息,记录程序所有的流程信息尝试,修改配置文件,运行 test1()
# 指定控制台日志输出的 layout 输出格式默认 SimpleLayout (HTMLLayout(以HTML展示日志信息) / xml.XMLLayout(以xml形式展示数据) / PatternLayout(自定义常用!) log4j.appender.console.layout = org.apache.log4j.HTMLLayout
输出的日志就会以,html 形式进行展示
PatternLayout 自定义,日志格式输出:log4j.properties 追加
# 设置默认的日志对象 rootLogger
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定控制台日志输出的 layout 输出格式默认 SimpleLayout (HTMLLayout / xml.XMLLayout / PatternLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容 layout.conversionPattern
log4j.appender.console.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 设置,数据格式:
# [%-10p]: [数据长度10且居左] %r输出自应用启动到输出该log信息耗费的毫秒数
# %r: 输出自应用启动到输出该 log 信息耗费的毫秒数
# %l: 输出日志时间发生的位置,包括类名、线程、及在代码中的行数
# %d: 输出服务器当前时间,默认为 ISO8601,也可以指定格式
后期这样的格式,公司都是有固定的规范的已经配置好了 了解即可!
输出日志,保存文件:filelog4j.properties 追加
设置 log4j.rootLogger = trace,console,file 绑定对应的 appender
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console,file
# 日志文件输出的 appender 对象
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.file.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径 (需要手动指定,日志文件保存到磁盘...不然出错,本人这回默认项目的盘符 D盘,没有文件创建文件~
log4j.appender.file.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8
运行test1()
log4j 不像JUL ,数据默就追加了~
按照文件大小拆分 rollingFilelog4j.properties 追加
设置 log4j.rootLogger = trace,console,rollingFile 绑定对应的 appender
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console,rollingFile
# 按照文件大小拆分的 appender 对象
# 日志文件输出的 appender 对象
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.rollingFile.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.rollingFile.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件内容的大小( 没个文件1mb 产生新的日志文件~
log4j.appender.rollingFile.maxFileSize = 1MB
# 指定日志文件的数量(最多产生 10个日志文件, log4j.log log4j2.log log4j3.log, 超出文件个数,旧的文件会新文件被覆盖
log4j.appender.rollingFile.maxBackupIndex = 10
Log4jTest.Java 循环1w次查看结果~
for (int i = 0; i < 10000; i++) {
logger.fatal("fatal 严重错误,一般会造成系统崩溃并终止运行");
logger.error("error 错误信息,不会影响系统运行");
logger.warn("warn 警告信息,可能会发生问题");
logger.info("info 追踪信息,记录程序所有的流程信息");
logger.debug("debug 调试信息,一般在开发中使用,记录程序变量参数传递信息等等");
logger.trace("trace 追踪信息,记录程序所有的流程信息");
}
按照时间规则拆分的 dailyFile
log4j.properties 追加
设置 log4j.rootLogger = trace,console,dailyFile 绑定对应的 appender
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console,dailyFile
# 按照时间规则拆分的 appender 对象;(默认每一天,生成一个新的文件~
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.dailyFile.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.dailyFile.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encoding = UTF-8
# 指定日期拆分规则 (年月日 时分秒,每秒生成一个文件~
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
运行:test1()
日志文件 写入数据库:创建对应的数据库, 数据库表脚本 .sql
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`) );
log4j.properties 追加
设置 log4j.rootLogger = trace,console,dailyFile 绑定对应的 appender
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console,logDB
#mysql
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
# 设置数据库: 连接信息还有操作的数据库,(本人的数据库的 log~
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/log
# 数据库 用户名
log4j.appender.logDB.User=root
# 数据库 密码
log4j.appender.logDB.Password=ok
# 设置入库的语句, %? 对应列要存的数据,参考,PatternLayout
log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
运行test1()
自定义 logger 对象:作用:对于多个生成环境,需要记录的是不同的日志…可以使用 自定义日志对象类设置~
log4j.properties
# 自定义 logger 对象设置 (输出日志级别更改为了 info, appender还会默认继承 rootlogger log4j.logger.com.wsm = info,console
Log4jTest.Java
@Testpublic void test2(){ //获取自定义日志对象~ Logger logger = Logger.getLogger("com.wsm"); //输出~ logger.fatal("fatal 严重错误,一般会造成系统崩溃并终止运行"); logger.error("error 错误信息,不会影响系统运行"); logger.warn("warn 警告信息,可能会发生问题"); logger.info("info 追踪信息,记录程序所有的流程信息"); logger.debug("debug 调试信息,一般在开发中使用,记录程序变量参数传递信息等等"); logger.trace("trace 追踪信息,记录程序所有的流程信息");}
只展示大于等于 info 的日志级别~
[FATAL ]0 com.wsm.Log4jTest.test2(Log4jTest.java:57) 2021-12-19 22:35:11.558 fatal 严重错误,一般会造成系统崩溃并终止运行 [ERROR ]3 com.wsm.Log4jTest.test2(Log4jTest.java:58) 2021-12-19 22:35:11.561 error 错误信息,不会影响系统运行 [WARN ]3 com.wsm.Log4jTest.test2(Log4jTest.java:59) 2021-12-19 22:35:11.561 warn 警告信息,可能会发生问题 [INFO ]3 com.wsm.Log4jTest.test2(Log4jTest.java:60) 2021-12-19 22:35:11.561 info 追踪信息,记录程序所有的流程信息JCL
全称为Jakarta Commons Logging,是Apache提供的一个通用日志API,是一种日志门面
-
它为 “所有的Java日志实现"提供一个统一的接口”,它自身也提供一个日志的实现,但是功能非常常弱 SimpleLog几乎被淘汰了, 一般不会单独使用它
他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk 自带的日志(JUL)
-
JCL 有两个基本的抽象类:Log(基本记录器) 和 LogFactory(负责创建Log实例)
创建Maven工程
添加 pom.xml依赖
commons-logging commons-logging 1.2 junit junit 4.12
JCLTest.Java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class JCLTest {
@Test
public void test(){
// 获取 log日志记录器对象, JCL通过一个日志工厂 获取一个日志对象;
Log log = LogFactory.getLog(com.wsm.JCLTest.class);
// 日志记录输出
log.info("hello jcl");
}
}
输出了和 JUL一样的日志数据, 当没有引入 log4j依赖时候 JCL默认就通过JUL实现,则使用log4j
十二月 19, 2021 11:02:59 下午 com.wsm.JCLTest test 信息: hello jcl切换 log4j日志框架
解开log4j的依赖配置 添加:依赖,配置文件 log4j.properties
# 设置默认的日志对象 rootLogger
# 日志级别 和 绑定的appender(可以多个) 逗号,分隔
log4j.rootLogger = trace,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定控制台日志输出的 layout 输出格式默认 SimpleLayout (HTMLLayout / xml.XMLLayout / PatternLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容 layout.conversionPattern
log4j.appender.console.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
运行test(),是log4j的输出方式
[INFO ]1 com.wsm.JCLTest.test(JCLTest.java:13) 2021-12-19 23:17:35.636 hello jcl总结:⭐
JCL 是一个日志门面,随着日志的使用频繁市面上又有很多个不同的日志框架,开发者需要同时掌握多个日志框架(实在不是很友好~
阿帕奇,就退出了一个日志门面,来统一实现了接口,JUL 和 log4j都对其进行了实现。
好处,开发者只需要学习一个日志框架的使用,即使后面更改日志框架,也不会对代码产生任何影响~ 只需要替换一个依赖即可!
-
JCL 是由默认实现的SimpleLog,但很少有人使用~
-
JUL 是Java自带的,所以不需要引入依赖,就默认使用了JUL实现!
但,JCL 对于不同日志框架的切换实现…
是以一种,硬编码形式实现,之兼容了少量的日志框架,如果后面需要引入新的日志,还需要更改配置文件~
所以,后面又出现了一种新的日志门面 Slf4j 它可以兼容目前市面上所有/以及未来的 日志框架
Slf4j简单日志门面Simple Logging Facade For Java
-
SLF4J主要是为了给Java日志访问提供一套标准、规范 的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架
-
当然slf4j自己也提供了功能较为简单的实现但是一般很少用到
-
现在很多的Java项目都使用,slf4j-api作为门面
-
配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接
-
官方地址
SLF4J日志门面主要提供两大功能:
- 日志框架的绑定
- 日志框架的桥接
创建Maven工程:
导入对应的依赖:
org.slf4j slf4j-api 1.7.26
Slf4jTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
//声明全局的 日志对象
public final static Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void Test(){
// 日志输出(默认输出至,控制台 info级别;
LOGGER.error("error");
LOGGER.warn("wring");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
// 使用占位符输出日志信息
String name = "wsm";
Integer age = 540;
LOGGER.info("用户:{},{}",name,age);
// 将系统的异常信息输出
try {
int i = 1/0;
} catch (Exception e) {
// e.printStackTrace(); 这样程序就不会,打印Java的 堆栈一推错误了...
LOGGER.error("出现异常:",e);
}
}
}
[main] ERROR com.wsm.Slf4jTest - error [main] WARN com.wsm.Slf4jTest - wring [main] INFO com.wsm.Slf4jTest - info [main] INFO com.wsm.Slf4jTest - 用户:wsm,540 [main] ERROR com.wsm.Slf4jTest - 出现异常: java.lang.ArithmeticException: / by zero at com.wsm.Slf4jTest.Test(Slf4jTest.java:29) #省略下面更多,异常信息~日志框架的绑定
Slf4j 作为一个强大的日志门面,可以在不改变代码的情况下,实现切换不同的日志框架…支持不同的日志框架的绑定!
上图,是Slf4j 官方的图片,与各个框架进行绑定的介绍:
application 指定是应用程序 SLF4J API 就是Slf4j日志门面 从左往右——>1-6
-
①Slf4j无绑定项目只是单纯的引入了 日志门面...
-
② ⑤ ⑥ 分别是 logback slf4j内置实现 log4j2
都是在,slf4j门面之后出现的,因此都对其进行了实现,之间导入 Slf4j日志门面依赖和 对应实现依赖即可,自动绑定完成
-
③ ④ 是在 slf4j之前开发的,没有直接的实现 slf4j门面
所以在,使用之前还需要导入对应的 Adaptation适配器来进适配.
使用 slf4j门面方法,内部适配器在调用,JUL / log4j 的方法~
接下来,粗略介绍一下各个日志框架的绑定:slf4j内置实现,入门案例就是
Logback 绑定因为,已经默认实现类,slf4j 直接引入依赖即可… 别忘记注释,slf4j内置实现依赖
- 这里不详细,介绍Logback使用.
slf4j 空实现:ch.qos.logback logback-classic 1.2.3
当我们还不能确定,使用什么日志框架来实现时候,可以引入 slf4j-nop
它是 slf4j的一种空实现,因为如果不引入 slf4j的实现,会报错Defaulting to no-operation (NOP) logger implementation
- 注释其它依赖 日志实习依赖 运行程序异常~
- 添加 slf4j-nop 依赖,运行test();
org.slf4j slf4j-nop 1.7.25
- 运行,不出异常,但无结果…
因为,Log4j诞生早于,Slf4j所以并没有直接对其进行实现,slf4j 与 log4j 进行绑定还需要添加一个 适配器
注释其它实现依赖…
org.slf4j slf4j-log4j12 1.7.12 log4j log4j 1.2.17
运行 test()报错:log4j:WARN No appenders could be found for logger (com.wsm.Slf4jTest).
- 需要引入对应的 appenders 需要,添加对应的log4j.properties配置文件~
同上,JUL也需要添加一个适配器 因为JUL 是JDk 自带的,所以,只需要引入一个 适配器依赖即可!
org.slf4j slf4j-jdk14 1.7.25
JUL ,JDK内有默认的 配置文件.
总结:⭐使用slf4j的日志绑定流程:
-
添加slf4j-api的依赖
-
使用slf4j的API在项目中进行统一的日志记录
-
绑定具体的日志实现框架:
绑定已经实现了slf4j的日志框架,直接添加对应依赖
绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
-
slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现
假设,突然让你维护一个比较老的项目。老板让你把原先项目的日志框架 log4j 换成 logback
- 咋办,两个框架一个属于 slf4j门面 一个属于 JCL门面 直接替换jar包,肯定不行!
- 那怎么办❓ 移除依赖,修改代码❓ 那真的太狗血了!
还好,slf4j 提供了桥接,技术 支持开发者引入对应的日志的 桥接器 来完成配置!
log4j 切换 logback DemoSlf4j模块,添加 Log4jTest.Java
import org.apache.log4j.Logger;
import org.junit.Test;
public class Log4jTest {
// 定义 log4j 日志记录器
public static final Logger LOGGER = Logger.getLogger(Log4jTest.class);
// 测试桥接器
@Test
public void test01()throws Exception{
LOGGER.info("hello lgo4j");
}
}
别忘记了 log4j 需要引入配置文件log4j.properties, 运行 test01();
15:52:04.308 [main] INFO com.itheima.Log4jTest - hello lgo4j
上面是 log4j的代码和输出,下面是,切换的logback的代码输出..
引入依赖:注释log4j的依赖...
ch.qos.logback
logback-classic
1.2.3
org.slf4j
log4j-over-slf4j
1.7.25
十二月 20, 2021 4:48:50 下午 com.wsm.log4jTest test01 信息: hello lgo4j
发现代码不变,输出的方式已经变成了 logback
原因:log4j-over-slf4j桥接器,内部调用了 Slf4j的方法,相当于进行了一个重定向~
注意⚠:log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现
jcl-over-slf4j.jar和 slf4j-jcl.jar不能同时出现
jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现
日志桥接器,不能和 日志的实现依赖同时出现,因为,日志桥接器 底层还是去调用Slf4j的接口方法. Slf4j 回去调用日志实现
而如果同时引入了,相同的 桥接器 日志实现,日志实现 --调用–> 桥接器 --调用–> Slf4j --调用–> 日志实现 陷入死循环,堆栈溢出!
LogbackLogback是由log4j创始人设计的另一个开源日志组件:官方地址
Logback主要分为三个模块:
- logback-core:其它两个模块的基础模块, Maven具有继承特性,引入了 classic/access 就默认具有 core了
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能 用于Tomcat/Jetty 日志直接输出到对应的服务器日志中去!
创建Maven 工程
引入 logback 依赖:
org.slf4j slf4j-api 1.7.26 ch.qos.logback logback-classic 1.2.3 junit junit 4.12
LogbackTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
//创建全局的 logger 对象
public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);
@Test
public void test(){
//调用 Slf4j 接口实现日志输出;
// logback 一共有五个日志级别,的默认级别是, debug
LOGGER.error("error");
LOGGER.warn("wring");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
存在,默认的配置 rootLogger 默认:控制台输出, 日志级别debug
10:28:18.657 [main] ERROR com.wsm.LogbackTest - error 10:28:18.662 [main] WARN com.wsm.LogbackTest - wring 10:28:18.662 [main] INFO com.wsm.LogbackTest - info 10:28:18.662 [main] DEBUG com.wsm.LogbackTest - debugLogback 介绍: logback会依次读取以下类型配置文件:
- logback.groovy
- logback-test.xml
- logback.xml 如果均不存在会采用默认配置 在 resources 资源目录下,创建任意一个配置文件即可
xml 格式,方便读取,和操作, 使用也相对比较多,本篇介绍logback.xml 配置文件编写✍~
logback组件之间的关系:和其它日志框架,也大致一样,日志对象 记录器 日志输出格式
Logger 日志记录器对象
-
根据context环境(配置文件,LoggerFactory.getLogger()生成一个 日志记录器对象,主要用于存放日志对象,定义日志类型、级别
-
Logger 可以被分配级别:
**级别包括:**TRACE、DEBUG、INFO、WARN 和 ERROR 定义于ch.qos.logback.classic.Level
Appender 记录器
- 用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等
Layout 日志输出格式
- 负责把事件转换成字符串,格式化的日志信息的输出,logback中Layout对象被封 装在encoder中
resources 资源目录下添加: logback.xml
控制台日志输出的 appendermylog
logback.xml追加:
System.err ${pattern}
控制台:输出对应格式的 system.err 输出的红色字体~
[ERROR] 2021-12-21 16:54:09.008 com.itheima.LogbackTest testQuick 16 [main] error [WARN ] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 17 [main] wring [INFO ] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 18 [main] info [DEBUG] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 19 [main] debug [TRACE] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 20 [main] trace输出日志文件:
logback.xml
输出文件 html${log_dir}/logback.log ${pattern}
logback.xml
日志拆分和归档压缩 / 异步日志${log_dir}/logback.html %-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m
logback.xml
${log_dir}/roll_logback.log ${pattern} ${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz 1MB ERROR ACCEPT DENY
为了能够看到效果, 循环 1w次日志打印~
自定义 looger 对象logback.xml
name 设置项目的 包名.包名.xxx 该包下,会默认具有 自定义logger对象, additivity可以用于设置是否继承 rootlogger
[ERROR] 2021-12-21 23:20:13.702 com.wsm.LogbackTest test 16 [main] error [WARN ] 2021-12-21 23:20:13.705 com.wsm.LogbackTest test 17 [main] wring [INFO ] 2021-12-21 23:20:13.705 com.wsm.LogbackTest test 18 [main] info
输出级别变成了 info
Log4j2:Apache Log4j 2是对Log4j的升级版 [官方地址](Log4j – Apache Log4j 2)
参考了logback的一些优秀的设计,并且修复了一些问题,因此带 来了一些重大的提升:
-
异常处理
在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异 常处理机制;
-
性能提升
log4j2相较于log4j 和logback都具有很明显的性能提升 听说提供了十几倍!
-
自动重载配置
参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产 上可以动态的修改日志的级别而不需要重启应用;
-
无垃圾机制
log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集,导致的jvm gc;
创建一个 Maven 工程:
引入依赖:pom.xml
org.apache.logging.log4j log4j-api 2.11.1 org.apache.logging.log4j log4j-core 2.11.1 junit junit 4.12
编写入门案例:Log4j2Test.Java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
public class Log4j2Test {
// 定义日志记录器对象 LogManager.getLogger();
public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
@Test
public void test(){
//log4j2 默认日志级别是 error;
LOGGER.fatal("fatal");
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("inf");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2 09:38:36.825 [main] FATAL com.wsm.Log4j2Test - fatal 09:38:36.838 [main] ERROR com.wsm.Log4j2Test - error
输出结果,提示没有配置对应的 配置文件!,可以在 resources资源目录下创建一个 log4j2.xml的配置文件 log4j2 的配置文件 和 logback大致相同;
虽然输出结果,有警告信息,但是依然正常的打印了日志:log4j2的默认日志级别是 error
Log4j2 配置文件:当前 resources 资源目录下创建一个 log4j2.xml 与 logback.xml配置文件大致相同~
log4j2.xml
/logs
15:41:00.743 [main] [FATAL] com.wsm.Log4j2Test:15 --- fatal 15:41:00.754 [main] [ERROR] com.wsm.Log4j2Test:16 --- error 15:41:00.754 [main] [WARN ] com.wsm.Log4j2Test:17 --- warn 15:41:00.755 [main] [INFO ] com.wsm.Log4j2Test:18 --- inf 15:41:00.755 [main] [DEBUG] com.wsm.Log4j2Test:19 --- debug 15:41:00.755 [main] [TRACE] com.wsm.Log4j2Test:20 --- trace
直接输出就没有,异常信息了, 而且日志级别是 trace
Slf4j + log4j2目前市面上最主流的日志门面就是SLF4J,Log4j2也是日志门面 也有自己对应的实现,现功能非常强大,性能优越
- 虽然,Slf4j 和 logback 是同一个开发者,但是log4j2 功能强大!
- Slf4j + Log4j2应该是未来的大势所趋 可最近出的一个bug 估计,也危险了☠
pom.xml 添加依赖:
- 使用 slf4j 作为 日志门面,需要使用 log4j2的适配器:
org.slf4j slf4j-api 1.7.26 org.apache.logging.log4j log4j-slf4j-impl 2.9.1
重新编写 slf4j 的接口调用方法! Slf4jTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
//slf4j 的获取,日志对象的方法;
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
// 快速入门
@Test
public void test01()throws Exception{
// 日志输出
LOGGER.error("error");
LOGGER.warn("wring");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
15:52:17.495 [main] [ERROR] com.wsm.Slf4jTest:16 --- error 15:52:17.500 [main] [WARN ] com.wsm.Slf4jTest:17 --- wring 15:52:17.501 [main] [INFO ] com.wsm.Slf4jTest:18 --- info 15:52:17.501 [main] [DEBUG] com.wsm.Slf4jTest:19 --- debug 15:52:17.501 [main] [TRACE] com.wsm.Slf4jTest:20 --- trace
现在输出的就是 Slf4j 的,数据了,但是它底层仍然是 log4j2 所以 log4j2.xml配置文件仍然有效果
异步日志:log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益 大大的提高了程序运行效率;
Log4j2提供了两种实现异步日志的方式:
- 一个是通过AsyncAppender 效率并没有特别大提升
- 一个是通过AsyncLogger 常用
两者都需要添加 依赖:
AsyncAppender 异步记录器;com.lmax disruptor 3.3.4
AsyncAppender 方式就直接在 log4j2.xml 添加对应的适配;
AsyncLogger 异步对象;
AsyncLogger 方式, 存在两种方式 全局异步和混合异步
-
全局异步
就是,所有的日志都异步的记录,在配置文件上不用做任何改动, 只需要添加一个 log4j2.component.properties 配置文件;
-
混合异步
你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活
只需要在 resources 资源目录下,创建一个配置文件 log4j2.component.properties 即可:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector混合异步对象:
就是在 log4j2.xml 文件,< Loggers > 中定义,对应的包路径下类对象… 设置成异步执行
无垃圾模式:
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停
-
许多的日志框架, 在稳态日志记录期间分配临时对象
日志事件对象,字符串, 字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC暂停发生的频率;
-
Log4j2 在2.6 版本后, 默认引入了 无垃圾运行模式 尽量的,不使用临时对象
-
2.6之后就默认使用了 无垃圾模式
log4j2.enableThreadlocals 设置为 true, 对象存储在 ThreadLocal字段中并重新使用,否则将为每个日志事件创建新对象 非Web应用程序的默认值
log4j2.enableDirectEncoders 设置为 true 日志事件转换为文本,则将此文本转换 为字节而不创建临时对象 注意: 由于使用共享缓冲区上的同步 所以建议使用异步记录器!
所以,只要升级到2.6 版本即可~
log4j2 日志框架性能,当于其它框架高很多,主要在 2.6之后引入了两个概念: 异步日志 无垃圾模式
SpringBoot 整合日志框架:Idea Spring lnitializr Spring构造器 创建一个 SpringBoot 工程:
SpringBoot 底层默认使用:SLF4J作为日志门面 logback作为日志实现
Maven依赖关系图:
也引入了JUL 和 log4j 的桥接, 可以直接转换!
SpringBoot日志使用test 模块下:DemoApplicationTests.Java
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
//创建日志记录器 导报: slf4j
public static final Logger LOGGER = LoggerFactory.getLogger(DemoApplicationTests.class);
@Test
void contextLoads() {
// 打印日志信息
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
省略… 以上SpringBoot 日志
2021-12-23 15:12:08.771 ERROR 18772 --- [ main] com.wsm.demo.DemoApplicationTests : error 2021-12-23 15:12:08.771 WARN 18772 --- [ main] com.wsm.demo.DemoApplicationTests : warn 2021-12-23 15:12:08.771 INFO 18772 --- [ main] com.wsm.demo.DemoApplicationTests : info
可以看到,SpringBoot 默认集成了 Slf4j logback 默认日志级别是 info 还可以通过,SpringBoot application.properties/yml 配置进行配置文件的更改!
SpringBoot 配置文件 ⚙SpringBoot 可以通过,配置文件,进行简单的修改日志配置:
application.properties 这里使用 pro 可以自行替换为 yml
# 指定自定义 logger 对象日志级别,直接设置根目录级别;
logging.level.com.wsm=trace
# 指定控制台输出消息格式
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n
# 指定存放日志文件的具体路径
# logging.file 已经过期的,因为这样设置不方便管理..
# logging.file=/logs/springboot.log
# logging.file.path 可以指定日志文件存放的目录,默认的文件名 spring.log 具有更好的辨识度了;
logging.file.path=/logs/springboot/
# 指定日志文件消息格式
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n
# Springboot 提供的配置文件不够...只能表示基本的日志配置!
SpringBoot 解析配置文件:
Springboot 默认的配置文件,仅支持基本的日志配置
给类路径下放上每个日志框架自己的配置文件;SpringBoot就不使用默认配置的了
| 日志框架 | 配置文件 |
|---|---|
| Logback | logback-spring.xml 或 logback.xml |
| Log4j2 | log4j2-spring.xml 或 log4j2.xml |
| JUL | logging.properties |
以 logback-spring.xml 举例子:
System.err ${pattern} [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] xxxxxxxx %m %n
application.properties 可以动态调节,生产环境pro 和 开发环境dev
# 指定项目使用的具体环境 spring.profiles.active=pro
来回切换环境, 查看运行效果
dev [ERROR] 2021-12-23 15:51:26.148 com.wsm.demo.DemoApplicationTests contextLoads 15 [main] -------- error [WARN ] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 16 [main] -------- warn [INFO ] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 17 [main] -------- info [DEBUG] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 18 [main] -------- debug [TRACE] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 19 [main] -------- trace pro [ERROR] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 15 [main] xxxxxxxx error [WARN ] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 16 [main] xxxxxxxx warn [INFO ] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 17 [main] xxxxxxxx info [DEBUG] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 18 [main] xxxxxxxx debug [TRACE] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 19 [main] xxxxxxxx trace切换日志 为 log4j2
首先,移除logback 实现依赖,添加log4j2 的依赖~
添加 log4j2的依赖配置:pom.xml
org.springframework.boot spring-boot-starter-web spring-boot-starter-logging org.springframework.boot org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-log4j2
输出日志信息就是 log4j2 的信息了
[ERROR] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== error [WARN ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== warn [INFO ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== info [DEBUG] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== debug [TRACE] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== trace [INFO ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== log4j2 info
添加对应的配置文件: log4j2.xml
重新执行输出的信息就是, 配置文件中的配置 红色字体, ------------- 间隔;
ok ,到这里日志介绍就完毕了, 感谢观看!



