Reputation: 81
I'm learning Java and I find that there are many functionalities that are standardized :
Let's take the Sl4j example : to use it correctly with log4j, we have to import the sl4j api, the sl4j/log4j bridge and the log4j implementation.
Question : In my class, I only communicate with the Slf4j API.
How my application knows about the log4j implementation ? Can someone explains what's happening exactly under the hood ?
Regards
Upvotes: 7
Views: 905
Reputation: 15070
The OP asks a general question about how implementation is injected in some different cases.
As sated in many answers, the SLF4J
gives the interface and log4j-slf4j
gives the implementation.
When you use the following statement:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger LOG = LoggerFactory.getLogger(FooBarClass.class);
...
LOG.debug("Foobar");
This is what is Happening:
We try to get the Logger
from the getLogger
method declared in the LoggerFactory
class :
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
}
...
}
So the magic happens at that statement:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
Because the classpath knows you implemented why, the StaticLoggerBinder
implementation is provided by log4j
.
As we can notice, log4j
provides a with its own implementation:
private final ILoggerFactory loggerFactory;
...
private StaticLoggerBinder() {
loggerFactory = new Log4jLoggerFactory();
}
And that's it !
For JPA/Hibernate part, you have to include hibernate-jpa-api
and hibernate-*
(core, entitymanager, etc).
Let's say you want to create an EntityManagerFactory
:
import javax.persitence.EntityManagerFactory
import javax.persitence.Persistence;
...
private static EntityManagerFactory EMF = Peristence.createEntityManagerFactory("foobar", null);
As for List
and ArrayList
, your classpath is fed with the interface and the implementation thanks to the JARs you import.
The EntityManagerFactory
comes from the hibernate-jpa-api
where we have a Persistence
class.
We can notice that the createEntityManagerFactory
method first lists all the providers and for each one of them, an createEntityManagerFactory
is fired.
This is where the hibernate
comes. It provides an HibernatePersistenceProvider
that implements the PersistenceProvider
class.
This is how Hibernate
is injected.
Upvotes: 5
Reputation: 131346
The SLF4J manual refers to how under the hoop SLF4J finds the implementation to use : Binding with a logging framework at deployment time.
SLF4J refers the thing that allows to use an implementation (Logback, Log4J, etc...) as "SLF4J bindings" :
As mentioned previously, SLF4J supports various logging frameworks. The SLF4J distribution ships with several jar files referred to as "SLF4J bindings", with each binding corresponding to a supported framework.
You have as many SLF4J bindings as implementations of SLF4J. And of course, an implementation API may have distinct "SLF4J bindings" according to its version :
To switch logging frameworks, just replace slf4j bindings on your class path. For example, to switch from java.util.logging to log4j, just replace slf4j-jdk14-1.7.22.jar with slf4j-log4j12-1.7.22.jar.
The binding with the implementation is not performed at runtime but at compile-time : each SLF4J binding is hardwired at compile time to use one and only one specific logging framework.
So, you have just to include the SLF4J binding in the classpath (for example slf4j-jdk14-1.7.22.jar) so that SLF4J uses it :
SLF4J does not rely on any special class loader machinery. In fact, each SLF4J binding is hardwired at compile time to use one and only one specific logging framework. For example, the slf4j-log4j12-1.7.22.jar binding is bound at compile time to use log4j. In your code, in addition to slf4j-api-1.7.22.jar, you simply drop one and only one binding of your choice onto the appropriate class path location. Do not place more than one binding on your class path. Here is a graphical illustration of the general idea.
That's why it is generally advised to never place more than one SLF4J binding on the classpath as SLF4J is not designed to choose the implementation at runtime.
Upvotes: 1
Reputation: 34900
If you are talking about dealing with slf4j
loggers, like:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooClass.class);
then it is pretty simple: org.slf4j.Logger
is just an interface, which has several implementations. In case of usage library slf4j-log4j12
, this interface is implemented by class org.slf4j.impl.Log4jLoggerAdapter
which internally contains
final transient org.apache.log4j.Logger logger;
So it is simple adapter which wraps your logging requests and invoke them on log4j
logger object:
public void debug(String msg) {
logger.log(FQCN, Level.DEBUG, msg, null);
}
More specifically, proper Logger
implementation is produced by LoggerFactory
which firstly creates Log4jLoggerFactory
via
StaticLoggerBinder.getSingleton().getLoggerFactory()
, latter creates needed Log4jLoggerAdapter
instance.
Generally it works via adaptation level like pictured on img from documentation:
Upvotes: 2
Reputation: 5813
Slf4j
can be used with log4j
or any other underlying logging library.
In case of log4j
, it uses log4j-slf4j-impl.jar
which contains necessary classes for communicating with log4j
library.
As per the documentation -
SLF4J doesn't resolve the logging implementation at execution, but directly at the compilation with a bridging API. So more than the JAR of SLF4J you need the following JARs : the bridging JAR and the JAR of the implementation. Here is what you get with Log4J :
Upvotes: 2