最近在做项目的过程中,无意中发现,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的就不用了,具体怎么配,这里不写了,有兴趣的可以自己查看一下。
以上便是我的一点简单的使用,自己留作记录,也希望能帮助到需要的朋友。