Log4j & Log4j2~从入门到入坑。

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。

发布了47 篇原创文章 · 获赞 1 · 访问量 1167

猜你喜欢

转载自blog.csdn.net/lyfGeek/article/details/104700445