关于升级 jar 包等前提要求
删掉原先的 log4j-1.XX 等 jar 包
从 1.XX 升级到 2.XX 平稳升级需要的 jar 包,其中包括用 sl4j-1.7.25 (1.7.21 暂时也不需要升级) 的版本包,具体可以看自己的工程配置是否需要
slf4j-log4j-升级到 2.8.2.zip
新建 log4j2.xml 配置文件
对于 tomcat 项目的版本要求:(以 7.0.43 为分界线),目前 jdk1.8 可以在 tomcat 7.0.32 ,7.0.94 正常启动代码
三种配置方式:
1、关于 log4j2.xml 的默认存放位置(WEB 项目可以使用)
按照官方推荐默认放到 resource 目录下面,名称必须叫 log4j2.xml ,其余文件名会读取失败,无需配置其它文件,如下图:
2、关于使用自定义路径配置文件(推荐 jar 包读取外部配置文件,比如 Eserver ItmsService 等模块)
2.1 在代码的 web.xml 文件里,增加如下配置,其中 param-value 为配置文件的绝对路径,包含 file:// 开头文件
org.apache.logging.log4j.web.Log4jServletContextListener
日志配置文件的路径
log4jConfiguration
file:///export/home/rms/EServer4WS/WEB-INF/log4j2.xml
2.2 修改 web.xml 文件的开头关于 servlet 的版本,因为 tomcat 7.0 之后,lib 包里面的 servlet-api.jar 包的版本是 3.0,所以必须修改 web.xml 的版本声明,否则会无法初始化 log4j2。
(将图一三个标红的地方应该改为 3.0),结果如图二
图一:
图二:
3、自定义路径并且实现相关 jar 包的预留接口(不需要改动代码和配置,目前只需要替换相关的 jar 包,把新建的 log4j2.xml 改名为 log4j.xml ,并且放在原先同级目录即可)
针对目前代码现状,为了不改动配置和代码,实现了 log4j-1.2-api-2.8.2.jar 预留的相关接口,具体哪个接口需要查看原本工程初始化 log4j 的类用的是哪个接口,比如 org.apache.log4j.xml.DOMConfigurator 类里面的相关接口,如下所示:
private static Logger logger = LoggerFactory.getLogger(DOMConfigurator.class);
// 如下相关通用实现log4j2初始化代码
public static void configureAndWatch(String configFilename)
{
LoggerContext logContext = (LoggerContext) LogManager.getContext(false);
File conFile = new File(configFilename);
logContext.setConfigLocation(conFile.toURI());
logContext.reconfigure();
logger.debug("init log4j2 ok");
}
该方式只需要按照第一步删除旧的 jar 包,上传新的 jar 包,特别是 log4j-1.2-api-2.8.2.jar 包,即可无缝完成升级
关于 log4j2.xml 配置文件中的注意事项
1、关于 log4j2 日志等级组合过滤器的问题
表示只输出 info <= level < warn 的 level 日志,也就是只打印 info 级别
2、关于 DefaultRolloverStrategy 默认删除以及个数的问题
注意%d{MM-dd-yyyy}要用年月日格式,不能加上时分秒,并且最后要有%i,这样log4j2才能判断出哪天一共产生几个文件
示例:
3、完整的 log4j2.xml 配置
衍生多线程配置问题
不同的线程输出日志到不同的文件中
不同的线程输出日志到不同的文件中有关 Log4j2 的内容很多,在此不一一列出,这里只介绍一种常用方法。如果在开发中遇到任何问题,推荐去官方文档中寻找解决方案。
实现 StrLookup
修改 log4j2.xml 配置文件如下,主要是添加 Routes 标签:
实现 StrLookup 中的 lookup 方法,代码如下:
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;
(name = "thread", category = StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {
public String lookup(String s) {
return Thread.currentThread().getName();
}
public String lookup(LogEvent logEvent, String s) {
return logEvent.getThreadName() == null ? Thread.currentThread().getName()
: logEvent.getThreadName();
}
}
测试方法如下:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestLog2 {
private static final Logger logger = LogManager.getLogger(TestLog2.class);
public static void main(String[] args) {
new Thread(() -> {
logger.info("info");
logger.debug("debug");
logger.error("error");
}).start();
new Thread(() -> {
logger.info("info");
logger.debug("debug");
logger.error("error");
}).start();
}
}
不同线程不同级别的日志输出到不同的文件中
要实现该功能,还要从 RoutingAppender 身上做文章。RoutingAppender 主要用来评估 LogEvents,然后将它们路由到下级 Appender。目标 Appender 可以是先前配置的并且可以由其名称引用的 Appender,或者可以根据需要动态地创建 Appender。RoutingAppender 应该在其引用的任何 Appenders 之后配置,以确保它可以正确关闭。
RoutingAppender 中的 name 属性用来指定该 Appender 的名字,它可以包含多个 Routes 子节点,用来标识选择 Appender 的条件,而 Routes 只有一个属性 “pattern”,该 pattern 用来评估所有注册的 Lookups,并且其结果用于选择路由。在 Routes 下可以有多个 Route,每个 Route 都必须配置一个 key,如果这个 key 匹配 “pattern” 的评估结果,那么这个 Route 就被选中。同时每个 Route 都必须引用一个 Appender,如果这个 Route 包含一个 ref 属性,那么这个 Route 将引用一个在配置中定义的 Appender,如果这个 Route 包含一个 Appender 的定义,那么这个 Appender 将会根据 RoutingAppender 的上下文创建并被重用。
废话说多了,直接上配置才简洁明了。log4j2.xml 配置如下:
logs
testLog
Asynchronous 全局配置
根据官方的性能测试我们知道,Loggers all async
的性能最高,但是我们在上边使用的是 Sync
模式(方法一,因为 Appender 默认是 synchronous 的)或 Async Appender
模式(方法二),那么如何更进一步让所有的 Loggers 都是 Asynchronous 的,让我们的配置更完美呢?想要使用 Loggers all async
只需要做两步操作。
因为 Loggers all async
是基于 LMAX Disruptor 实现的,所以我们首先需要添加这个依赖
com.lmax
disruptor
3.4.2
其次是设置系统属性
log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
可以在前面提到的 ThreadLookup 类中,添加静态代码块
static {
System.setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
}
在 src/main/resources
目录下添加 log4j2.component.properties 配置文件,其内容为
log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合使用 Synchronous 和 Asynchronous Loggers
依旧需要依赖 com.lmax:disruptor
,但不需要设置系统属性 log4j2.contextSelector
,在配置中可以混合使用同步和异步的 loggers,使用
或者
去指定需要异步的 loggers,
元素还可以包含
和
用于同步的 loggers。
一个混合了同步和异步的 Loggers 配置如下:
%d %p %class{1.} [%t] %location %m %ex%n
在上面示例的配置中,root logger 就是同步的,但是 com.foo.Bar 的 logger 就是异步的。
使用 Log4j 日志的注意事项
在使用异步日志的时候需要注意一些事项,如下:
- 不要同时使用 AsyncAppender 和 AsyncLogger,也就是在配置中不要在配置 Appender 的时候,使用 Async 标识的同时,又配置 AsyncLogger,这不会报错,但是对于性能提升没有任何好处。
- 不要在开启了全局同步的情况下,仍然使用 AsyncAppender 和 AsyncLogger。这和上一条是同一个意思,也就是说,如果使用异步日志,AsyncAppender、AsyncLogger 和全局日志,不要同时出现。
- 如果不是十分必须,不管是同步异步,都设置 immediateFlush 为 false,这会对性能提升有很大帮助。
4、如果不是确实需要,不要打印 location 信息,比如 HTML 的 location,或者 pattern 模式里的%C or $class, %F or %file, %l or %location, %L or %line, %M or %method, 等,因为 Log4j 需要在打印日志的时候做一次栈的快照才能获取这些信息,这对于性能来说是个极大的损耗。