log4j⽇志不输出的问题
今天服务器上报错,想先去看⼀下⽇志进⾏排查,结果发现⽇志很久都没有输出过了。从上午排查到下午,刚刚解决,因此记录⼀下,但现在也只是知其然,并不知其所以然,所以如果⼤家有什么想法请在下⽅评论。
先说⼀下环境,服务器是linux,项⽬是运⾏在tomcat下的Spring项⽬,⽇志⽤的是log4j。
⾸先,从10⽉13号开始便没有新的⽇志⽂件了。假设⽇志名为(如果你设置了DailyRollingFileAppender,那么你当天的⽇志⽂件就是),先备份该⽂件到其他⽬录下,然后删除该⽂件,重新启动tomcat。这是为了确认你的log4j配置是否有问题,因为这是最容易出错的地⽅。很遗憾,我不是这⾥出的问题,因为项⽬重启后,⽇志⽂件⼜重新⽣成了,但很奇怪的是,⽇志⽂件是空的,其⼤⼩为0.
感觉⾃⼰碰上了很神奇的问题,因此我在⾃⼰的本地进⾏调试,启动项⽬后发现,正常的项⽬启动⽇志是有的:
15:13:48:0253  INFO [RMI TCP Connection(3)-127.0.0.1] -Root WebApplicationContext: initialization completed in 18479 ms
但我⾃⼰的⼀些⽇志输出是不显⽰的,⽐如:
private static final Logger log = Logger(MyDomain.class);
log.info("show info log");
show info log这句话就不打印,现在证明,我的⽇志配置没有问题,服务器也到了我的⽇志⽂件,但应该是我⾃⼰的Logger是不对应正确的⽇志输出的,因为我的console(控制台)有显⽰。
接下来,我就是开始看源码了。先是Logger(Class<?> clazz)⽅法:
public static Logger getLogger(Class<?> clazz) {
Logger logger = Name());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = CallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
}
}
return logger;
}
好吧,没什么⽤,看不出我的logger变成了,继续看getLogger(String name)⽅法:
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
Logger(name);
}
这时我在Logger(name);这⾏打了断点,我看到了这样的东西:
为什么我的iLoggerFactory是⽤的logback中的实现?其实也是怪我⾃⼰⼤意,我其实依赖了⼀个基于Spring Boot的项⽬(虽然我只是⽤了⾥⾯的⼀些domain类,但因为设计不当,还没有把这些domain类单独提成⼀个_项⽬),⽽Spring Boot中⼀般默认就依赖的logback。通过gradle查看项⽬的依赖树,也证实了我的这⼀猜想(./gradlew ⼦项⽬名称:dependencies):
|    +--- org.springframework.boot:spring-boot-starter-web:2.0.2.RELEASE
|    |    +--- org.springframework.boot:spring-boot-starter:2.0.2.RELEASE
|    |    |    +--- org.springframework.boot:spring-boot:2.0.2.RELEASE
|    |    |    |    +--- org.springframework:spring-core:5.0.6.RELEASE (*)
|    |    |    |    \--- org.springframework:spring-context:5.0.6.RELEASE (*)
|    |    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.0.2.RELEASE
|    |    |    |    \--- org.springframework.boot:spring-boot:2.0.2.RELEASE (*)
|    |    |    +--- org.springframework.boot:spring-boot-starter-logging:2.0.2.RELEASE
|    |    |    |    +--- ch.qos.logback:logback-classic:1.2.3
|    |    |    |    |    +--- ch.qos.logback:logback-core:1.2.3
|    |    |    |    |    \--- org.slf4j:slf4j-api:1.7.25
接下来就好办了,你排除掉ch.qos.logback的依赖就可以了,在你的adle中增加:
configurations {
}
这个时候你再重新调试⼀下看看:
完美,现在是log4j中的实现,得到了我想要的操作。当然了,既然我知道之前项⽬中的slf4j是logback实
现了,那么我⾃然也可以换成logback的配置,但这就需要我将项⽬换成⽤Spring Boot启动,这个改动有点⼤,如果以后有必要的话,我再将这个exclude删除,换成Spring Boot的形式。
这次Spring Boot帮我们默认启⽤的是logback,那么有没有什么简单⽅法可以知道呢?如果你的项⽬出现了以下的⽇志输出,说明你的项⽬当前有不⽌⼀个SLF4J的实现组件:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/project.war/WEB-INF/lib/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/project.war/WEB-INF/lib/slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See /codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
因为在org.slf4j.LoggerFactory的bind⽅法中有关于这⽅⾯的输出:
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
// 查你的当前项⽬有⼏个slf4j的实现
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
// 如果多余⼀个就打印
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
/
/ 这个是具体选了哪⼀个实现(重点关注)
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = Message();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = Message();
if (msg != null && ains("org.slf4j.Singleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
特别要注意的是Singleton();这⾏代码,StaticLoggerBinder在logback-classic和slf4j-log4j12这两个jar包各有⼀个,因log4j2不打印日志
此,Spring boot是⾃动选择logback-classic(虽然我在本地运⾏的时候还是默认进⼊的slf4j-log4j12,但是会提醒我Source code does not match the bytecode,因此我判断依旧进的是logback-classic),所以只要把logback给exclude掉,就解决了这个问题。
现在看问题,更加关注源代码,因为这可以让我们更加快速定位问题,并且也能据此⼤致猜出其解决⽅案。希望⼤家能⼀起看看源代码,如果你有什么发现,可以在下⽅留⾔,我将和你⼀起讨论。
有兴趣的话可以关注我的或者头条号,说不定会有意外的惊喜。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。