用Log4j 2记录日志

2023-10-31

说明

maven工程中增加对Log4j 2的依赖

下面代码示例的maven工程中的pom.xml文件中需要增加对Log4j 2的依赖:

  	<dependency>
  	  <groupId>org.apache.logging.log4j</groupId>
  	  <artifactId>log4j-core</artifactId>
  	  <version>2.20.0</version>
  	</dependency>

log4j 2的配置

配置说明参考文档

https://logging.apache.org/log4j/2.x/manual/configuration.html

配置文件中pattern的详细说明

例如,下面配置文件片段中用到了pattern:

  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="File1" fileName="${filename}">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
  </Appenders>

pattern的详细说明请参考:
https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout
在这里插入图片描述

通常给每个类创建自己的Logger

为了便于对日志的过滤、搜索、排序等,通常每个类都获取它自己的带名字的Logger,而不是所有类共用一个Logger。
例如通常的做法:
private static final Logger logger = LogManager.getLogger();
创建一个Logger ,名字就是调用类的全限定名。

建议将Logger 声明为static的

Logger 可以声明为static的、或者非static的,但建议声明为static的。这样做的目的是为了节约实例化的成本。

Logger的名字

大多数log实现用层级结构匹配Logger的名字和日志的配置,层级结构用点号“.”表示,跟包名类似。例如,com.thb.register和com.thb.common的父都是com.thb。例如,下面几种写法得到的Logger的名字相同:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();
}
package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {	
	
	private static final Logger logger = LogManager.getLogger(Test.class);
}
package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {	

	private static final Logger logger = LogManager.getLogger(Test.class.getName());
}

替换参数

在记录日志时,经常需要将一些动态信息放进去,log4j 2和log4j 1.x的做法不同。

在log4j 1.x中的做法为如下:

// log4j 1.x中的做法,代码中要显式判断对应级别的日志是否打开
if (logger.isInfoEnabled()) {
	logger.info("名字为:" + name);
}

上面代码,对日志级别的判断实际进行了两次,第一次是调用isInfoEnabled()方法的时候,第二次是调用info方法记录日志的时候。如果记录日志的地方多,代码就显得庞杂

在log4j 2中的做法为:

// log4j 2中的做法,用替换参数,代码中不需要显式判断对应级别的日志是否打开
logger.info("名字为:{}", name);

上面代码,对日志级别的判断只进行了一次,而且日志字符串只有在对应级别的日志允许输出的情况下才进行构造。代码更加简洁

备注:使用替换参数,后面的参数值如果是函数(没有使用Lambda表达式的方式)就达不到提升性能的效果了,因为此时无论该级别的日志是否真正打开,函数都会被执行。

通过Java-8的Lambda表达式支持日志消息懒构造

log4j 2通过Java-8的Lambda表达式支持日志消息懒构造,即日志消息在对应的日志级别打开的情况下才构造:

logger.info("the information is {}", () -> expensiveOperation())

log4j 2的日志级别

下面日志级别按照由高到低列出:
OFF:不记录日志(其实这个本质上不是日志级别,是个关闭所有日志开关)
FATAL
ERROR
WARN
INFO
DEBUG
TRACE
ALL:所有日志都记录(其实这个本质上不是日志级别,是个打开所有日志开关)

在过滤器或者Logger中配置了某个级别的日志,那么实际会记录该级别及该级别以上的日志。例如,如果配置了日志级别为INFO,那么实际会记录FATAL、ERROR、WARN、和INFO几个级别的日志。

代码示例

代码示例公共说明

如果没有特别说明,下面代码示例中maven工程中src/main/resources/log4j2.xml的配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="File1" fileName="${filename}">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

用LogManager.ROOT_LOGGER_NAME获取root Logger的名字

LogManager.ROOT_LOGGER_NAME是root Logger的名字,这个名字是空字符串""。

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	public static Logger logger = LogManager.getLogger();

	public static void main(String[] args) {
		System.out.println("root logger name: " + LogManager.ROOT_LOGGER_NAME);
		//System.out.println("logger name: " + logger.getName());		
	}

}

运行结果:

在这里插入图片描述
从上面输出结果可以发现,root Logger的名字是空字符串""。

用LogManager的getLogger()获取一个带名字的Logger

LogManager.getLogger()返回一个带名字的Logger,这个Logger的名字就是调用的类的全限定名称。这个方法经常使用。
下面代码中获取Logger的方法是典型的获取方法。

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {		
		System.out.println("logger name: " + logger.getName());		
	}

}

运行输出:
在这里插入图片描述
从上面输出可以看出,Logger的名字是调用类的全限定名称,此处是com.thb.Test。

几种方法获取相同名字的Logger

方法一:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();
	
	public static void main(String[] args) {
		System.out.println(logger.getName());
	}

}

运行输出:

com.thb.Test

