最近在使用Log4j2的时候,碰到了一些问题:无论是使用Configurator.initialize还是LogManager.getContext().reconfigure()都无法完全更新log4j2的配置。

在查看了Log4j2的源代码过后,笔者找到了解决方案。

tl;dr.

1
2
3
4
5
6
7
8
9
Configurator.initialize(
    ClassLoader.getSystemClassLoader(),
    configuration
);

Configurator.initialize(
    configuration
);
// 缺一不可

如果观察LogManager.getLogger(Class<?>),会发现其调用的是getContext(cls.getClassLoader(), false).getLogger(cls);

1
2
3
4
5
public static Logger getLogger(final Class<?> clazz) {
    final Class<?> cls = callerClass(clazz);
    // note this:     ↓
    return getContext(cls.getClassLoader(), false).getLogger(cls);
}

对比之下的LogManager.getLogger(String)

1
2
3
4
public static Logger getLogger(final String name) {
        // note this:         ↓
        return name != null ? getContext(false).getLogger(name) : getLogger(StackLocatorUtil.getCallerClass(2));
    }

如果使用LogManager.getLogger(String)这个函数,那么loader将会是null(对比之下,LogManager.getLogger(Class<?>)使用cls.getClassLoader()获取了loader)。可能会有一条可疑的警告信息: WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance. 并且会获取到与带有loader参数不同的LoggerContext

getLogger(Class<?>)虽然一直作为log4j2的典型用法,仍然有一些第三方库使用getLogger(String)。比如netty。

所需需要对带classLoader参数和不带此参数的LoggerContext都进行更新。代码见上。