Our web app runs in a tomcat 9 container and uses Log4j 2.13.3 as logging system.
The web app includes org.apache.xmlgraphics:fop 2.3, which uses apache commons-logging (version 1.2 in our setup instead of 1.0.4 originally used in fop).
This combination has been running for years using log4-jcl, the Commons Logging Bridge, and all commons-logging output correctly went into the files configured by log4j. The configuration is fairly easy, as described in this SO answer
However, for no apparent reason, fop has recently started writing all its (rather verbose) logs to stderr (ie directly to catalina.out) instead of the log file configured, but only on some of our systems. We have some 50 tomcat instances running basically identical software and identical system configurations; one third of them still logs correctly.
I assume that commons-logging doesn't find log4j-jcl's implementation of org.apache.commons.logging.LogFactory.
Debugging log4j using Configuration status="trace" doesn't provide any hint of a failure in log4j-jcl or elsewhere.
I'm out of my wits.Any suggestions?
Answering my own question in case it's helpful to others.
I needed to add a file called commons-logging.properties in the classpath, containing one line:
org.apache.commons.logging.LogFactory=org.apache.logging.log4j.jcl.LogFactoryImpl
Kudos to Piotr for his comment about setting org.apache.commons.logging.diagnostics.dest.
Apparently, if the class is not explicitly specified, the implementation of LogFactory that is actually used depends on the (somewhat random) order the classloader loads the potential candidates.
I my webapp a maven dependency brought sl4f along as a transitive dependency, so on some systems slf4j was chosen instead of log4j, but slf4 didn't have a valid configuration and defaulted to STDERR.
While your answer explains why Log4j 2.x is not chosen as JCL's backend, some details don't add up: the original commons-logging
never chooses SLF4j as backend (cf. source code ). Hence the situation must be more complicated:
commons-logging
discovers alternative LogFactory
implementations through the ServiceLoader
utility.LogFactory
implementations in your classpath: log4j-jcl
and spring-jcl
. The latter is a complete replacement for the standard commons-logging
library (ie it contains a copy of org.apache.commons.logging.LogFactory
itself) and has some complex backend selection rules (cf. source code ), which in your case prefer SLF4J over Log4j 2.x. Since the classloader randomly loads the org.apache.commons.logging.LogFactory
class from either commons-logging
or spring-jcl
you can have two kinds of problems:
commons-logging
version is chosen the ServiceLoader
randomly loads log4j-jcl
's implementation or spring-jcl
's implementation. You already solved this case in your answer.spring-jcl
's version is chosen the SLF4J backend will be used.To solve all these potential problems I would:
commons-logging
and keep the clone spring-jcl
. Even better I would exclude both and use the deterministic jcl-over-slf4j
(complete JCL replacement that always uses SLF4J)log4j-jcl
since it is no longer needed (it is only required by the original commons-logging
),log4j-slf4j-impl
SLF4J binding. This solution adds an additional API to your logging configuration (JCL -> SLF4J -> Log4j 2.x), but guarantees stability. It is also the way spring-boot-starter-log4j2
works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.