JUL to SLF4J Bridge
You need to call SLF4JBridgeHandler.install()
. You also need to enable all log levels at the root logger (reason in excerpt below) in java.util.logging and remove the default console appender.
This handler will redirect jul logging to SLF4J. However, only logs
enabled in j.u.l. will be redirected. For example, if a log statement
invoking a j.u.l. logger disabled that statement, by definition, will
not reach any SLF4JBridgeHandler instance and cannot be redirected.
The whole process can be accomplished like so
import java.util.logging.Logger;
import org.slf4j.bridge.SLF4JBridgeHandler;
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
Logger.getLogger("").setLevel(Level.FINEST); // Root logger, for example.
You can set the level to something higher than finest for performance reasons, but you won't be able to turn those logs on without enabling them in java.util.logging
first (for the reason mentioned above in the excerpt).
How to run jul-to-slf4j bridge once per JVM?
There are various ways to make some code run at the beginning of a test suite.
Here are 4 (I'm sure there are more):
JUnit via
RunWith
Suite
withSuite.SuiteClasses
andBeforeClass
(adapted from examples inSuiteTest
):@RunWith(Suite.class)
@SuiteClasses({FirstTest.class, SecondTest.class/*, ...*/, LastTest.class})
public static class AllWithSLF4JBridgeHandler {
@BeforeClass
public static void registerRootLoggerHandlers() {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}TestNG with BeforeSuite:
/**
* Base class for each test class (i.e. every test class should extend this class).
*/
public abstract class BaseTest {
@BeforeSuite
public void registerRootLoggerHandlers() {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}TestNG with Guice:
/**
* Test module. Each test class should be annotated with `@Guice(TestModule.class)`.
*/
public class TestModule implements Module {
@Override
public void configure(Binder binder) {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}Static initialization blocks (test-framework independent):
/**
* Base class for each test class (i.e. every test class should extend this class).
*/
public abstract class BaseTest {
static {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
I'm not sure how all of these methods work with Surefire's parallel mode. Methods 1 and 2 may not work there but I believe methods 3 and 4 should.
Another option would be to not use the programmatic installation of the SLF4JBridgeHandler
but to use a java.util.logging.config file or class (see LogManager
):
"java.util.logging.config.file":
logging.properties file:
// register SLF4JBridgeHandler as handler for the j.u.l. root logger
handlers = org.slf4j.bridge.SLF4JBridgeHandlerSystem property assignment:
java -Djava.util.logging.config.file=/path/to/logging.properties ...
This works well if you know the path to your logging file beforehand.
"java.util.logging.config.class":
Using a file may not be a good option if you're deploying a WAR and don't know where the file will be, etc. so alternatively you can create a logging config class:
public class SLF4JBridgeHandlerInitializer {
public SLF4JBridgeHandlerInitializer() throws IOException {
String loggingConfigurationString = "handlers = " + SLF4JBridgeHandler.class.getName();
InputStream inputStream = new ByteArrayInputStream(loggingConfigurationString.getBytes());
LogManager.getLogManager().readConfiguration(inputStream);
}
}System property assignment:
java -Djava.util.logging.config.class=package.SLF4JBridgeHandlerInitializer ...
I've done this before and it has worked well for me (SLF4JBridgeHandler.Initializer by mfulton26 · Pull Request #57 · qos-ch/slf4j).
These final two options should initialize each JVM instance as long as the appropriate system property is set.
What is impact of using jul-to-slf4j several times in Java EE / Spring container?
I open request to improve documentation: http://jira.qos.ch/browse/SLF4J-351
Response is:
There is one j.u.l.LogManager instance per JVM.
Thus, any manipulation of it impacts the whole JVM.
So in multi-application container environment jul-to-slf4j should be a single instance per JVM and loaded from system classes or you get hard to explain problems.
Java logging: slf4j over jul and log4j2
The reason why Log4j 2 is still using its own configuration is because you are only using SLF4J's log4j-over-slf4j
which is for Log4j 1 (despite not being that clearly documented). For Log4j 2 to SLF4J, you will have to add Log4j 2 to SLF4J Adapter (dependency information) as well.
For jul-to-slf4j
you have to follow the steps described in the SLF4JBridgeHandler
documentation:
- Either specify it in the
logging.properties
file of your JRE. Though if you have no control over the environment your application is running, this is likely not an option. - Or programmatically install it:
static {
// Verify that it is not installed yet; other dependency might have already
// installed it which would cause duplicate handler
// Unfortunately SLF4J provides no built-in method for this procedure, so
// race condition between different classes could happen
if (!SLF4JBridgeHandler.isInstalled()) {
// Remove default handler logging to System.err
SLF4JBridgeHandler.removeHandlersForRootLogger();
// Install the SLF4J handler
SLF4JBridgeHandler.install();
}
}
Additionally you should configure the LevelChangePropagator
for Logback. Otherwise, because the SLF4J class is only a handler, lowering the SLF4J log level (e.g. for debugging) would have no effect on java.util.logging
.
Note however, that redirecting java.util.logging
in general is a pain, especially if your project has multiple entry points. You will have to perform the SLF4J bridge initialization check in every one of these entry points.
As a side note: The situation for the java.util.logging
to Log4j 2 bridge (Log4j JDK Logging Adapter) it is not much better. That library instead implements LogManager
which therefore integrates more tightly into java.util.logging
, but has the disadvantage that it has to be specified using a System property (or the logging.properties
file) and must be specified before java.util.logging.LogManager
is loaded (which might not be in your control), afterwards a custom LogManager
implementation cannot be specified anymore.
There is also a JUL Handler class called Log4jBridgeHandler
, but that only exists on the not yet released 3.0.0 branch and has not been backported yet, see backport pull request.
NPE when using JUL with SLF4J at trace level
This is bug FilterChainContext.toString crashes with NullPointerException #1959 in Grizzly.
There are two overloads of String.valueOf, with Object and char[]. (Java 8)
Because of the dangerous unbound generic in getMessage both overloads would seem applicable, however the char[] overload is chosen, probably because it is more specific.
[snip]
However String.valueOf(char[]) will fail if the result of getMessage is null. I would assume a ClassCastException could happen in other cases. This can be fixed by explicitly casting the result to object or ensuring that the correct generics are infered.
You need to upgrade to a newer version of Grizzly (2.4.3).
A regression bug was filed in Nov 10, 2020 under: FilterChainContext.toString causes NullPointerException #2110 which reports fixed in 4.0.0-M2-RELEASE.
Commit is listed as: https://github.com/eclipse-ee4j/grizzly/commit/d7a96f15083a55c38f63746007f664f1ede9f474
Send/redirect/route java.util.logging.Logger (JUL) to Logback using SLF4J?
It's very easy and not a performance issue anymore.
There are two ways documented in the SLF4J manual. There are also precise examples in the Javadocs
Add jul-to-slf4j.jar to your classpath. Or through maven dependency:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.0</version>
</dependency>
If you don't have logging.properties (for java.util.logging), add this to your bootstrap code:
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
If you have logging.properties (and want to keep it), add this to it:
handlers = org.slf4j.bridge.SLF4JBridgeHandler
In order to avoid performance penalty, add this contextListener to logback.xml (as of logback version 0.9.25):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<!-- reset all previous level configurations of all j.u.l. loggers -->
<resetJUL>true</resetJUL>
</contextListener>
...
</configuration>
Related Topics
Implementing Custom Methods of Spring Data Repository and Exposing Them Through Rest
Java's Collections.Shuffle Is Doing What
Merging Two JSON Documents Using Jackson
How to Get a List of Trusted Root Certificates in Java
Can't Make Jackson and Lombok Work Together
Shuffle a List of Integers with Java 8 Streams API
How to Identify Contents of a Byte[] Is a Jpeg
Absolute Minimum Code to Get a Valid Oauth_Signature Populated in Java or Groovy
Formatting a String to a Currency Format in Jasper Report
Eclipse - Debugger Doesn't Stop at Breakpoint
How to Update Maven Repository in Eclipse
How to Upper Case Every First Letter of Word in a String
Service Layer and Controller: Who Takes Care of What
Java.Lang.Noclassdeffounderror: Org.Slf4J.Loggerfactory
How to Dynamically Create a Test Suite in Junit 4