方法二:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {	
	
	private static final Logger logger = LogManager.getLogger(Test.class);

	public static void main(String[] args) {
		System.out.println(logger.getName());
	}

}

运行输出:

com.thb.Test

方法三:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {	

	private static final Logger logger = LogManager.getLogger(Test.class.getName());

	public static void main(String[] args) {
		System.out.println(logger.getName());
	}

}

运行输出:

com.thb.Test

用LogManager的getLogger(String name)创建Logger时指定名字

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	// 创建Logger时明确指定了名字为Thb
	private static final Logger logger = LogManager.getLogger("Thb");

	public static void main(String[] args) {
		double r = 2;
		
		// log4j 2中的做法,代码中不需要显式判断对应级别的日志是否打开
		logger.info("面积为:{}", Math.PI * Math.pow(r, 2));
	}

}

运行输出:

08:55:31.715 [main] INFO  Thb - 面积为:12.566370614359172

在控制台打印一条INFO级别的日志

打印日志的代码:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {		
		System.out.println("logger name: " + logger.getName());	
		logger.info("hello");
	}

}

输出:
在这里插入图片描述

在控制台打印日志,使用替换参数构造日志字符串

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {

    /**
     * 日志类.
     */
    private static final Logger LOGGER = LogManager.getLogger();

    /**
     * 主函数.
     * @param args
     */
    public static void main(String[] args) {
        final String name = "Mike";
        final Demo demo = new Demo();
        LOGGER.info("name is {}", name);
    }
}

运行输出:

08:30:09.638 [main] INFO  com.thb.Demo - name is Mike

不正确地使用替换参数:参数值是函数(不是Lambda表达式)

下面代码中本意是要使用替换参数的方式提升性能(字符串晚构造),但因为参数值是函数(不是Lambda表达式的形式),结果就是即便该级别的日志没有实际打开,函数也会被执行。

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {

    /**
     * 日志类.
     */
    private static final Logger LOGGER = LogManager.getLogger();

    /**
     * 主函数.
     * @param args
     */
    public static void main(String[] args) {
        final Demo demo = new Demo();

        // 即便这个级别日志级别没有打开,函数method2也会被执行,因为逗号后面是函数
        LOGGER.trace("name is {}", demo.method2());
    }
    
    /**
     * 函数2.
     * @return 字符串
     */
    public String method2() {
        System.out.println("in method2");
        return "in method2";
    }
}

运行输出:

in method2

从上面输出可以看到,尽管trace级别的日志没有被打印,但代替参数值的函数还是被执行了(我们不期望它执行),这样就没有达到提升性能的目的。

通过Java-8的Lambda表达式支持日志消息懒构造

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {
		double r = 2;
		
		// 通过Java-8的Lambda表达式支持日志消息懒构造
		logger.info("面积为:{}", () -> Math.PI * Math.pow(r, 2));		
	}

}

运行输出:

09:29:48.911 [main] INFO  com.thb.Test - 面积为:12.566370614359172

多个参数都使用Lambda表达式

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {

    /**
     * 日志类.
     */
    private static final Logger LOGGER = LogManager.getLogger();
    private static final int AGE = 10;

    /**
     * 主函数.
     * @param args
     */
    public static void main(String[] args) {
        final Demo demo = new Demo();
        LOGGER.info("name is {} and age is {}", () -> demo.method1(), () -> demo.method2());       
    }

    /**
     * 函数1.
     * @return 名字
     */
    public String method1() {
        return "ok";
    }

    /**
     * 函数2.
     * @return 年龄
     */
    public int method2() {
        return AGE;
    }
}

运行输出:

13:34:17.281 [main] INFO  com.thb.Demo - name is ok and age is 10

参数使用Lambda表达式,因为日志级别没有打开,所以Lambda表达式不会执行

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {

    /**
     * 日志类.
     */
    private static final Logger LOGGER = LogManager.getLogger();

    /**
     * 主函数.
     * @param args
     */
    public static void main(String[] args) {
        final Demo demo = new Demo();

        // 如果这个级别的日志没有打开,method3不会被执行,因为使用了Lambda表达式
        LOGGER.trace("name is {}", () -> demo.method3());
    }

    /**
     * 函数3.
     * @return 字符串
     */
    public String method3() {
        System.out.println("in method3");
        return "in method3";
    }
}

运行以后没有任何输出,这个是因为trace级别的日志没有被打开,Lambda表达式没有被执行,因此函数method3没有被执行。

两个类都定义了静态的Logger,并且在一个类中调用另外一个类的方法

定义一个类,类中定义了自己的静态Logger:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class AnotherClass {
	private static final Logger logger = LogManager.getLogger();

	public void method() {
		logger.info("hello from AnotherClass");
	}
}

定义一个主类,在主类中也定义了一个静态Logger:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {		
		logger.info("hello from Test");
		
		AnotherClass another = new AnotherClass();
		another.method();
	}

}

