2021/12/14
- 间隔过年还有 44天
想家😘
,这两天有一件事闹的满城风雨的~
-
某个日志框架,呈现了重大的bug ,据说好多,大厂大佬加班加点的改bug~😱
而后,自己想如同自己对日志框架还不怎么了理解,只晓得导入依赖,配置文件,失常的应用…
更深刻的就不太革除了...
连忙,学习总结了一波笔记~👍
日志框架📕
日志的概念
日志文件是用于记录零碎操作事件的文件汇合
-
在计算机领域,日志文件
logfile
是一个记录了产生在运行中的操作系统或其他软件中的事件的文件,或者记录了在网络聊天软件的用户之间发送的音讯。
为什么须要日志:
-
对于一个应用程序来说日志记录是必不可少的一部分
能够帮忙,开发者疾速的定位问题,找到问题,进行解决…👍
现有的日志框架🛠
呈现日期排序:
Java 日志框架倒退了很多年,曾经呈现了很多个版本~倒退过程也比拟凌乱~
- log4j → JUL → JUC
Commons Logging
→ Slf4j → Logback → log4j2
Java日志框架发展史✍:
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
JUL全称Java.util.Logging
- 是java原生的日志框架,应用时不须要另外援用第三方类库
- 绝对其余日志框 架使用方便,学习简略,可能在小型利用中灵便应用
JUL 架构介绍
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 是非凡的日志级别
全副敞开
全副启动
高级Demo
验证,JUL默认输入级别:
//查看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对象的父子关系~
JUL 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
JUL 配置文件模式应用:
下面的,硬编码模式,能够实现简略的 日志记录输入,然而理论开发中,并不不便 开发中应用🙁
- 下面咱们晓得,
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
/** 配置文件模式应用,JUL */
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就理解这么多了,当初用的也很少了…简直没有公司在应用了...
Log4j
Log4j是Apache下的一款开源的日志框架:官方网站
- 通过在Log4J,咱们能够管制日志信息输入到:
控制台
、文件
、甚至是数据库中
- 咱们能够管制每一条日志的输入格局,通过定义日志的输入级别,能够 更灵便的管制日志的输入过程
Log4j组件
Log4J 次要由:Loggers日志记录器
Appenders输入端
Layout日志格式化器
Loggers日志记录器
管制日志的输入级别与日志是否输入
Logger.getLogger(类的全限定名/类对象~);
-
Logger的名字大小写敏感,其命名
有继承机制
com.wsm 会继承,com限定名的日志属性~
-
Log4J中有一个非凡的logger叫做“root”
他是所有logger的根,也就意味着其余所有的logger都会间接 或者间接地继承自root
root logger能够用Logger.getRootLogger()办法获取
Appenders输入端
指定日志的输入中央输入到控制台、文件
输入端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输入到控制台 |
FileAppender | 将日志输入到文件中 |
DailyRollingFileAppender | 将日志输入到一个日志文件,并且每天输入到一个新的文件 |
RollingFileAppender | 将日志信息输入到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会主动把文件改名,同时产生一个新的文件 |
JDBCAppender | 把日志信息保留到数据库中 |
Layout日志格式化器
管制日志信息的输入格局
格式化器类型 | 作用 |
---|---|
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-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 为了不便测试,能够导入 JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- log4j 具备操作数据库的性能,引入对应的 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
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
等很多的配置文件,属性…
rootLogger 配置文件加载默认 日志对象
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
/** 配置文件加载: rootlogger对象; */
@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,也能够指定格局
前期这样的格局,公司都是有固定的标准的曾经配置好了
理解即可!
输入日志,保留文件:file
log4j.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 ,数据默就追加了~
依照文件大小拆分 rollingFile
log4j.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
/** 自定义 logger 对象 */
@Test
public 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实例)
JCL入门
创立Maven工程
增加 pom.xml
依赖
<dependencies>
<!-- JCL的日志依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- junit的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- log4j的依赖! -->
<!-- <dependency>-->
<!-- <groupId>log4j</groupId>-->
<!-- <artifactId>log4j</artifactId>-->
<!-- <version>1.2.17</version>-->
<!-- </dependency>-->
</dependencies>
JCLTest.Java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
/** JUL入门案例~ */
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日志门面次要提供两大性能:
- 日志框架的绑定
- 日志框架的桥接
SLF4J入门
创立Maven工程:
导入对应的依赖:
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
Slf4jTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Slf4j入门案例 */
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应用.
<!--logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
slf4j 空实现:
当咱们还不能确定,应用什么日志框架来实现时候,能够引入 slf4j-nop
它是 slf4j的一种空实现,因为如果不引入 slf4j的实现,会报错Defaulting to no-operation (NOP) logger implementation
- 正文其它依赖
日志实习依赖
运行程序异样~
- 增加
slf4j-nop
依赖,运行test();
<!-- nop 日志开关: slf4j的一种空实现~ -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
</dependency>
- 运行,不出异样,但无后果…
Log4j 绑定
因为,Log4j诞生早于,Slf4j所以并没有间接对其进行实现,slf4j 与 log4j 进行绑定
还须要增加一个 适配器
正文其它实现依赖…
<!--绑定 log4j 日志实现,须要导入适配器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!-- log4j依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
运行 test()
报错:log4j:WARN No appenders could be found for logger (com.wsm.Slf4jTest).
- 须要引入对应的
appenders
须要,增加对应的log4j.properties
配置文件~
JUL 绑定
同上,JUL也须要增加一个适配器
因为JUL 是JDk 自带的,所以,只须要引入一个 适配器依赖即可!
<!--绑定 jul 日志实现,须要导入适配器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
JUL ,JDK内有默认的 配置文件.
总结:⭐
应用slf4j的日志绑定流程:
- 增加slf4j-api的依赖
- 应用slf4j的API在我的项目中进行对立的日志记录
-
绑定具体的日志实现框架:
绑定曾经实现了slf4j的日志框架,间接增加对应依赖
绑定没有实现slf4j的日志框架,先增加日志的适配器,再增加实现类的依赖
- slf4j有且仅有一个日志实现框架的绑定(如果呈现多个
默认应用第一个依赖日志实现
日志框架的桥接Bridging
假如,忽然让你保护一个比拟老的我的项目。老板让你把原先我的项目的日志框架 log4j 换成 logback
- 咋办,两个框架一个属于
slf4j门面
一个属于JCL门面
间接替换jar包,必定不行! - 那怎么办❓
移除依赖,批改代码❓
那真的太狗血了🐕!
还好,slf4j 提供了桥接,技术 反对开发者引入对应的日志的 桥接器
来实现配置!
log4j 切换 logback Demo
Slf4j模块,增加 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的依赖...
<!-- Slf4j的桥接技术 -->
<!-- 将log4j 日志框架,不更改代码清空下,替换为 logback: 正文掉所有的依赖,只保留 JUnit -->
<!-- log4j的所需依赖 -->
<!-- <dependency>-->
<!-- <groupId>log4j</groupId>-->
<!-- <artifactId>log4j</artifactId>-->
<!-- <version>1.2.17</version>-->
<!-- </dependency>-->
<!-- 正文,log4j 的实现依赖; -->
<!--logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--配置 log4j 的桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
十二月 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 –调用–> 日志实现 陷入死循环,堆栈溢出!
Logback
Logback是由log4j创始人设计的另一个开源日志组件:官网地址
Logback次要分为三个模块:
- logback-core:其它两个模块的根底模块,
Maven具备继承个性,引入了 classic/access 就默认具备 core了
- logback-classic:它是log4j的一个改进版本,同时它残缺实现了slf4j API
- logback-access:拜访模块与Servlet容器集成提供通过Http来拜访日志的性能
用于Tomcat/Jetty 日志间接输入到对应的服务器日志中去!
Logback 入门案例:
创立Maven 工程
引入 logback 依赖:
<!--slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--junti 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
LogbackTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** logBack日志入门案例 */
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 - debug
Logback 介绍:
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中
Logback 配置⚙:
配置文件入门:
resources 资源目录下增加: logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- configuration xml的根节点,存在三个属性;
scan: 当此属性设置为true时,配置文件如果产生扭转,将会被从新加载,默认值为true
scanPeriod: 设置监测配置文件是否有批改的工夫距离,如果没有给出工夫单位,默认单位是毫秒,当scan为true时,此属性失效;
debug: 当此属性设置为true时,将打印出logback外部日志信息,实时查看logback运行状态。默认值为false;
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- contextName:
用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default;
置成其余名字,用于辨别不同应用程序的记录;
-->
<contextName>mylog</contextName>
<!-- property:
用来定义变量值,它有两个属性name和value,通过<property>定义的值 能够被配置文件上下文援用, ${name名} 来应用变量
name: 变量的名称
value: 的值时变量定义的值
-->
<!--定义日志文件保留门路属性-->
<property name="log_dir" value="/logs"></property>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>
<!-- 日志输入格局:
%-5level
%d{yyyy-MM-dd HH:mm:ss.SSS}日期
%c类的残缺名称
%M为method
%L为行号
%thread线程名称
%m或者%msg为信息
%n换行 -->
</configuration>
控制台日志输入的 appender
logback.xml
追加:
<!-- 控制台日志输入的 appender 它有两个必要属性name和class;
name指定appender名称
class指定appender的全限定名,不同的 输入Appender 又有不同的子元素设置:
-->
<!-- ConsoleAppender 把日志输入到控制台;
<encoder>: 对日志进行格式化
<target>: Java控制台输入,字符串System.out(默认)或者System.err(红色字体
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--管制输入流对象 默认 System.out 改为 System.err-->
<target>System.err</target>
<!--日志音讯格局配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- rootlogger 配置:
level: 设置日志输入级别;
<appender-ref>: 设置rootlogger 绑定的appender,反对绑定多个~
-->
<root level="ALL">
<appender-ref ref="console"/> <!-- 依据 appender 的name 属性进行关联; -->
</root>
控制台:输入对应格局的 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
<!-- 日志文件输入的 appender-->
<!-- FileAppender 把日志增加到文件;
<file>: 被写入的文件名,能够是绝对目录,也能够是相对目录,如果下级目录不存在会主动创立,没有默认值;
<append>: 如果是 true 日志被追加到文件结尾 (默认 true;
<encoder>: 对记录事件进行格式化;
<prudent>: 如果是 true,日志会被平安的写入文件,即便其余的FileAppender也在向此文件做写入操作; (效率低默认是 false;
-->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<!--日志文件保留门路: ${log_dir}引入下面的,全局变量-->
<file>${log_dir}/logback.log</file>
<!--日志音讯格局配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- rootlogger 配置:
level: 设置日志输入级别;
<appender-ref>: 设置rootlogger 绑定的appender,反对绑定多个~
-->
<root level="ALL">
<appender-ref ref="console"/> <!-- 依据 appender 的name 属性进行关联; -->
<appender-ref ref="file"/>
</root>
输入文件 html
logback.xml
<!--html 格局日志文件输入 appender-->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
<!--日志文件保留门路-->
<file>${log_dir}/logback.html</file>
<!--html 音讯格局配置-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
</layout>
</encoder>
</appender>
<!-- 设置:rootlogger 配置 -->
日志拆分和归档压缩 / 异步日志
logback.xml
<!-- 日志拆分和归档压缩的 appender 对象-->
<!-- RollingFileAppender
省略与 FileAppender 雷同的元素;
<rollingPolicy>: 当产生滚动时,决定RollingFileAppender的行为,波及文件挪动和重命名。属性class定义具体的滚动策略类;
<prudent>: 当为true时,不反对FixedWindowRollingPolicy,
-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志文件保留门路-->
<file>${log_dir}/roll_logback.log</file>
<!--日志音讯格局配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!--指定拆分规定-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--fileNamePattern 依照工夫和压缩格局申明拆分的文件名-->
<fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!--maxFileSize 设置依照文件大小拆分: -->
<maxFileSize>1MB</maxFileSize>
<!-- maxHistory 可选节点,管制保留的归档文件的最大数量,超出数量就删除旧文件; -->
</rollingPolicy>
<!--日志级别过滤器-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--日志过滤规定
<level>: 设置过滤级别
<onMatch>: 用于配置合乎过滤条件的操作
<onMismatch>: 用于配置不合乎过滤条件的操作
onMatch/onMismatch 执行一个过滤器会有返回个枚举值: DENY,NEUTRAL,ACCEPT
DENY 日志将立刻被摈弃不再通过其余过滤器;
NEUTRAL 有序列表里的下个过滤器过接着解决日志;
ACCEPT 日志会被立刻解决,不再通过残余过滤器;
-->
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步日志
appender反对通过, appender-ref 间接援用其它的 appender属性~
-->
<!-- AsyncAppender
写出文件,时候会开启一个线程,以异步模式写出日志文件~
-->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<!--指定某个具体的 appender-->
<appender-ref ref="rollFile"/>
</appender>
<!-- 设置:rootlogger 配置 -->
为了可能看到成果, 循环 1w次日志打印~
自定义 looger 对象
logback.xml
<!--自定义 looger 对象
additivity="false" 自定义 logger 对象是否继承 rootLogger
-->
<logger name="com.wsm" level="info" additivity="false">
<appender-ref ref="console"/>
</logger>
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的升级版 官网地址)
参考了logback的一些优良的设计,并且修复了一些问题,因而带 来了一些重大的晋升:
-
异样解决
在logback中,Appender中的异样不会被利用感知到,然而在log4j2中,提供了一些异 常解决机制;
-
性能晋升
log4j2相较于log4j 和logback都具备很显著的性能晋升
据说提供了十几倍!
-
主动重载配置
参考了logback的设计,当然会提供主动刷新参数配置,
最实用的就是咱们在生产 上能够动静的批改日志的级别而不须要重启利用
; -
无垃圾机制
log4j2在大部分状况下,都能够应用其设计的一套无垃圾机制,防止频繁的日志收集,导致的jvm gc;
Log4j2入门案例:
创立一个 Maven 工程:
引入依赖:pom.xml
<dependencies>
<!--log4j2日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!--log4j2 日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<!--junit 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
编写入门案例:Log4j2Test.Java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
/** log4j2 疾速入门 */
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
<?xml version="1.0" encoding="UTF-8"?>
<!--
status="warn" 日志框架自身的输入日志级别
monitorInterval="5" 主动加载配置文件的间隔时间,不低于 5 秒,益处是:生产环境下的我的项目,进行更改,零碎会主动从新加载!
-->
<Configuration status="debug" monitorInterval="5">
<!-- 集中配置属性进行治理,想到于: 成员变量 ,应用时通过:${name} -->
<properties>
<property name="LOG_HOME">/logs</property>
</properties>
<!--日志解决,设置appenders 集-->
<Appenders>
<!--控制台输入 appender
target: 控制台输入的形式 System.err / System.out
-->
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<!--日志文件输入 appender-->
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</File>
<!--应用随机读写流的日志文件输入 appender,性能进步-->
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</RandomAccessFile>
<!--依照肯定规定拆分的日志文件的 appender
filePattern: 拆分文件名,$${date:yyyy-MM-dd}每天生成一个文件夹, %i 数字排序~
-->
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!--日志级别过滤器-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<!--日志音讯格局-->
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<!--在系统启动时,登程拆分规定,生产一个新的日志文件-->
<OnStartupTriggeringPolicy />
<!--依照文件大小拆分,10MB -->
<SizeBasedTriggeringPolicy size="10 MB" />
<!--依照工夫节点拆分,规定依据filePattern定义的-->
<TimeBasedTriggeringPolicy />
</Policies>
<!--在同一个目录下,文件的个数限定为 30 个,超过进行笼罩-->
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<!--logger 定义-->
<Loggers>
<!--应用 rootLogger 配置 日志级别 level="trace"-->
<Root level="trace">
<!--指定日志应用的处理器-->
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
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 预计,也危险了☠
Slf4j + log4j2 整合案例:
pom.xml
增加依赖:
- 应用 slf4j 作为 日志门面,须要应用 log4j2的适配器:
<!--应用slf4j 作为日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--应用 log4j2 的适配器进行绑定-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
</dependency>
从新编写 slf4j 的接口调用办法!
Slf4jTest.Java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** slf4j + log4j2整合 */
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
罕用
两者都须要增加 依赖:
<!--异步日志依赖-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
AsyncAppender 异步记录器;
AsyncAppender 形式就间接在 log4j2.xml
增加对应的适配;
<Appenders>
<!-- 异步输入 appender -->
<Async name="Async">
<!-- 指定须要被异步执行的适配器: ref="适配器name" -->
<AppenderRef ref="Console"/>
<!-- <AppenderRef ref="file"/> 能够多个...-->
</Async>
</Appenders>
AsyncLogger 异步对象;
AsyncLogger 形式, 存在两种形式 全局异步和混合异步
-
全局异步
就是,所有的日志都异步的记录,在配置文件上不必做任何改变, 只须要增加一个
log4j2.component.properties
配置文件; -
混合异步
你能够在利用中同时应用同步日志和异步日志,这使得日志的配置形式
更加灵便
全局异步对象:
只须要在 resources 资源目录下,创立一个配置文件 log4j2.component.properties
即可:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合异步对象:
就是在 log4j2.xml
文件,< Loggers > 中定义,对应的包门路下类对象.. 设置成异步执行
<!--logger 定义-->
<Loggers>
<!-- 自定义异步 logger 对象 -->
<AsyncLogger name="com.wsm" level="trace" includeLocation="false" additivity="false">
<AppenderRef ref="Console"/>
</AsyncLogger>
</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
举例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] -------- %m %n"></property>
<!--控制台日志输入的 appender-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--管制输入流对象 默认 System.out 改为 System.err-->
<target>System.err</target>
<!--日志音讯格局配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- <springProfile>: SpringBoot 反对应用这样的对日志进行设置不同的场景~ -->
<!-- 开发环境 -->
<springProfile name="dev">
<pattern>${pattern}</pattern>
</springProfile>
<!-- 生产环境 -->
<springProfile name="pro">
<pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] xxxxxxxx %m %n</pattern>
</springProfile>
</encoder>
</appender>
<!--自定义 looger 对象 additivity="false" 自定义 logger 对象是否继承 rootLogger -->
<logger name="com.wsm" level="info" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
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
<dependencies>
<!-- SpringBoot web starter 的集中依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--idea 执行后,排除 logback 日志实现-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- SpringBoot test starter 的集中依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--应用 log4j2 的日志启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
输入日志信息就是 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
<?xml version="1.0" encoding="UTF-8"?>
<!--
status="warn" 日志框架自身的输入日志级别
monitorInterval="5" 主动加载配置文件的间隔时间,不低于 5 秒
-->
<Configuration status="trace" monitorInterval="5">
<!--日志解决-->
<Appenders>
<!--控制台输入 appender-->
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L -------------- %m%n" />
</Console>
</Appenders>
<!--logger 定义-->
<Loggers>
<!--应用 rootLogger 配置 日志级别 level="trace"-->
<Root level="trace">
<!--指定日志应用的处理器-->
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
从新执行输入的信息就是, 配置文件中的配置 红色字体, ------------- 距离;
ok ,到这里日志介绍就结束了, 感激观看! 😀📺
发表回复