用Log4j 2记录日志

说明

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("面积为:" + Math.PI * Math.pow(r, 2));
}

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

在log4j 2中的做法为:

复制代码
// log4j 2中的做法,用替换参数,代码中不需要显式判断对应级别的日志是否打开
logger.info("面积为:{}", Math.PI * Math.pow(r, 2));

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

通过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");
	}

}

输出:

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

为了进行对比,首先给出log4j 1.x中的做法:

复制代码
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;
				
		// log4j 1.x中的做法,代码中要显式判断对应级别的日志是否打开
		if (logger.isInfoEnabled()) {
			logger.info("面积为:" + Math.PI * Math.pow(r, 2));
		}
	}

}

运行输出:

复制代码
08:41:01.549 [main] INFO  com.thb.Test - 面积为:12.566370614359172

再给出log4j 2中的做法:

复制代码
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;
		
		// log4j 2中的做法,使用替换参数,代码中不需要显式判断对应级别的日志是否打开
		logger.info("面积为:{}", Math.PI * Math.pow(r, 2));
	}

}

运行输出:

复制代码
08:43:14.741 [main] INFO  com.thb.Test - 面积为:12.566370614359172

通过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

两个类都定义了静态的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
相关推荐
启山智软6 分钟前
【中大企业选择源码部署商城系统】
java·spring·商城开发
我真的是大笨蛋9 分钟前
深度解析InnoDB如何保障Buffer与磁盘数据一致性
java·数据库·sql·mysql·性能优化
怪兽源码37 分钟前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
恒悦sunsite43 分钟前
Redis之配置只读账号
java·redis·bootstrap
梦里小白龙1 小时前
java 通过Minio上传文件
java·开发语言
人道领域1 小时前
javaWeb从入门到进阶(SpringBoot事务管理及AOP)
java·数据库·mysql
sheji52611 小时前
JSP基于信息安全的读书网站79f9s--程序+源码+数据库+调试部署+开发环境
java·开发语言·数据库·算法
毕设源码-邱学长1 小时前
【开题答辩全过程】以 基于Java Web的电子商务网站的用户行为分析与个性化推荐系统为例,包含答辩的问题和答案
java·开发语言
摇滚侠1 小时前
Java项目教程《尚庭公寓》java项目从开发到部署,技术储备,MybatisPlus、MybatisX
java·开发语言
€8112 小时前
Java入门级教程24——Vert.x的学习
java·开发语言·学习·thymeleaf·数据库操作·vert.x的路由处理机制·datadex实战