运行结果:

15:10:28.161 [main] INFO  com.thb.Test - hello from Test
15:10:28.199 [main] INFO  com.thb.AnotherClass - hello from AnotherClass

用traceEntry(String format, Object… params)在函数的入口记录trace日志

maven工程中src/main/resources/log4j2.xml的配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="File1" fileName="${filename}">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

记录日志的代码示例:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {
		Test test = new Test();
		test.method("hello", 10);		
	}
	
	public void method(String name, int age) {
		logger.traceEntry("name: {} and age: {}", name, age);		
	}

}

运行输出:

10:26:48.335 [main] TRACE com.thb.Test - Enter name: hello and age: 10

用traceExit(R result)在函数的结尾记录trace日志

maven工程中src/main/resources/log4j2.xml的配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="File1" fileName="${filename}">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

记录日志的代码示例:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {
		Test test = new Test();
		test.method("hello", 10);		
	}
	
	public boolean method(String name, int age) {
		boolean result = true;
		logger.traceExit(result);
		return result;
	}

}

运行输出:

10:36:38.201 [main] TRACE com.thb.Test - Exit with(true)

用traceEntry()和traceExit()记录函数的进入和离开trace日志

如果函数没有参数、或者我们对参数不感兴趣,可以直接用traceEntry()记录函数的进入日志。
如果函数不返回任何结果、或者我们对返回结果不感兴趣,可以直接用traceExit()记录函数的离开日志。
maven工程中src/main/resources/log4j2.xml的配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="File1" fileName="${filename}">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

输出日志代码:

package com.thb;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {
	
	private static final Logger logger = LogManager.getLogger();

	public static void main(String[] args) {
		Test test = new Test();
		test.method();
	}
	
	public void method() {
		logger.traceEntry();
		logger.traceExit();		
	}
}

运行结果:

11:09:29.794 [main] TRACE com.thb.Test - Enter
11:09:29.799 [main] TRACE com.thb.Test - Exit

用catching(Throwable throwable)打印ERROR级别的异常

catching(Throwable throwable)用 ERROR级别打印异常。

示例:

package com.thb;

import java.io.IOException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {
    private static final Logger LOGGER = LogManager.getLogger();
    
    public static void main(String[] args) {
        final Demo demo = new Demo();
        demo.method();
    }

    public void method() {
        try {
            throw new IOException("illegal input type");
        } catch(IOException e) {
            LOGGER.catching(e);
            // do something
        } finally {
            // do something
        }
    }
}

运行输出:
在这里插入图片描述

用catching(Level level, Throwable throwable)打印指定级别的异常

catching(Level level, Throwable throwable)可以用指定级别打印异常。

示例:

package com.thb;

import java.io.IOException;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {
    private static final Logger LOGGER = LogManager.getLogger();
    
    public static void main(String[] args) {
        final Demo demo = new Demo();
        demo.method();
    }

    public void method() {
        try {
            throw new IOException("illegal input type");
        } catch(IOException e) {
            LOGGER.catching(Level.FATAL, e);
            // do something
        } finally {
            // do something
        }
    }
}

运行输出:
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用Log4j 2记录日志 的相关文章

