I'm learning Java and I find that there are many functionalities that are standardized :
logging (using SLF4J)
Persistence (using JPA)
REST (using JAX-RS)
SOAP (using JAX-WS)
etc.
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
The OP asks a general question about how implementation is injected in some different cases.
Logging
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 !
Persistence
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.
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 :
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:
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.
Related
I've found a few examples (even on Stack Overflow) of some programmatic configuration of Logback logging appenders, but as much as I've incorporated into my own setup hasn't worked for me so far. Some examples produce an actual Logger instance, but considering I've already got a Logger being statically instantiated within my class, I want to be able to programmatically enable an Appender that I've defined for unit testing purposes.
Here is my custom appender:
package org.example.logging;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.util.ArrayList;
import java.util.List;
// Credit to https://stackoverflow.com/a/29077499/5476186
public class TestAppender extends AppenderBase<ILoggingEvent> {
private static List<ILoggingEvent> events = new ArrayList<>();
#Override
protected void append(ILoggingEvent e) {
events.add(e);
}
public static List<ILoggingEvent> events() {
return List.copyOf(events);
}
public static void clear() {
events.clear();
}
}
And in my testing code, I'm trying to configure my TestAppender to "kick in" so that, after invoking this method in my test setup, I can capture the logs and validate them:
package org.example.logging;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
// ...
// Mostly modeled after https://stackoverflow.com/a/7825548/5476186
private static void startAppender() {
LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory();
TestAppender appender = new TestAppender();
appender.setContext(logCtx);
appender.setName("TEST");
// I was hoping this would statically allow the appender to kick in,
// but all of the examples then attach this appender to a Logger instance.
appender.start();
}
Obviously, this isn't working for me. So I guess I have two contingent questions.
Is this possible and, if so, how can I make it work?
If this is not possible, what's the cleanest way to accomplish what I'm trying to do? (Enable/disable appenders during testing without having to manually mess with a config file.)
In one of the threads linked above, I found this answer which looks like one possible solution is to modify the text in the configuration file and to force a reload, but that doesn't seem super clean to me. Another option would be to create my own wrapper Logger factory which I could use to provide loggers with my TestAppender during test execution with dependency injection. I'll probably be creating a wrapper anyway, even though I'm using SLF4J.
Side note: I know that my test code as currently written is pretty tightly coupled with Logback instead of SLF4J, so I'm open to criticism/advice on that issue, too.
If you're using slf4j in your production code, then there is already a project that can help in testing: Its called slf4j-test
In a nutshell, it provides an API to retrieve a "test logger" in the test that will keep all the logged messages in memory so that you'll be able to verify them.
So that you:
Execute a method that logs something
Retrieve a test logger
call getLoggingEvents() on the test logger and verify the logged events
The link that I've provided contains an example of the API as well as maven integration example.
If, alternatively you would like to use logback directly for the tests or something, there is already a ListAppender shipped as a part of logback distribution that allows retrieval of events that have passed through the appender. You can add it programmatically to the logger and use inside the test.
Here you can find a comprehensive example of doing that
We’re using log4j2 to do logging throughout our application and now I want to add some additional functions to the LogManager…at the same time, I hoped it would be possible to hide the “implementation details” of the LogManager for the rest of the application, so that instead of importing the log4j2 Logger everywhere, I can expose my own interface or class, from my own proprietary LogManager (that way, it would be possible to refactor or replace the way I store log messages without affecting the entire application).
I can create my own LogManager, called LM, easily like this:
package com.xxx.yyy.logging;
import org.apache.logging.log4j.LogManager;
public class LM extends LogManager {
…add own methods here…
}
But when I call:
LM.getLogger(Application.class)
It returns an object of type org.apache.logging.log4j.Logger – is there an easy way to “wrap and expose” this interface via my own package, so that the rest of the application don’t have to be concerned with log4j?
I’ve tried something like:
package com.xxx.yyy.logging;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.message.MessageFactory;
public class LMlogger extends org.apache.logging.log4j.core.Logger implements Logger {
protected LMlogger(LoggerContext context, String name,
MessageFactory messageFactory) {
super(context, name, messageFactory);
// TODO Auto-generated constructor stub
}
};
…in order to implement the class returned as well as the expected interface, but still I can’t “down-cast” the returned Logger-object to my own LMlogger (which makes sense, as my class is the sub-class). But is there another way to keep the log4j2 implemenation details in one place only, without having to wrap each and every method of the Logger-class?
You can use the tool that log4j2 provides to generate custom Logger wrappers.
This tool was intended to add convenience methods for custom log levels or hide existing log levels, but you can use it for any purpose.
It also hides the LogManager from the client code (your app), and the generated code is in whatever package you specify, so client code won't be aware it is using log4j2.
You may need to regenerate the wrapper when you upgrade log4j2 after it had API changes (which is rare, but there will be additional API in 2.6).
Look at the following way:
Create a same-name Class org.apache.logging.log4j.LogManager in your project, and copy the source code of the original org.apache.logging.log4j.LogManager in log4j lib. Then edit the code and add what you want.
Logger or other class in the same way.
But you must make sure that your project must be loaded before the log4j lib.
I have several data processing algorithms that can be assembled into a pipeline to transform data. The code is split into two components: A pre-processing component that does data loading-related tasks, and a processing pipeline component.
I currently have the two parts compiled and packaged into two separate jars. The idea is that the same pre-processing jar can be shipped to all customers, but the pipeline jar can be exchanged depending on customer requirements. I would like to keep the code simple and minimize configuration, so that rules out the use of OSGi or CDI frameworks.
I've gotten some hints by looking at SLF4J's implementation. That project is split into two parts: A core API, and a bunch of implementations that wrap different logging APIs. The core API makes calls to dummy classes (which exist in the core project simply to allow compilation) that are meant to be overridden by the same classes found in the logging projects. At build time, the compiled dummy classes are deleted from the core API before packaging into jar. At run time, the core jar and a logging jar are required to be included in the class path, and the missing class files in the core jar will be filled in by the files from the logging jar. This works fine, but it feels a little hacky to me. I'm wondering if there is a better design, or if this is the best that be done without using CDI frameworks.
Sounds like the strategy software design pattern.
https://en.wikipedia.org/wiki/Strategy_pattern
Take a look at the ServiceLoader.
Example Suppose we have a service type com.example.CodecSet which is
intended to represent sets of encoder/decoder pairs for some protocol.
In this case it is an abstract class with two abstract methods:
public abstract Encoder getEncoder(String encodingName);
public abstract Decoder getDecoder(String encodingName);
Each method returns an appropriate object or null if the provider does
not support the given encoding. Typical providers support more than one
encoding. If com.example.impl.StandardCodecs is an implementation of
the CodecSet service then its jar file also contains a file named
META-INF/services/com.example.CodecSet
This file contains the single line:
com.example.impl.StandardCodecs # Standard codecs
The CodecSet class creates and saves a single service instance at
initialization:
private static ServiceLoader<CodecSet> codecSetLoader
= ServiceLoader.load(CodecSet.class);
To locate an encoder for a given encoding name it defines a static factory method which iterates
through the known and available providers, returning only when it has
located a suitable encoder or has run out of providers.
public static Encoder getEncoder(String encodingName) {
for (CodecSet cp : codecSetLoader) {
Encoder enc = cp.getEncoder(encodingName);
if (enc != null)
return enc;
}
return null;
}
A getDecoder method is defined similarly.
You already understand the gist of how to use it:
Split your project into parts (core, implementation 1, implementation 2, ...)
Ship the core API with the pre-processor
Have each implementation add the correct META-INF file to its .jar file.
The only configuration files that are necessary are the ones you package into your .jar files.
You can even have them automatically generated for you with an annotation:
package foo.bar;
import javax.annotation.processing.Processor;
#AutoService(Processor.class)
final class MyProcessor extends Processor {
// …
}
AutoService will generate the file
META-INF/services/javax.annotation.processing.Processor
in the output classes folder. The file will contain:
foo.bar.MyProcessor
I have an Enum inside a jar that I have produced myself. This jar is a dependency of a second jar, which uses the enum values.
Now, the second jar is a logging framework, whereas the first jar in this case is the model classes of the logging framework.
I am trying to implement this logging framework into a web application that I have made. Long story short, it still needs some work, but I am stuck on a single problem. An error in the framework's configuration initialization is caught as an exception, and it calls a method. This method has an Enum value as one of it's parameters. However, I get a java.lang.NoSuchFieldError on this enum.
The Enum value was ERROR, so i figured it could be a coincidence. But when I changed it to BABYLOVE the error message changed as well.
I've checked for redundancies and/or possible overlappings in class/enum names, but there are none that I can find.
Sequence of order:
Web App calls for initialization of logging-framework (direct dependency)
logging-framework has issues loading it's own configuration, and throws an exception
Exception is handeled, and a method is called to register the error
The method is called with several parameters, one which is an enum value from logging-framework-model.jar, which is a transitive dependency of the web app
The web-app throws an exception
java.lang.NoSuchFieldError: BABYLOVE
at logging.framework.Constants.<clinit>(Constants.java:52)
at logging.framework.Logger.<init>(Logger.java:60)
at logging.framework.LogContext.getLoggerFromContext(LogContext.java:95)
at logging.framework.LogContext.getCurrent(LogContext.java:48)
at action.navigation.CalendarElementEditorAction.execute(CalendarElementEditorAction.java:39)
Truncated. see log file for complete stacktrace
Constants, line 51-52:
public static final Event ConfigValidationFailed =
EventLogHelper.getEvent(EventLogSource.LoggingFramework, EventLogEntryType.BABYLOVE");
EventLogEntryType:
#XmlType(name = "EventLogEntryType")
#XmlEnum
public enum EventLogEntryType {
//for test purposes, should be removed. This variable is given a name that can not be confused with standard names in error messages, like Error and Warning can.
#XmlEnumValue("BabyLove")
BABYLOVE("BabyLove"),
#XmlEnumValue("Error")
ERROR("Error"),
#XmlEnumValue("Warning")
WARNING("Warning"),
#XmlEnumValue("Information")
INFORMATION("Information"),
#XmlEnumValue("SuccessAudit")
SUCCESSAUDIT("SuccessAudit"),
#XmlEnumValue("FailureAudit")
FAILUREAUDIT("FailureAudit");
private final String value;
EventLogEntryType(String v) {
value = v;
}
public String value() {
return value;
}
public static EventLogEntryType fromValue(String v) {
for (EventLogEntryType c: EventLogEntryType .values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
I don't know if it matters, but I am using maven2 to deal with my dependencies.
I was told to check if the versions of my dependencies had mismatches, and after checking the war's content, I found that to be the problem.
My webapp is one of two very similar ones, that both has a dependency to a jar containing some base model and business logic classes. I had previously added the logging framework (version 1) to that project's pom.xml. So the logging framework 1.0 was a transitive dependency of the web app, while the logging framework 2.0 was a direct dependency of the web app. I am guessing that direct dependencies has precedence over transitive dependencies, so 2.0 was the one who was packaged into my war. However, since the logging framework is composed of a framework (direct dependency), and a set of model classes (transitive dependency), the war was packaged with logging framework model version 1.0.
After I unpacked the war, and found this, it was a pretty easy process to find out where it was wrongly imported, and I ended up with only logging framework version 2.0 for the complete set.
I want to distribute a lib relying on the SLF4J logger interface. What is the best practice way to obtain the logger which integrate nicely into any other project? Sorry for the unstructured question style, I'm still trying to figure out how all this stuff is glued together.
In other projects I always use this piece of code, because I want to obtain a custom logger:
private final static Logger LOGGER = LoggerFactory.getILoggerFactory().getLogger(NAME_OF_APP);
If I create the class org.slf4j.impl.StaticLoggerBinder and have it some other lib, does the therein defined factory get used even if I just call LoggerFactory.getLogger(NAME_OF_APP) or is some default slf4j factory used?
I want the user to be able to use his own factory and logger, so which way is to perfere, and most of all why?
I'm not sure I fully understand what you are trying to do.
SLF4J is composed of two parts. First the API which you use in your lib to code your logging calls. And secondly the implementation which you use during your development to do logging, but DO NOT set as a dependency of the lib.
Because SLF4J looks for the implementations on the class path the developers using our lib can simple include any implementation they want. Sometimes is some quite strange ways :-) They can use a range of prebuilt implementations or code their own. It's up to them.
I don't think you need to do anything more than just use SLF4J's API as is.
From http://slf4j.org/manual.html
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
So, always use LoggerFactory.getLogger(...). The argument allow the logger backend to determine the behavior of the logger returned to you.
I am not sure I fully understand what your scenario is.
But from my viewpoint, what you want is a distributed logging component.
A simple approach to do that is a socket appender. And a full feature component for distributed logging may like Facebook's scribe.
Use Logger static wrapper from jcabi-log:
import com.jcabi.log.Logger;
public class MyLibraryClass {
public void foo() {
Logger.info(this, "some information");
}
}
All logs will be sent through SLF4J.