Apache Log4j 2是对Log4j的升级,它比其前身Log4j1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。
为什么要开发Log4j 2 ?
- Apache Log4j 2 中将日志的API接口和实现进行了分离.
log4j-api.jar
是一个日志门面,是定义的Log4j 2 API 日志接口log4j-core.jar
定义的Log4j 2 API 接口的实现API 分割后,由于
log4j-api.jar
是门面日志,也就意味着不仅可以和log4j-core.jar
一起使用,也可以用Logback
的日志实现logback-core.jar
一起使用。
示例组合如下:
- log4j-api.jar + log4j-core.jar
- log4j-api.jar + logback-core.jar
- Log4j API 比SLF4J门面日志更好更强大
- 1.
Log4j API
支持记录消息而不仅仅是字符串- 2.
Log4j API
支持lambda
表达式- 3.
Log4j API
提供了比SLF4J
更多的日志记录方法- 4.除了SLF4J支持的“参数化日志记录”格式之外,
Log4j API
还支持使用java.text.MessageFormat
语法以及printf-sytle
消息的事件。- 5.Log4j API提供了
LogManager.shutdown()
方法。 底层日志记录实现必须实现Terminable
接口才能使方法生效。- 6.完全支持其他构造,如
Markers
,log Levels
和ThreadContext
(aka MDC
)
Log4j 2
包含基于LMAX Disruptor
库的下一代异步记录器。 在多线程场景中,异步记录器的吞吐量比Log4j 1.x
和Logback
高18倍,延迟低。 有关详细信息,请参阅异步日志记录性能 否则,Log4j 2
明显优于Log4j 1.x
,Logback
和java.util.logging
,尤其是在多线程应用程序中。
虽然Log4j 2 API将提供最佳性能,但Log4j 2支持Log4j 1.2,SLF4J,Commons
Logging和java.util.logging(JUL)API。
log4j 2.4及更高版本需要Java 7,版本2.0-alpha1到2.3需要Java 6.某些功能需要可选的依赖项;这些功能的文档指定了依赖项。
- Log4j 2.12.1 生产环境可用
- Log4j 2 不兼容Log4j 1.x,但是有一个适配器可以让继续使用Log4j 1.x API
- 修复了Log4j 2.6 的一些Bug,详情可看Bug修复列表
- Log4j 2.12.1与以前的版本保持二进制兼容性。
组件 | 描述 |
---|---|
log4j-api | 应用程序应使用和编码的接口 |
log4j-core | 标准实现,也称为Log4j 2 Core,包含Appender,Filters等。 |
log4j-iostreams | 用于处理旧API的额外类,这些API期望来自java.io 的类用于记录 |
log4j-taglib | 使用Log4j 2在JavaServer Pages™中实现无Java登录的标记库。 |
JSP Tag Library (TLD Doc) | Log4j 2 JSP标记库的特殊类Javadoc标记库文档。 |
组件依赖库
组件 | 描述 |
---|---|
Commons Logging Bridge | 允许针对Apache Commons Logging API编写的应用程序使用Log4j 2进行日志记录的桥接器。Common Logging API+ Log4j 2 Core |
SLF4J Binding | 允许针对SLF4J API编写的应用程序使用Log4j 2进行日志记录的桥接器 SLF4j+Log4j2 Core |
Java Util Logging Adapter | 允许针对java.util.logging API编写的应用程序使用Log4j 2进行日志记录的桥梁。java.util.logging API+Log4j 2 Core |
Log4j 1.2 API Bridge | 允许针对Log4j 1.2.x API编写的应用程序使用Log4j 2进行日志记录的桥梁。Log4j 1.2.x API—>Log4j 2 |
Log4j 2 to SLF4J Adapter | 允许针对Log4j 2 API编写的应用程序使用SLF4J进行日志记录的适配器。 Log4j2 —>SLF4j |
Log4j JMX GUI | 基于Java Swing的客户端,用于远程查看状态记录器和编辑Log4j配置。 |
Log4j JPA | Apache Log4j Java Persistence API Appender |
- Log4j 2在API和实现(core)中分解,其中API提供应用程序应编写的接口。
- 严格来说,Log4j Core仅在运行时需要,而不是在编译时需要。
添加依赖到 pom.xml
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-apiartifactId>
<version>2.12.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.12.1version>
dependency>
如果项目中有多个log4j版本依赖为了使Log4j模块版本彼此保持同步,提供了BOM pom.xml文件以方便使用。
添加依赖如下
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-bomartifactId>
<version>2.12.1version>
<scope>importscope>
<type>pomtype>
dependency>
dependencies>
dependencyManagement>
- The Apache Log4j 1.x Compatibility API
- 如果现有组件使用Log4j 1.x并且您希望将此日志记录路由到Log4j 2,则删除所有log4j 1.x依赖项并添加以下内容
代码中有log4j 1. x 的代码,升级了Log4j2 依赖情况下
log4j 1.x 升级到log4j 2.x 添加兼容包依赖如下:
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-1.2-apiartifactId>
<version>2.12.1version>
dependency>
注意: 需要和4.1.1 中添加的依赖组合使用。
The Apache Log4j binding between Log4j 2 API and SLF4J
如果有使用log4j-api 作为日志门面调用代码,那么都转成SLF4J API
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-to-slf4jartifactId>
<version>2.12.1version>
dependency>
- The Apache Log4j SLF4J API binding to Log4j 2 Core
- 如果现有组件使用SLF4J并且您希望将此日志记录路由到Log4j 2,则添加以下内容但不删除任何SLF4J依赖项。
代码里将使用SLF4J API作为日志门面,Log4j 2 作为日志实现
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-slf4j-implartifactId>
<version>2.12.1version>
dependency>
Common Logging 作为门面日志,日志实现类使用Log4j 2 Core
The Apache Log4j Commons Logging Adapter
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-jclartifactId>
<version>2.12.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-webartifactId>
<version>2.12.1version>
dependency>
Log4j日志标记库创建了在不使用Java脚本的情况下在JSP中插入日志语句的功能。 它使用标准的Log4j 2 API根据您的Log4j配置记录消息。
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-taglibartifactId>
<version>2.12.1version>
dependency>
pom.xml添加如下
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
<version>2.1.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-1.2-apiartifactId>
<version>2.12.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-jclartifactId>
<version>2.12.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-webartifactId>
<version>2.12.1version>
dependency>
<context-param>
<param-name>log4jConfigurationparam-name>
<param-value>classpath:/log4j2.xmlparam-value>
context-param>
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListenerlistener-class>
listener>
<filter>
<filter-name>log4jServletFilterfilter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilterfilter-class>
filter>
<filter-mapping>
<filter-name>log4jServletFilterfilter-name>
<url-pattern>/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
<dispatcher>INCLUDEdispatcher>
<dispatcher>ERRORdispatcher>
filter-mapping>
我们只需要如下添加如下依赖即可:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-1.2-apiartifactId>
<version>2.12.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-jclartifactId>
<version>2.12.1version>
dependency>
传递依赖详情如下:
<dependency> <groupId>org.apache.logging.log4jgroupId> <artifactId>log4j-slf4j-implartifactId> <version>2.11.2version> <scope>compilescope> dependency> <dependency> <groupId>org.apache.logging.log4jgroupId> <artifactId>log4j-coreartifactId> <version>2.11.2version> <scope>compilescope> dependency> <dependency> <groupId>org.apache.logging.log4jgroupId> <artifactId>log4j-julartifactId> <version>2.11.2version> <scope>compilescope> dependency> <dependency> <groupId>org.slf4jgroupId> <artifactId>jul-to-slf4jartifactId> <version>1.7.28version> <scope>compilescope> dependency>
注意事项
切记:
- 1.查看系统属性log4j.configurationFile是否配置了,如果配置了使用
ConfigurationFactory
- 2.如果没有配置,那么查找log4j2-test.properties
- 3.如果也没找到,那么检查是否有log4j2-test.yaml或者log4j2-test.yml
- 4.如果也没找到,那么检查是否有log4j2-test.json或者log4j2-test.jsn
- 5.如果也没找到,那么检查是否有log4j2-test.xml
- 6.如果也没找到,那么检查是否有log4j2.properties
- 7.如果也没找到,那么检查是否有log4j2.yaml或log4j2.yml
- 8.如果也没找到,那么检查是否有log4j2.json或log4j2.jsn
- 9.如果也没找到,那么检查是否有log4j2.xml
- 10.如果也没知道,那么使用默认的DefaultConfiguration,只输出到控制台
log4j2.xml配置如下:
<configuration name="log4j2Config" monitorInterval="30" status="WARN" strict="false" >
<Properties>
<property name="appName" value="myApp"/>
<Property name="baseDir" value="/opt/appLogs/${appName}/log">Property>
<Property name="logName" value="${baseDir}/${appName}.log">Property>
<Property name="logArchive" value="${baseDir}/$${date:yyyy-MM-dd}/${appName}-archive-%d{yyyy-MM-dd}-%i.log.gz"/>
Properties>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT" ignoreExceptions="false">
<PatternLayout charset="GB18030" pattern="%d{yyyy/MM/dd HH:mm:ss,SSS} %-5level %l %n%msg%n"/>
Console>
<RollingFile name="rollingFile"
append="true"
bufferedIO="true"
bufferSize="8192"
createOnDemand="false"
immediateFlush="true"
fileName="${logName}"
filePattern="${logArchive}"
ignoreExceptions="true"
>
<PatternLayout>
<charset>UTF-8charset>
<Pattern>%d{yyyy/MM/dd HH:mm:ss,SSS} %-5level %l %n%msg%nPattern>
PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="1GB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" maxRandomDelay="0"/>
Policies>
<DefaultRolloverStrategy max="20" min="1">
<Delete basePath="${baseDir}" maxDepth="8">
<IfFileName glob="*/app-*.log.gz">
<IfLastModified age="30d">
<IfAny>
<IfAccumulatedFileSize exceeds="10GB" />
<IfAccumulatedFileCount exceeds="100" />
IfAny>
IfLastModified>
IfFileName>
Delete>
DefaultRolloverStrategy>
RollingFile>
Appenders>
<Loggers>
<Logger name="com.xingyun" level="DEBUG" additivity="true"/>
<root level="ERROR">
<AppenderRef ref="STDOUT" />
<AppenderRef ref="rollingFile" />
root>
Loggers>
configuration>
关于日志格式可参考 log4j2配置详解(节点和输出格式)
- 自定义日志级别
- Log4J 2 支持自定义日志级别,可以使用Level.forName() 定义不同的日志级别.
Log4J内置标准日志级别
Standard Level | intLevel |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | Integer.MAX_VALUE |
关于这个,网上大多数都是错误的,思路偏了 像下面这样做只是代码里动态修改了log4j2.xml中的配置,压根不正确。
比如这篇:Log4j2代码方式配置实现线程级动态控制
loggerConfiguration.start();
appender.start();
主要原因在于这里有一个很大的误区,当我们自定义线程日志的时候很容易不小心引错了包。
我们可以看到有两个包,这俩有啥区别呢?
org.apache.logging.log4j.Logger
和org.apache.logging.log4j.core.Logger
log4j1.x 升级到log4j 2.x之后发生了一个改变,那就是实现和接口分离。
org.apache.logging.log4j.Logger
是log4j2 日志门面,只是日志接口类org.apache.logging.log4j.core.Logger
是Log4j2 日志接口实现类
我们如果想要自定义一个线程日志类,注意事项:
org.apache.logging.log4j.core.Logger
这个类才有logger.addAppender(appender);
方法, logger.setAdditive(false);
这样设置可以保持独立和log4j.xml 配置互相不冲突工具类源码封装如下:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
import org.apache.logging.log4j.core.appender.rolling.OnStartupTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.File;
import java.nio.charset.Charset;
/**
* @author 星云
* @功能 线程日志工具类 使用Log4j2自定义线程日志类
* @date 9/17/2019 7:09 AM
*/
public class ThreadCustomLogger {
/**
* 获取日志上下文
*/
private static LoggerContext loggerContext= (LoggerContext) LogManager.getContext(false);
/**
* 获取日志配置文件实例
*/
private static Configuration loggerConfiguration=loggerContext.getConfiguration();
/**
* 滚动文件日志记录器
*/
private static RollingFileAppender.Builder rollingFileAppenderBuilder;
/**
* 日志输出文件夹
*/
private static volatile String lOG_BASE_PATH="/opt/appLogs/myApp/log/customThread";
/**
* 日志输出格式
*/
private static volatile String OUT_PUT_FORMAT_VALUE="%d{yyyy/MM/dd HH:mm:ss,SSS} %-5level %l %n%msg%n";
/**
* 滚动策略
*/
private static volatile String FILE_PATTERN_VALUE=lOG_BASE_PATH+File.separator+"customThread-archive-%d{yyyy-MM-dd}-%i.log.gz";
/**
* @param threadFolderName
* @param loggerName
* @return
*/
public static Logger getLogger(String threadFolderName, String loggerName) {
//是接口
Appender appender;
//日志分割文件夹路径
String threadLoggerFolder=lOG_BASE_PATH+ File.separator+threadFolderName;
//判断是否需要重复初始化共同配置
if(null==rollingFileAppenderBuilder){
//初始化相同的公共配置
rollingFileAppenderBuilder=initCustomRollingFileAppender();
}
//带文件大小和日期时间滚动策略的日志记录器
appender=rollingFileAppenderBuilder.withFileName(threadLoggerFolder+File.separator+loggerName+".log").build();
appender.start();
//返回实例
Logger logger =(Logger)LogManager.getLogger(loggerName);
//是否继承自log4j2.xml中Root节点的配置
logger.setAdditive(false);
//设置拦截的日志级别
logger.setLevel(Level.DEBUG);
//设置追加器
logger.addAppender(appender);
return logger;
}
/**
* 初始化
* @return
*/
private static RollingFileAppender.Builder initCustomRollingFileAppender(){
//日志打印输出布局
Layout layout= PatternLayout.newBuilder()
//设置字符集
.withCharset(Charset.forName("UTF-8"))
//加载配置
.withConfiguration(loggerConfiguration)
//输出布局
.withPattern(OUT_PUT_FORMAT_VALUE).build();
//系统启动出发滚动策略 当系统重新加载启动的时候触发该策略
/*OnStartupTriggeringPolicy onStartupTriggeringPolicy=OnStartupTriggeringPolicy.createPolicy(1);*/
//根据大小触发滚动策略 当文件大小增加到1GB时候触发该策略
SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy= SizeBasedTriggeringPolicy.createPolicy("1GB");
//根据日期和时间触发滚动策略
TimeBasedTriggeringPolicy timeBasedTriggeringPolicy= TimeBasedTriggeringPolicy.newBuilder()
//如果是yyyy-MM-dd 那么1表示一天 如果是yyyy-MM 那么1表示一个月
.withInterval(1)
.withModulate(true)
.withMaxRandomDelay(0)
.build();
//设置默认值
DefaultRolloverStrategy defaultRolloverStrategy= DefaultRolloverStrategy.newBuilder()
.withMin("1")
.withMax("20")
.build();
//日志滚动追加器
RollingFileAppender.Builder rollingFileAppenderBuilder = RollingFileAppender.newBuilder()
.setName("customRollingFileAppenderBuilder")
//日志是否追加模式
.withAppend(true)
//是否使用缓冲区
.withBufferedIo(true)
//缓冲区大小
.withBufferSize(8192)
.withCreateOnDemand(false)
//是否立即刷新
.withImmediateFlush(true)
//是否忽略异常继续写入
.setIgnoreExceptions(true)
//默认的滚动策略
.withStrategy(defaultRolloverStrategy)
//启动时候出发的策略
/*.withPolicy(onStartupTriggeringPolicy)*/
//文件大小达到条件触发策略
.withPolicy(sizeBasedTriggeringPolicy)
//日期和时间达到条件触发策略
.withPolicy(timeBasedTriggeringPolicy)
//日志输出格式
.setLayout(layout)
//滚动输出归档压缩包文件命名格式
.withFilePattern(FILE_PATTERN_VALUE);
return rollingFileAppenderBuilder;
}
}
在代码中调用方式如下:
import com.xingyun.springbootwithlog4j2sample.util.ThreadCustomLogger;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 星云
* @功能
* @date 9/15/2019 3:45 PM
*/
@Slf4j
@RestController
public class LogController {
private static final org.slf4j.Logger LOGGER_SLF4J= LoggerFactory.getLogger(LogController.class);
private static final Logger LOGGER_LOG4J2= LogManager.getLogger(LogController.class);
private static Logger loggerCustom= null;
@GetMapping(value = "/log.do")
public String log(){
//第一种日志使用SLF4J 日志门面调用
LOGGER_SLF4J.debug("this is debug message with slf4j");
LOGGER_SLF4J.info("this is info message with slf4j");
LOGGER_SLF4J.warn("this is warn message with slf4j");
LOGGER_SLF4J.error("this is error message with slf4j");
//第二种方式使用log4j API 日志门面调用
LOGGER_LOG4J2.debug("this is debug message with log4j2");
LOGGER_LOG4J2.info("this is info message with log4j2");
LOGGER_LOG4J2.warn("this is warn message with log4j2");
LOGGER_LOG4J2.error("this is error message with log4j2");
//第三种方式 配合lombok @Slf4j注解使用
log.debug("this is debug message with lombok");
log.info("this is info message with lombok");
log.warn("this is warn message with debug");
log.error("this is error message with lombok");
//第四种方式自定义线程日志
if(null==loggerCustom){
loggerCustom= ThreadCustomLogger.getLogger("myThread",LogController.class.getSimpleName());
loggerCustom.debug("this is custom debug message");
loggerCustom.info("this is custom info message");
loggerCustom.warn("this is custom warn message");
loggerCustom.error("this is custom error message");
}
return "log test finished,please check console message";
}
}
- spring-boot-with-log4j2-sample
- Apache Log4j 2 官网
- Log4j2代码方式配置实现线程级动态控制
- log4j2配置详解(节点和输出格式)
本篇完,喜欢我的博文,欢迎点赞,关注 ~