最近在做项目的过程中,无意中发现,log4j1.x居然存在多线程下出现死锁的问题,这可是很严重的问题,果断改啊。Apache不愧是个有良心的组织,出了log4j2.x版本,不紧解决了死锁问题,还支持异步写日志等。当然,本次只说明一下log4j2.x的简单使用,复杂的使用以后用到再说。
下面直接上代码:
一、引入jar包
在纯Java的项目中,需要引入的jar包
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
在web项目中,需要引入的jar包
<!-- slf4j核心包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.13</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.13</version>
<scope>runtime</scope>
</dependency>
<!--核心log4j2jar包 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<!--用于与slf4j保持桥接 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.4.1</version>
</dependency>
<!--web工程需要包含log4j-web,非web工程不需要 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.4.1</version>
<scope>runtime</scope>
</dependency>
可以看出来,log4j2.x与log4j1.x相比,增加了很多依赖包
二、Java代码中的使用:
方法一:直接使用log4j的api
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static Logger logger = LogManager.getLogger(CloudResourceController.class);
方法二:对log4j进行一次封装
1、封装接口类Logger:
/**
* 自定义logger接口,避免下次修改log版本后还要修改所有的Java文件
*
* @ClassName: Logger
* @author chenting
* @date 2017年7月7日
*
*/
public interface Logger {
/**
* Log a message at the DEBUG level.
*
* @param msg
* the message string to be logged
*/
public void debug(String msg);
/**
* Log a message at the INFO level.
*
* @param msg
* the message string to be logged
*/
public void info(String msg);
/**
* Log a message at the WARN level.
*
* @param msg
* the message string to be logged
*/
public void warn(String msg);
/**
* Log a message at the ERROR level.
*
* @param msg
* the message string to be logged
*/
public void error(String msg);
/**
* Log an exception (throwable) at the ERROR level with an accompanying
* message.
*
* @param msg
* the message accompanying the exception
* @param t
* the exception (throwable) to log
*/
public void error(Object msg, Throwable t);
}
2、接口实现类:
import org.apache.logging.log4j.LogManager;
/**
* Logger实现类,避免下次修改log版本后还要修改所有的Java文件
*
* @ClassName: LoggerFactory
* @author chenting
* @date 2017年7月7日
*
*/
public class LoggerFactory implements Logger{
private org.apache.logging.log4j.Logger logger;
private LoggerFactory(Class<?> clazz){
this.logger = LogManager.getLogger(clazz);
}
private LoggerFactory(String className){
this.logger = LogManager.getLogger(className);
}
public synchronized static Logger getLogger(Class<?> clazz) {
return new LoggerFactory(clazz);
}
public synchronized static Logger getLogger(String className) {
return new LoggerFactory(className);
}
/**
* Log a message at the DEBUG level.
*
* @param msg
* the message string to be logged
*/
public void debug(String msg) {
this.logger.debug(msg);
}
/**
* Log a message at the INFO level.
*
* @param msg
* the message string to be logged
*/
public void info(String msg) {
this.logger.info(msg);
}
/**
* Log a message at the WARN level.
*
* @param msg
* the message string to be logged
*/
public void warn(String msg) {
this.logger.warn(msg);
}
/**
* Log a message at the ERROR level.
*
* @param msg
* the message string to be logged
*/
public void error(String msg) {
this.logger.error(msg);
}
/**
* Log an exception (throwable) at the ERROR level with an accompanying
* message.
*
* @param msg
* the message accompanying the exception
* @param t
* the exception (throwable) to log
*/
public void error(Object msg, Throwable t) {
this.logger.error(msg, t);
}
}
3、具体使用:
private static Logger logger = LoggerFactory.getLogger(MainController.class);
按照上面三个步骤来实现即可
这样做的一个好处是,下次再想改其他的日志的话,只需要更新一下Logger接口的实现类即可,不用再像方法一那样,一个个的修改Java文件。有个坏处就是,没法具体定位到日志具体是在哪个Java文件中输出的,只能看到日志最终是经过LoggerFactory的接口输出的,这个希望有看到的朋友给出出主意。
三、log4j2的配置文件
与log4j1.x不同的是,log4j2.x已经不支持properties类型的配置文件,只支持xml,json等的日志,针对我个人来讲,配置起来2.x比1.x麻烦,或许是对1.x产生依赖了把,废话不多说,上代码:
这里我使用的是xml格式的,文件名log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出, 这一行我设置为OFF,表示不记录log4j2本身的日志 monitorInterval: Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。 --> <Configuration status="off" monitorInterval="1800"> <Properties> <!-- 日志基础路径 --> <Property name="BASEPATH">${sys:catalina.base}/logs</Property> <!-- 日志文件名称 --> <Property name="LOG_FILENAME">cms.log</Property> <!-- 日志保存天数 --> <Property name="LOG_SAVED_DATE">10d</Property> <!-- 日志输出格式 --> <Property name="LOG_PATTERN_LAYOUT">%d{yyyy-MM-dd HH:mm:ss.SSS} [ %level ] [ %thread ] %class{36} - %M %L - %msg%xEx%n</Property> </Properties> <Appenders> <!--这个输出控制台的配置 --> <Console name="Console" target="SYSTEM_OUT"> <!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY" /> <!-- 输出日志的格式 --> <PatternLayout pattern="${LOG_PATTERN_LAYOUT}" /> </Console> <!-- 按天备份一个日志 --> <!-- fileName为生成的文件名,也可以采用相对路径模式,filePattern为时间到达后产生新日志,旧日志的文件名 --> <RollingFile name="RollingFile" fileName="${BASEPATH}/${LOG_FILENAME}" filePattern="${BASEPATH}/${LOG_FILENAME}_%d{yyyy-MM-dd}.log"> <!-- 日志输出格式 --> <PatternLayout pattern="${LOG_PATTERN_LAYOUT}" /> <Policies> <!-- 每1天更新一次,此处查阅网上和官方示例中,都是以小时出现,我测试是以天为单位。(官方文档中说明按item类型是否是小时,但没找到在哪里设置item类型)另有其他各类型策略,请参阅官方文档 --> <!-- TimeBasedTriggeringPolicy需要和filePattern配套使用,由于filePattern配置的时间最小粒度是dd天,所以表示每一天新建一个文件保存日志。 SizeBasedTriggeringPolicy表示当文件大小大于指定size时,生成新的文件保存日志。 --> <TimeBasedTriggeringPolicy modulate="true" interval="1" /> </Policies> <!-- 最多备份10天以内的日志,此处为策略限制,Delete中可以按自己需要用正则表达式编写 --> <!-- DefaultRolloverStrategy字段中加入max="30"经测试是配合SizeBasedTriggeringPolicy限制%i的存在数量,并没有发现是网上流传的是最多保存多少个文件的限制,也或许是我写的有问题 --> <DefaultRolloverStrategy> <!-- log4j2.5版本之后才支持 --> <Delete basePath="${BASEPATH}" maxDepth="1"> <IfFileName glob="${LOG_FILENAME}_*.log" /> <!-- 日志保存天数 --> <IfLastModified age="${LOG_SAVED_DATE}" /> </Delete> </DefaultRolloverStrategy> </RollingFile> </Appenders> <Loggers> <!-- 配置日志的根节点 --> <!-- 然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> <root level="debug"><!-- level设置为info后,mybatis就没有SQL日志输出了,暂时未找到原因 --> <appender-ref ref="Console" /> <appender-ref ref="RollingFile" /> </root> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.mybatis" level="INFO"></logger> <logger name="org.apache.ibatis.io" level="INFO"></logger> <logger name="org.springframework" level="INFO"></logger> <logger name="druid.sql.Connection" level="INFO"></logger> <logger name="druid.sql.DataSource" level="INFO"></logger> <logger name="druid.sql.Statement" level="INFO"></logger> <logger name="druid.sql.ResultSet" level="INFO"></logger> </Loggers> </Configuration>
针对Loggers的配置,还可以这么配:
<Loggers> <!-- 配置日志的根节点 --> <!-- 然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> <root level="INFO"><!-- level设置为info后,mybatis就没有SQL日志输出了,暂时未找到原因 --> <appender-ref ref="Console" /> </root> <!-- 普通日志 --> <logger name="com.visionvera" level="INFO"> <appender-ref ref="DnsRollingFile" /> </logger> <!-- 心跳日志 --> <logger name="heart" level="INFO"> <appender-ref ref="HeartRollingFile" /> </logger> <!-- 屏蔽druid的日志输出 --> <logger name="com.alibaba.druid" level="error"></logger> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.mybatis" level="INFO"></logger> <logger name="org.apache.ibatis.io" level="INFO"></logger> <logger name="org.springframework" level="INFO"></logger> <logger name="druid.sql.Connection" level="INFO"></logger> <logger name="druid.sql.DataSource" level="INFO"></logger> <logger name="druid.sql.Statement" level="INFO"></logger> <logger name="druid.sql.ResultSet" level="INFO"></logger> </Loggers>
这样配的目的是,想让不同业务的日志输出到不同文件中,其中name=“heart”的日志,在代码中需要这么写:
private static Logger hearLogger = LoggerFactory.getLogger("heart");
需要注意的是,log4j2.x版本在servlet2.x版本下,需要在web.xml里面进行配置,servlet3.x的就不用了,具体怎么配,这里不写了,有兴趣的可以自己查看一下。
以上便是我的一点简单的使用,自己留作记录,也希望能帮助到需要的朋友。