一、什么是log4j2

log4j2 是一个优秀的日志框架,和他对应的其他的日志框架例如logback; 之前的版本是log4j 1.x 这个是升级版。我们一般都是搭配slf4j使用

1.1 日志框架和日志输出接口的关系

日志输出接口,通常我们也叫他日志门面,现在Java中认可度最高的日志门面就是 slf4j (Simple logging facade for Java)

slf4j 和 log4j2 有什么关系呢

slf4j 并不是一个框架,他是一个抽象层,使用适配器模式,它本身不输出日志,还是对接 log4j logback这样的日志输出框架,实现日志输出。

为啥要用 slf4j 这样的门面输出日志啊? log4j logback 不能输出吗?

当然不是,日志框架当然也能输出日志,但是有很多缺陷。

使用 slf4j 的优点

  • 他以适配器模式对接 各种各样的日志框架, 这样当你使用不同的日志框架的时候,不用再去维护两套配置和环境
  • 他本身 提供类似占位符 “{}” 这样的功能,不用再做日志拼接,占用更多内存
  • 注解的引入方式可以更少的节省代码 @slf4j 可以直接通过注解的方式使用, 不用在类之前做繁杂的定义啦

当然 slf4j 的优点还有很多,这里就不一一例举了,感兴趣的可以自己在网上搜一搜。

二、 Log4j2的入门

2.1 最简单的log4j2的使用

接下来我们一点点接触 log4j2, 首先我们不使用 slf4j, 我们直接使用log4j 自己的日志输出

maven 引入依赖

xml复制代码<dependencies>

    <!-- log4j 自带的日志输出 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.17.1</version>
    </dependency>

    <!-- log4j 的内核 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.17.1</version>
    </dependency>

</dependencies>

我们来手写第一个demo

arduino复制代码import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2Application {
    
    private static Logger logger = LogManager.getLogger(Log4j2Application.class);
    
    public static void main(String[] args) {

        // 当没有任何配置文件的时候, 只会打印error及以上级别,但是至少,他能打印

        logger.debug(" debug level ");
        logger.info(" info level ");
        logger.warn(" warn level ");
        logger.error(" error level ");
        logger.fatal(" fatal level ");
    }
}

输出:

ini复制代码10:09:02.444 [main] ERROR com.chason.Log4j2Application -  error level 
10:32:04.712 [main] FATAL com.chason.Log4j2Application -  fatal level

我们发现,没有任何配置文件的时候 log4j 也是能进行输出的,但是他只是输出了 error 及以上级别的日志。

2.2 配置log4j

Log4j2 可以以多种方式进行配置,我们来看看官方文档咋说的

Configuration of Log4j 2 can be accomplished in 1 of 4 ways:

  1. Through a configuration file written in XML, JSON, YAML, or properties format.
  2. Programmatically, by creating a ConfigurationFactory and Configuration implementation.
  3. Programmatically, by calling the APIs exposed in the Configuration interface to add components to the default configuration.
  4. Programmatically, by calling methods on the internal Logger class.

翻译一下

  1. 通过XML、JSON、YAML或者properties格式的配置文件;
  2. 通过创建一个 ConfigurationFactory 和 Configuration 接口的实现
  3. 调用 Configuration 接口暴露的方法来在默认配置的基础上添加其他组件
  4. 通过在内部 Logger 类上调用方法

下面我们一点点的尝试配置的方法

2.2.1 xml文件的方式配置

这种方式是我们生产中最常见的方式了,弄一个log4j2.xml的配置文件,直接把所有的日志输出方式都写在里面,只要开发过的应该都有映象哈

resources 下新建一个log4j2.xml文件,文件内容如下

xml复制代码<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="debug" monitorInterval="30">

    <Properties>
        <Property name="date_format" value="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        <Property name="log_path">D:/logs</Property>
    </Properties>

    <Appenders>
        <Console name="ConsoleAppend" target="SYSTEM_OUT">
            <PatternLayout pattern="${date_format}"/>
        </Console>

        <File name="FileAppend" fileName="${log_path}/xml_config.log" bufferedIO="true" immediateFlush="true">
            <PatternLayout>
                <pattern>${date_format}</pattern>
            </PatternLayout>
        </File>
    </Appenders>

    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="ConsoleAppend"/>
            <AppenderRef ref="FileAppend"/>
        </Root>
    </Loggers>