随机推荐

  • git cherry-pick使用总结

    第一次在csdn发文章 还没找到节奏 请多多指教 这次给大家介绍一下Git中常用的cherry pick cherry pick的作用 将现有的某个提交应用到当前分支上 git cherry pick edit n m parent num
  • QComboBox类的使用,下拉列表框的触发:activated与currentIndexChanged的区别

    一 介绍 QcomboBox属于继承自QWidget 给用户提供一种呈现选项列表的方式 作用 使其占最小的控件 列举最多的选项供用户选择 二 触发条件 当前用户点击所选的具体列表项 两种触发方式 1 void currentIndexCha
  • Redis学习笔记五:redis主从复制

    通常使用redis时 如果redis存储的是一些非常重要的数据 那么只配置一台服务器的redis是有风险的 以为如果主服务器宕机 影响到正常业务之外 最终要的是数据的丢失 因此我们常常会配置多台redis做集群 除了防止主机宕机 我们还可以
  • 【AI视野·今日CV 计算机视觉论文速览 第166期】Mon, 28 Oct 2019

    AI视野 今日CS CV 计算机视觉论文速览 Mon 28 Oct 2019 Totally 47 papers 上期速览 更多精彩请移步主页 Interesting 联合显著性检测 提出了一种从单张图像中检测出具有相似语义属性的物体显著性
  • [转]一文看懂区块链架构设计 - 从概念到底层技术

    前言 区块链作为一种架构设计的实现 与基础语言或平台等差别较大 区块链是加密货币背后的技术 是当下与VR虚拟现实等比肩的热门技术之一 本身不是新技术 类似Ajax 可以说它是一种技术架构 所以我们从架构设计的角度谈谈区块链的技术实现 无论你
  • 广西人才网实习信息爬取与数据库存储实战

    广西人才网实习信息爬取与数据库存储实战 https www gxrc com 大家好 我是W 项目介绍 本项目为CrawlSpider结合MySQL MongoDB爬取求职网站信息的项目 目标是将网站指定分类下的招聘信息 包括 职位名称 公
  • Jupyter Notebook导入和删除虚拟环境

    在Jupyter Notebook中加载虚拟环境 比如现在我创建了一个虚拟环境名为pytorch 那么首先我先进入虚拟环境 activate pytorch Linux下需要使用source activate 然后运行 conda inst
  • Android init.rc整理

    AIL概述 init rc由AIL语言编写而成 可以参考system core init README md来学习AIL语法相关知识 不同Android版本关于AIL的说明存在一些细微差异 但基本语法和总的思路是不变的 往往我们可以先查看对
  • 2021哈工大机器翻译实验室经验贴(回忆版)

    哈工大机器翻译实验室 有两轮 一轮笔试 一轮面试 第一轮笔试题 根据本人的回忆 3道简答 5道编程题 60分钟 一 简答题 1 Windows下的软件Office 在Ubuntu环境下能否运行 说明理由 2 已知账户名和账户密码 说明登录到
  • 反射与线程间通讯

    反射 一 在运行状态中 对于任意一个类 都能够获取到这个类的所有属性和方法 对于任意一个对象 都能够调用它的任意一个方法和属性 包括私有的方法和属性 这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制 通俗点讲 通过
  • 设置linearlayout最大高度_数据中心:主要设备用房高度需求及建筑层高规划

    主要设备用房高度需求 数据中心主要设备用房为35KV开闭所 10KV开闭所 低压变配电房 动力 低压变配电房 IT UPS 柴油发电机组 冷冻机房 IT机房模块间等 35KV开闭所通常单独设置不在IT机房大楼内布置 本文不再讨论 各设备用房
  • Nginx服务优化

    配置nginx隐藏版本号 隐藏nginx版本号 避免安全漏洞泄漏 方法一 修改配置文件法 root www conf vim usr local nginx confnginx conf 17 http 18 include mime ty
  • 图解---散列表(hash表)的基本原理以及hash冲突(碰撞)--易懂版

    图解 散列表 hash表 的基本原理以及hash冲突 碰撞 易懂版 散列表为什么诞生 它用于做什么 先说说数组 数组的优点是查找比较快 但是添加和删除效率比较低 再说说链表 链表的优点是添加和删除效率比较快 相对于数组 但是遍历需要一个指针
  • 一种软件网络验证方式的实现 + 网络验证转本地验证的一种实现(附VC源码)...

    目前很多软件都是通过网络验证来实现的 一种比较流行的方式便是把服务器端 如验证网页 放在服务器上 软件为客户端 当软件注册或启动时通过网络与服务器端进行数据交换 重新实现验证的目的 个人觉得网络验证将是一种趋势 做得好的网络验证方式将是对软
  • Spring 源码 事件监听

    2019独角兽企业重金招聘Python工程师标准 gt gt gt spring 监听器 一 事件监听机制概述 二 事件监听机制结构 三 Spring监听机制架构 Spring的Application拥有发布事件并且注册事件监听器的能力 拥
  • python验证码识别MuggleOCR通用识别使用

    先来看看MuggleOCR简介 白嫖 这是一个为麻瓜设计的本地OCR模块 只需要简单几步操作即可拥有两大通用识别模块 让你在工作中畅通无阻 这套模型是基于 https github com kerlomz captcha trainer 训
  • JSP注释(4种)

    说到注释 相信大家肯定都不陌生 它是对程序代码的解释和说明 注释可以提高代码的可读性 让他人能够更加轻松地了解代码 从而提高团队合作开发的效率 在 JSP 中可以使用以下 4 种注释 HTML 注释 带有 JSP 表达式的注释 隐藏注释 脚
  • 登录和注册的基本实现,超简单!

    前序 相信有很多的人在刚刚做项目的实现 登录与注册功能的实现是基本的要求 要是刚刚开始写的小伙伴肯定会有很多的困惑 这里我介绍一下自己的写法 希望能帮到你 也希望能免费点个小 这里就以之前我写的一个为例 大家可以根据自己的规则来更改 一 登
  • python 短路法提高二叉堆插入效率

    在学习 problem solving with algorithms and data structure using python 中的二叉堆时 其插入数据方法是将这个数据放在列表的尾部 然后通过一次次与父节点进行比较 并且交换 实现顺
  • 用Log4j 2记录日志

    说明 maven工程中增加对Log4j 2的依赖 下面代码示例的maven工程中的pom xml文件中需要增加对Log4j 2的依赖