Log4j & Log4j2~从入门到入坑。
文章目录
为什么需要 Log4j。
我们在做开发时,要使用
System.out.println()
来查看程序的执行结果,以便开发人员根据结果做进一步调试。
在项目上线之后,我们仍然需要追踪程序的运行以便继续进行维护。
但是,System.out.println();
是非常消耗资源的。
而且,如果我们需要在某处增加或删除该模块的日志追踪,如果使用System.out.println();
就需要修改源码,重新编译,测试,上线。。。
这时,我们就需要一个“遥控器”,随时打开或关闭日志的输出(空调)。
于是,Log4j 来了。
关键是,ta 还带来了“调节空调温度”功能——日志级别:
trace > debug > info > warn > error > fatal。
并且,不再仅限于输出在 console,还可以输出到文件。(联想:Linux 管道符:>
)。
JUnit 优势。
“空调开关”。
- 不修改源码情况下随时开关(修改配置文件)。
- 定义输出什么内容(日志级别 or 包名 + 类名 + 方法名)。
- 定义输出到哪里(控制台或文件)。
日志。
调试日志。
运行日志。
异常日志。
日志级别。
fatal
error
warn
info
debug
trace
四个关键。
目的地(appender)
输出到什么地方。
log4j.appender.geek.File=org.apache.log4j.FileAppender布局(layout)
log4j.appender.geek.File.layout=org.apache.log4j.PatternLayout
log4j.appender.geek.File.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n控制单元(logger)
log4j.rootLogger=debug,geek.File,geek.Console
级别(level)
trace > debug > info > warn > error > fatal。
打印自身,往后靠。
在 resources 目录下创建 log4j.properties 配置文件。
# 全部在同一个文件中。
#log4j.appender.geek.File=org.apache.log4j.FileAppender
# 每天产生一个文件。
log4j.appender.geek.File=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.geek.File.file=/home/geek/geek/geek.log
log4j.appender.geek.File.file=/home/geek/IdeaProjects/log4j_demo/src/main/resources/geek.log
log4j.appender.geek.File.DatePattern=.yyyy-MM-dd
log4j.appender.geek.File.layout=org.apache.log4j.PatternLayout
log4j.appender.geek.File.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n
log4j.appender.geek.Console=org.apache.log4j.ConsoleAppender
log4j.appender.geek.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.geek.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n
log4j.rootLogger=debug,geek.File,geek.Console
# 如果不指定,默认用 rootLogger(控制器)。
# 由 rootLogger 控制器指挥,用 debug 级别,输出到文件和控制台。
com.geek.dao.UserDao
package com.geek.dao;
import org.apache.log4j.Logger;
public class UserDao {
// log4j。
private static final Logger LOGGER = Logger.getLogger(UserDao.class);
// slf4j
// private Logger logger1 = LoggerFactory.getLogger(this.getClass());
public static void main(String[] args) {
LOGGER.debug("hello, log4j~~~debug");
LOGGER.info("hello, log4j~~~info");
LOGGER.warn("hello, log4j~~~warn");
LOGGER.error("hello, log4j~~~error");
}
}
输出。
2020-03-06 17:28:37,407 DEBUG (com.geek.dao.UserDao:main) - hello, log4j~~~debug
2020-03-06 17:28:37,411 INFO (com.geek.dao.UserDao:main) - hello, log4j~~~info
2020-03-06 17:28:37,412 WARN (com.geek.dao.UserDao:main) - hello, log4j~~~warn
2020-03-06 17:28:37,412 ERROR (com.geek.dao.UserDao:main) - hello, log4j~~~error
Process finished with exit code 0
并且会在 log4j.appender.geek.File.file=/home/geek/IdeaProjects/log4j_demo/src/main/resources/geek.log 生成日志文件。
2020-03-06 17:29:56,152 DEBUG (com.geek.dao.UserDao:main) - hello, log4j~~~debug
2020-03-06 17:29:56,156 INFO (com.geek.dao.UserDao:main) - hello, log4j~~~info
2020-03-06 17:29:56,157 WARN (com.geek.dao.UserDao:main) - hello, log4j~~~warn
2020-03-06 17:29:56,157 ERROR (com.geek.dao.UserDao:main) - hello, log4j~~~error
分析。
# 现在来看
# log4j.appender.geek.File.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n
# %5p ——> 日志级别处,占 5 位,不足 5 位,前面加 空格 占位。
# (%C:%M) ——> 包名类名 + ":" + 方法名。
# %m ——> 日志内容。
# %n ——> 回车。
# 2020-03-06 17:29:56,152 DEBUG (com.geek.dao.UserDao:main) - hello, log4j~~~debug
# 2020-03-06 17:29:56,156 INFO (com.geek.dao.UserDao:main) - hello, log4j~~~info
# 2020-03-06 17:29:56,157 WARN (com.geek.dao.UserDao:main) - hello, log4j~~~warn
# 2020-03-06 17:29:56,157 ERROR (com.geek.dao.UserDao:main) - hello, log4j~~~error
关于级别。
4 个级别。
debug > info > warn > error。
打印自身,往后靠。
log4j.rootLogger=debug,geek.File,geek.Console
这里为 debug,则打印 debug, info, warn, error。
这里为 info,则打印 info, warn, error。
这里为 warn,则打印 warn, error。
这里为 error,则打印 error。
常见 Appender 输出源。
常见 Appender。
#a=org.apache.log4j.ConsoleAppender(控制台)。
#b=org.apache.log4j.FileAppender(文件)。
#c=org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)。
#d=org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸晨产生一个新的文件)。
#e=org.apache.log4j.WriterAppender(将日志信息以流的格式发送到任意指定的地方)。
#f=org.apache.log4j.jdbc.JDBCAppender(把日志用 JDBC 记录到数据库中)。
Layout 布局。
布局就是指输出信息的格式。在 Log4j 中称作 Layout。
常见 Layout。
a=org.apache.log4j.HTMLLayout(以 HTML 表格形式布局)。
b=org.apache.log4j.PatternLayout(可以灵活地指定布局模式)。
c=org.apache.log4j.SimpleLayout(包含日志信息有级别和信息字符串)。
d=org.apache.log4j.EnhancedPatternLayout(enhanced...加强...)。
e=org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)。
其他参数。
# 常见参数。
%c(输出所属的类所在类目。通常就是所在类的全名)。
%d(输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式)。
eg. %d{yyyy-MM-dd HH:mm:ss,SSS}
%m(输出代码中指定的消息)。
%n(输出一个回车换行符,Windows —> "\r\\n",Unix —> "\n")。
%p(输出优先级。——> `DEBUG`,`INFO`,`WARN`,`ERROR`,`FATAL`)。
%r(输出自应用启动到输出该 log 信息耗费的毫秒数)。
%t(输出产生该日志事件的线程名)。
log 的使用。
通常配合 try { ... } catch { ... }
使用。
package com.geek.dao;
import org.apache.log4j.Logger;
public class UserDao {
// log4j。
private static final Logger LOGGER = Logger.getLogger(UserDao.class);
// slf4j。(logback)。
// private Logger logger1 = LoggerFactory.getLogger(this.getClass());
public static void main(String[] args) {
int age = 0;
try {
LOGGER.debug("111");
age = 10 / 0;
LOGGER.debug("222");
} catch (Exception e) {
e.printStackTrace();
LOGGER.error(e.getMessage(), e.getCause());
// throw new ...
}
}
}
// 222 不会打印。
2020-03-06 20:00:16,132 DEBUG (com.geek.dao.UserDao:main) - 111
java.lang.ArithmeticException: / by zero
at com.geek.dao.UserDao.main(UserDao.java:23)
2020-03-06 20:00:16,136 ERROR (com.geek.dao.UserDao:main) - / by zero
其中,假设,LOGGER.debug("111");
和 LOGGER.debug("222");
在开发阶段需要,而在生产阶段不需要了。使用 Log4j,在生产阶段临时改需求就不需要修改源码,直接修改配置文件(改为 error 级别,LOGGER.debug 就不会被打印或写入 log 文件。)
输出源。
一个输出源(log4j.rootLogger)。——> 默认使用。
两个输出源。
# log4j.rootLogger=warn, geek.File, geek.Console
# 如果不指定,默认用 rootLogger(控制器)。
# 由 rootLogger 控制器指挥,用 warn 级别,输出到文件和控制台。
#
log4j.logger.com.geek.dao=error, geek.File, geek.Console
log4j.logger.com.geek=debug, geek.Console
log4j.logger. 后面的为包路径。
级别取精确,输出为各自。
级别取 log4j.logger.com.geek.dao=error, geek.File, geek.Console
—> error。
log4j.logger.com.geek=debug, geek.Console
—> 级别为 debug
,> error,不输出。
Appender,Layout,Logger。
每个 appender 后面必然要跟随 layout,指定自己的风格样式。
每个 Logger 都可以指定一个级别,同时引用多个 Appender。
每个 Appender 也同时可以被多个 Logger 引用。
log4j.xml
log4j.xml 优先于 log4j.properties。
优势。
filter 精确匹配,避免往后靠的大于等于,可以 OnlyOne 过滤出我需要的。
additivity=“false” 精确匹配,停止下面的 logger 生效。
log4e
官网:http://log4e.jayefem.de/index.php
可以经常看到:
int age = 0;
if (logger.isDebugEnabled()) {
logger.debug("main() - int age = " + age);
}
使用 if () {} —> 字符串的拼接需要开辟内存空间,消耗资源,影响运行效率。
Log4j2
加入 Maven 依赖。
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
</dependencies>
测试类。
package com.geek.log4j2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Demo {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(Log4j2Demo.class);
logger.trace("hello, log4j~~~trace");
logger.debug("hello, log4j~~~debug");
logger.info("hello, log4j~~~info");
logger.warn("hello, log4j~~~warn");
logger.error("hello, log4j~~~error");
logger.fatal("hello, log4j~~~fatal");
}
}
与 Log4j 不同,Log4j2 在没有配置文件的情况下,默认可以使用。
来看结果。
00:05:17.021 [main] ERROR com.geek.log4j2.Log4j2Demo - hello, log4j~~~error
00:05:17.047 [main] FATAL com.geek.log4j2.Log4j2Demo - hello, log4j~~~fatal
Process finished with exit code 0
默认级别:error
。
接下来自己配置。
测试类。
package com.geek.log4j2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Demo {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(Log4j2Demo.class);
logger.trace("hello, log4j~~~trace");
logger.debug("hello, log4j~~~debug");
logger.info("hello, log4j~~~info");
logger.warn("hello, log4j~~~warn");
logger.error("hello, log4j~~~error");
logger.fatal("hello, log4j~~~fatal");
}
}
配置。这里使用 xml 配置文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!--<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">-->
<Configuration status="off"><!-- 关闭 log4j2 自己的日志。-->
<Appenders>
<Console name="myConsole" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5p][%d{yyyy-MM-dd HH:mm:ss}] [%c %L] %m%n"/>
</Console>
<File name="myLogFile" fileName="/home/geek/IdeaProjects/log4j_demo/lob4j2_demo/src/main/resources/test.log"
append="true">
<PatternLayout pattern="[%-5p][%d{yyyy-MM-dd HH:mm:ss}] [%c %L] %m%n"/>
</File>
<RollingFile name="myRollingFile"
fileName="/home/geek/IdeaProjects/log4j_demo/lob4j2_demo/src/main/resources/app.log"
filePattern="/home/geek/IdeaProjects/log4j_demo/lob4j2_demo/src/main/resources//$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="[%-5p][%d{yyyy-MM-dd HH:mm:ss}] [%c %L] %m%n"/>
<SizeBasedTriggeringPolicy size="1KB"/>
</RollingFile>
</Appenders>
<Loggers>
<!--
<Logger name="" level="info">
<AppenderRef ref="myConsole"/>
</Logger>
-->
<Root level="trace">
<AppenderRef ref="myConsole"/>
<AppenderRef ref="myLogFile"/>
<AppenderRef ref="myRollingFile"/>
</Root>
</Loggers>
</Configuration>
运行后发现报出错误 。目前尚未解决。
但不影响执行结果(日志文件已生成)。
ERROR StatusLogger Unrecognized format specifier [p]
ERROR StatusLogger Unrecognized conversion specifier [p] starting at position 5 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [c]
ERROR StatusLogger Unrecognized conversion specifier [c] starting at position 35 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [L]
ERROR StatusLogger Unrecognized conversion specifier [L] starting at position 38 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [m]
ERROR StatusLogger Unrecognized conversion specifier [m] starting at position 42 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [n]
ERROR StatusLogger Unrecognized conversion specifier [n] starting at position 44 in conversion pattern.
[TRACE][2020-03-07 00:42:57] [com.geek.log4j2.Log4j2Demo 12] hello, log4j~~~trace
[DEBUG][2020-03-07 00:42:57] [com.geek.log4j2.Log4j2Demo 13] hello, log4j~~~debug
[INFO ][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 14] hello, log4j~~~info
[WARN ][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 15] hello, log4j~~~warn
[ERROR][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 16] hello, log4j~~~error
[FATAL][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 17] hello, log4j~~~fatal
Process finished with exit code 0
日志文件。
app.log
hello, log4j~~~trace
hello, log4j~~~debug
hello, log4j~~~info
hello, log4j~~~warn
hello, log4j~~~error
hello, log4j~~~fatal
test.log
[TRACE][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 12] hello, log4j~~~trace
[DEBUG][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 13] hello, log4j~~~debug
[INFO ][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 14] hello, log4j~~~info
[WARN ][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 15] hello, log4j~~~warn
[ERROR][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 16] hello, log4j~~~error
[FATAL][2020-03-07 00:08:57] [com.geek.log4j2.Log4j2Demo 17] hello, log4j~~~fatal
并且生成 .gz
文件。
app-03-07-2020-1.log.gz
slf4j。
Simple Logging Facade for Java。