</Configuration>

我们测试代码基本不变:

arduino复制代码import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2Application {

    private static Logger logger = LogManager.getLogger(Log4j2Application.class);

    public static void main(String[] args) {

        // 配置了log4j2.xml文件之后
        logger.debug(" debug level ");
        logger.info(" info level ");
        logger.warn(" warn level ");
        logger.error(" error level ");
        logger.fatal(" fatal level ");

    }

}

执行结果:

ini复制代码10:44:35.713 [main] INFO  com.chason.Log4j2Application -  info level 
10:44:35.715 [main] WARN  com.chason.Log4j2Application -  warn level 
10:44:35.716 [main] ERROR com.chason.Log4j2Application -  error level 
10:44:35.716 [main] FATAL com.chason.Log4j2Application -  fatal level 

对应的目录下也出现了日志文件

image.png

下面我么简单解释下上面的配置文件:

xml复制代码monitorInterval:  Log4j每隔指定的秒数来重新读取配置文件,刷新配置
<Properties> :    里面是一些自定义的参数 上面的例子定义了一个输出格式和日志输出的文件夹
<Appenders> :      使用Appenders元素可以将日志事件数据写到各种目标位置

<Console> :       控制台打印日志的配置
<File> :           普通文件日志的配置
2.2.2 properties 文件配置的方式

我们在resources 创建一个 log4j2.properties文件

文件内容如下

ini复制代码# 类比成 log4j2.xml 中的<property>
property.filename = D:/logs/proFile.log

# 控制台输出的相关配置

# 输出类型 控制台
appender.console.type = Console
# 给他起个名字
appender.console.name = STDOUT
# 格式相关的配置
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %m%n

# 文件输出的相关配置

# 输出类型 文件
appender.file.type = File
# 起个名字
appender.file.name = LocalFile
# 输出到哪个文件
appender.file.fileName = ${filename}
# 输出格式
appender.file.layout.type = PatternLayout
appender.file.layout.pattern = %d{HH:mm:ss.SSS} --> [%t] %-5level %logger{36} - %m%n

# root 节点相关配置

# 日志级别: 这个级别 是个兜底级别,真的级别还是根据我们配置的对应的日志级别而定
# 如果我们配置了过滤器的级别 ,那么会生效过滤器级别,否则以这个日志级别为准
rootLogger.level = debug

# 2.17.2 版本以下通过这种方式将 root 和 Appender关联起来
# 2.17.2 版本以上有更简便的写法
rootLogger.appenderRef.stdout.ref = STDOUT
rootLogger.appenderRef.file.ref = LocalFile

运行结果如下:

ini复制代码16:37:17.749 [main] DEBUG com.chason.app.Log4j2Application -  debug level 
16:37:17.753 [main] INFO  com.chason.app.Log4j2Application -  info level 
16:37:17.753 [main] WARN  com.chason.app.Log4j2Application -  warn level 
16:37:17.753 [main] ERROR com.chason.app.Log4j2Application -  error level 
16:37:17.757 [main] FATAL com.chason.app.Log4j2Application -  fatal level 

同时对应的文件夹下也生成了文件

image.png

好了,截止到目前为止,我们已经使用了两种配置文件的方法配置log4j2 并实现对应的日志输出,其他配置文件的方法大家参考官方文档自己尝试尝试,如果真的看不懂或者懒得看,留言给我吧,大家都期望的话我把配置文件的方式给补全。

大家发现一个问题没有,当前我的resources下面其实有两个配置文件, 我在演示properties配置的时候没有让大家删除xml文件吧,那么为啥在有了两个配置文件之后,log4j2居然没有迷糊, 而且这次好像xml配置文件没有生效?这是怎么回事呢?

我们来看看log4j的启动加载过程,我们在 idea中加入 启动参数 -Dlog4j.debug

image.png

再次运行程序,我们看看控制台, 我们发现控制台中多了好多东西啊, 但是我们先别慌,关注下面一段内容

