To share logback configuration between multiple projects we embed our logback.xml file within a common jar. e.g. mylogger.jar. Projects depend upon this jar for logging hence it's always on the classpath. This means the logback.xml will be found as documented at
https://logback.qos.ch/manual/configuration.html#auto_configuration
However if another jar, e.g. otherlib.jar, also embeds a logback.xml file we'll see a warning
09:27:03,122 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
09:27:03,122 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
09:27:03,122 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [jar:file:/WEB-INF/lib/mylogger.jar/logback.xml]
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs multiple times on the classpath.
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/WEB-INF/lib/mylogger.jar/logback.xml]
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/WEB-INF/lib/otherlib.jar/logback.xml]
Worse sometimes it does not pick the correct logback.xml as this behavior is non-deterministic according to Controlling the classpath in a servlet.
Is there any mechanism to force the warning to fail a build? This'll alert us to the above scenario whereas a warning can be ignored.
During initialisation Logback emits Status
events to describe what's happening. These ...
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs multiple times on the classpath.
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/WEB-INF/lib/mylogger.jar/logback.xml]
09:27:03,123 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/WEB-INF/lib/otherlib.jar/logback.xml]
... are log statements for some of the Status
events. These Status
events are emitted by Logback's ContextInitializer
...
if (urlSet != null && urlSet.size() > 1) {
sm.add(new WarnStatus("Resource [" + resourceName + "] occurs multiple times on the classpath.", loggerContext));
for (URL url : urlSet) {
sm.add(new WarnStatus("Resource [" + resourceName + "] occurs at [" + url.toString() + "]", loggerContext));
}
}
You're likely seeing those events logged because you have configured Logback with <configuration debug="true">
. Using debug=true
is equivalent to installing an OnConsoleStatusListener
.
You could register a custom StatusListener
which reacts to these Status
events differently. Given that you want to "force the warning to fail a build" then you could throw an exception when your StatusListener
encounters the "Resource ... occurs multiple times on the classpath." event.
Here's an (untested) example:
import ch.qos.logback.core.status.Status;
import ch.qos.logback.core.status.StatusListener;
public class StrictConfigurationWarningStatusListener implements StatusListener {
@Override
public void addStatusEvent(Status status) {
if (status.getEffectiveLevel() == Status.WARN) {
// you might want to consider how best to evaluate whether this is the message you are interested in
// this approach is bound to a string and hence will no longer work if Logback changes this message
if (status.getMessage().endsWith("occurs multiple times on the classpath.")) {
throw new LogbackException(status.getMessage());
}
}
}
}
You can register your listener in logback.xml
as follows:
<statusListener class="some.package.StrictConfigurationWarningStatusListener" />
With the above registration and listener in place you'll be able to intercept the "Resource ... occurs multiple times on the classpath." events and provide your own action/response to them.