kotlin复制代码TRACE StatusLogger Trying to find [log4j2-test18b4aac2.properties] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.properties] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yaml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.yaml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.json] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.json] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.jsn] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.jsn] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.xml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test18b4aac2.xml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.properties] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.properties] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.yml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.yml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.yaml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.yaml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.json] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.json] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.jsn] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.jsn] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2-test.xml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j2-test.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j2-test.xml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.properties] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.properties] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.yml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.yml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.yml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.yaml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.yaml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.yaml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.json] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.json] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.json] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.jsn] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.jsn] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.jsn] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j218b4aac2.xml] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.
TRACE StatusLogger Trying to find [log4j218b4aac2.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
TRACE StatusLogger Trying to find [log4j218b4aac2.xml] using ClassLoader.getSystemResource().
TRACE StatusLogger Trying to find [log4j2.properties] using context class loader sun.misc.Launcher$AppClassLoader@18b4aac2.

发现 log4j 其实在启动过程中找了很多配置文件,从properties 到 yml 再到 yaml 再到 json 再到 jsn 再到 xml, 当找到了一个配置文件之后,他就开始加载配置,后面的配置文件就不再加载了。

这我们就明白了,原来配置文件的加载还有先后顺序。 不仅文件类型有讲究,文件名也有讲究啊。

OK, 这就明白为啥 有 properties 之后 xml 配置文件没有生效了。

官方文档怎么说的呢?

  1. Log4j will inspect the "log4j2.configurationFile" system property and, if set, will attempt to load the configuration using the ConfigurationFactory that matches the file extension. Note that this is not restricted to a location on the local file system and may contain a URL.
  2. If no system property is set the properties ConfigurationFactory will look for log4j2-test.properties in the classpath.
  3. If no such file is found the YAML ConfigurationFactory will look for log4j2-test.yaml or log4j2-test.yml in the classpath.
  4. If no such file is found the JSON ConfigurationFactory will look for log4j2-test.json or log4j2-test.jsn in the classpath.
  5. If no such file is found the XML ConfigurationFactory will look for log4j2-test.xml in the classpath.
  6. If a test file cannot be located the properties ConfigurationFactory will look for log4j2.properties on the classpath.
  7. If a properties file cannot be located the YAML ConfigurationFactory will look for log4j2.yaml or log4j2.yml on the classpath.
  8. If a YAML file cannot be located the JSON ConfigurationFactory will look for log4j2.json or log4j2.jsn on the classpath.
  9. If a JSON file cannot be located the XML ConfigurationFactory will try to locate log4j2.xml on the classpath.
  10. If no configuration file could be located the DefaultConfiguration will be used. This will cause logging output to go to the console.

翻译一下:

  1. log4j 会去检查 log4j2.configurationFile 系统属性 ,若被配置了 ,将尝试使用与文件扩展名匹配的ConfigurationFactory加载配置, 注意,这并不局限于本地文件系统上的某个位置,而且可能包含一个URL。
  2. 如果没有设置系统属性,则ConfigurationFactory属性将查找 classpath 中的 log4j2-test.perperties
  3. 若没找到,则YAML ConfigurationFactory就在 classpath 中寻找log4j2-test.yaml 或 log4j2-test.yml配置文件
  4. 若没找到,则JSON ConfigurationFactory就在 classpath 中寻找 log4j2-test.json 或 log4j2-test.json 配置文件
  5. 若没找到,则XML ConfigurationFactory 就在 classpath 中寻找log4j2-test.xml配置文件
  6. 若没找到测试配置文件,则properties ConfigurationFactory就在 classpath 中寻找log4j2.properties配置文件
  7. 若没找到,则YAML ConfigurationFactory就在 classpath 中寻找 log4j2.yaml 或 log4j2.yml 配置文件
  8. 若没找到,则JSON ConfigurationFactory就在 classpath 中寻找 log4j2.json 或 log4j2.jsn 配置文件
  9. 若没找到,则XML ConfigurationFactory 就在 classpath 中寻找 log4j2.xml 配置文件 (生产环境常用)
  10. 如果找不到配置文件,将使用DefaultConfiguration。这将导致日志输出到控制台

OK , 第一节,我们就简单说说 log4j2 是啥, 和slf4j 的关系, 以及入门级别的用法, 配置文件的加载顺序等等。 后面我们将继续深入,了解怎么通过代码进行 log4j2的配置,以及log4j2的更深原理,他为什么这么快。

下节见

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