Project, I am working on, is using several open source frameworks. Each framework uses some logging library.
My problem is that final deliverable contains log4j (in two different versions), slf4j with logback and of course common logging.
I am looking for solution how to reduce number of libraries, can I exclude logging libraries in maven's pom file and configure slf4j to 'intercept' logging messages?
Slf4j has some information in its manual about how to do some of this.
I think what you are looking for is to exclude log4j and commons-logging from all of your maven dependencies and load the log4j-over-slf4j and the jcl-over-slf4j packages instead. They are designed to replace the log4j and commons-logging and contains the appropriate org.apache.log4j and org.apache.commons.logging classes to replace Logger, Log, and friends. This won't cut down on the libraries but it will put all of the log output through sl4fj. Unless you get rid of the usage of these classes (obviously) you can't reduce the dependency on either the slf4j wrapper or the original.
If you are instead looking to use one logging package then the right way to do it might instead to switch to use commons-logging which is designed as a logging delegator and then use the slf4j-jcl package which hooks into commons-logging.
For posterity, you exclude the dependency this way for each of the dependencies that requires log4j.
<dependency>
<groupId>.../groupId>
<artifactId>...</artifactId>
<version>...</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
Check this out
EDIT read your question a bit more closely. slf4j adds a legacy bridge for non slf4j projects. But you will still need to supply the dependent jars on your runtime path. Its just the cost of not repeating someone else's work.
I mean think about it you have all those fully qualified symbols that essentially need linked in your jvm. Nothing short of some straight voodoo is going to get you around including the dependent libraries.
Have a look at pax-logging. It brings in pretty much all the logging framework interfaces you can think of (right package names and classes, etc...) but it doesn't implement them. Instead, it links those facades to a log4j backend.
In other words, your projects continue using their original logging frameworks, but these are (transparently) handed over to a log4j. This way your logging output is consistent, and configured only in one place.
Related
I created a library using Java & Maven that contains some common Spring functionality for reuse in various Spring-Based projects.
The library will obviously need to log error/information messages. Spring allows the developer to use whichever logging library they prefer, how do I make my library do the same?
Spring uses commons-logging which is a logging facade that allows you to write logging code without knowing what is the actual logging framework.
Depending on what you have configured, the commons-logging will then channel the logging messages to the actual implementation. This allows you to write your code using commons-logging, and the users to use any logging framework supported by commons-logging.
Personally I would go with slf4j (Simple Logging Facade 4 Java) which is similar to commons-logging, but newer and functions in pretty much the same way.
It's also possible to bridge different libraries or facades, so that even if libraries use different logging frameworks they will always end up in the logger of your choice. Looking to the monitor to my right there are bridge libraries as follows: jcl-over-slf4j (commons-logging to slf4j), log4j-over-slf4j and jul-to-slf4j (java.util.logging to slf4j).
Note: See the link in comments for more information about how slf4j = good, JCL = bad ;)
We use slf4j + logback, and happened to have some third-party libraries which use commons-logging. How do I set it up to use logback?
The answer is to not use commons-logging.jar, since SLF4J was designed to do what commons-logging does but better. As #MahdeTo refers to, you need to use jcl-over-slf4j.jar.
Check out the documentation from the slf4j website on migrating from commons-logging.
I come across this question too, and found out jcl-over-slf4j.jar indeed can solve the problem, I couldn't understand that why commons-logging couldn't use logback automatically, since commons-logging is log interface and logback is implementation, they should integrate automatically, until I found this:
The Apache Commons Logging (JCL) provides a Log interface that is
intended to be both light-weight and an independent abstraction of
other logging toolkits. It provides the middleware/tooling developer
with a simple logging abstraction, that allows the user (application
developer) to plug in a specific logging implementation.
JCL provides thin-wrapper Log implementations for other logging tools,
including Log4J, Avalon LogKit (the Avalon Framework's logging
infrastructure), JDK 1.4, and an implementation of JDK 1.4 logging
APIs (JSR-47) for pre-1.4 systems. The interface maps closely to Log4J
and LogKit.
Obviously not all the log interface can integrate nicely with log implementation which mean, if you really want to use logback, jcl-over-slf4j.jar is your only solution now because JCL only support Log4J, Logkit, JDK 1.4.
Just add jcl-over-slf4j to the dependencies of your project (check current version at https://search.maven.org/search?q=g:org.slf4j%20AND%20a:jcl-over-slf4j&core=gav)
for those all who wants to keep the final package size smaller; checkout
mvn dependency:tree result of your project and if any dependency to commons-logging exists, exclude them as well. Since the jcl-over-slf4j.jar contains both Log and LogFactory classes with exact same package structure, these commons-logging jars will be extra on your final package.
I have this 3rd party library that has:
slf4j-api-1.5.5.jar
slf4j-jdk14-1.5.5.jar
jcl-over-slf4j-1.5.5.jar
I want to write some tests against this library and see its log output, and I don't want to add any more logging libraries (no log4j or anything else).
I understand that SLF4J and Common Logging are both logging abstractions so I probably need to write my own simple concrete logger (or maybe not, since jcl-over-slf4j includes org.apache.commons.logging.impl.SimpleLog?). If so, what interfaces should I implement, and more importantly, how do I set up SL4J/Common Logging to use my logger in my test? I read in the SLF4J docs that I have to modify the StaticLoggerBinder class... does that really mean that I actually have to download SLF4J sources, modify the class and recompile it?
We use SLF4J. It's very useful but several of the jars have confusing names and it's not real clear starting out to know which ones are incompatible.
SLF4J is the API you use for logging in your code (e.g. log.info("blah"). However, SLF4J has no configuration aspect to it. At runtime you add exactly one jar to the classpath that binds the API to the 'real' logging subsystem. If you want to use Log4J, add slf4j-log4j.jar or the StaticBinder jar for Simple or JDK, or Logback. You configure any of these logging implementations as you normally would without SLF4J.
There are several SLF4J modules available to redirect existing logging statements written using the APIs of Log4J, Apache Commons Logging, and java.util.logging to SLF4J. This allows you to setup a single logging configuration for all these disperate implementations. (This is very useful to avoid configuring both, say, Log4J and JUL if you have libraries that bind directly to any legacy logging framework.)
The SLF4J legacy page explains these concepts in depth. Heck, there is even a module to redirect Sysout.out/err to SFL4J.
To more directly answer your question: sure you can write your own logging implementation to go under SLF4J; but the only reason to do so is because you are already locked into some homegrown craptastic logging framework.
If you want to keep it simple, use the built-in (as of jdk 1.4) logger
https://docs.oracle.com/en/java/javase/19/docs/api/java.logging/java/util/logging/Logger.html
the jdk binding jar comes with slf4j. you want to make sure the jar is deployed to your webapp WEB-INF/lib dir or just in your classpath otherwise. See (slf4j.org/faq.html#where_is_binding) and this (slf4j.org/faq.html) for more information.
the jar you want to add to your classpath is slf4j-jdk14.jar. Note that the jdk logger is already available, this jar is the link between the slf4j interfaces and the chosen logger implementation. the jdk logging binding jar comes with the slf4j distribution. this should do it for you.
Your question leds me to believe that you have not read the the SLF4J user manual. It's a very short document. If after reading the document you still have the same question, then the document fails its purpose and needs to be clarified.
Anyways, you don't need to implement your own Logger class. Just use slf4j-simple which ships with SLF4J.
So I've been porting some of our services to use Logback over log4j and I've been using the log4j-over-slf4j jar to spoof log4j for our legacy dependencies. The only issue is log4j-over-slf4j doesn't implement many of the classes of log4j so I'm seeing a decent few class not found exceptions from dependent jars. How have people got around this? Is it enough to just put the nlog4j in your classpath instead of the log4j-over-slf4j jar? I haven't tested this last idea yet but I'm planning on it tomorrow.
Reference:
http://www.slf4j.org/legacy.html
http://www.slf4j.org/nlog4j/
The only issue is log4j-over-slf4j doesn't implement many of the classes of log4j so I'm seeing a decent few class not found exceptions from dependent jars. How have people got around this?
Well, I guess you're facing "problems" explicitly mentioned in the Bridging legacy APIs link you posted:
When does it not work?
The log4j-over-slf4j module will not
work when the application calls log4j
components that are not present in the
bridge. For example, when application
code directly references log4j
appenders, filters or the
PropertyConfigurator, then
log4j-over-slf4j would be an
insufficient replacement for log4j.
However, when log4j is configured
through a configuration file, be it
log4j.properties or log4j.xml, the
log4j-over-slf4j module should just
work fine.
If the dependent JARs are under your control, you should really try to migrate the code using the SLF4J Migrator tool.
Is it enough to just put the nlog4j in your classpath instead of the log4j-over-slf4j jar?
This might solve the ClassNotFoundException but I wonder how the whole logging is going to work. And given that NLog4J is no longer actively developed, I wonder if this is a good long term solution.
I have an existing application which does all of its logging against log4j. We use a number of other libraries that either also use log4j, or log against Commons Logging, which ends up using log4j under the covers in our environment. One of our dependencies even logs against slf4j, which also works fine since it eventually delegates to log4j as well.
Now, I'd like to add ehcache to this application for some caching needs. Previous versions of ehcache used commons-logging, which would have worked perfectly in this scenario, but as of version 1.6-beta1 they have removed the dependency on commons-logging and replaced it with java.util.logging instead.
Not really being familiar with the built-in JDK logging available with java.util.logging, is there an easy way to have any log messages sent to JUL logged against log4j, so I can use my existing configuration and set up for any logging coming from ehcache?
Looking at the javadocs for JUL, it looks like I could set up a bunch of environment variables to change which LogManager implementation is used, and perhaps use that to wrap log4j Loggers in the JUL Logger class. Is this the correct approach?
Kind of ironic that a library's use of built-in JDK logging would cause such a headache when (most of) the rest of the world is using 3rd party libraries instead.
One approach I have used successfully is to use slf4j as my primary logging API. I then have slf4j bind to log4j. 3rd party dependencies using other frameworks (like JUL) can be bridged to slf4j.
We use SLF4J on our current project and it's worked very well for us. SLF4J is written by Ceki Gülcü, the creator of Log4J, and he's done a really great job. In our code we use the SLF4J logging APIs directly, and we configure SLF4J so that calls from the Jakarta Commons Logging (JCL), java.util.logging (JUL), and Log4J APIs are all bridged to the SLF4J APIs. We need to do that because like you we use third party (open source) libraries that have chosen different logging APIs.
On the bottom of SLF4J, you configure it to use a particular logger implementation. It comes with an internal, or "simple" logger, and you can override this with Log4J, JUL, or Logback. Configuration is all done simply by dropping in different jar files in your classpath.
Originally, we used the Logback implementation, also written by Ceki Gülcü. This is very powerful. However, we then decided to deploy our application to the Glassfish Java EE application server, whose log viewer expects JUL-formatted messages. So today I switched from Logback to JUL, and in just a few minutes I replaced two Logback jars with an SLF4J jar that connects it to the JUL implementation.
So like #overthink, I would heartily recommend using SLF4J in your setup.
There is a simpler alternative than SLF4J to bridge JUL with log4j, see http://people.apache.org/~psmith/logging.apache.org/sandbox/jul-log4j-bridge/examples.html
You just have to put the jul-log4j-bridge on the classpath and add a system property:
-Djava.util.logging.manager=org.apache.logging.julbridge.JULBridgeLogManager
jul-log4j-bridge is not in Maven Central and can be fetched from this repository:
<repository>
<id>psmith</id>
<url>http://people.apache.org/~psmith/logging.apache.org/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
and then used with:
<dependency>
<groupId>org.apache.logging</groupId>
<artifactId>apache-jul-log4j-bridge</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>apache-log4j-component</artifactId>
</exclusion>
</exclusions>
</dependency>
It's also possible to rebuild it from sources with the following steps:
svn co http://svn.apache.org/repos/asf/logging/sandbox/jul-to-log4j-bridge/
edit pom.xml, replace the dependency on log4j:log4j:1.2.15 with log4j:apache-log4j-extras:1.2.17 and remove the dependency on apache-log4j-component
mvn package
OCTOBER 2014
Since version 2.1 of log4j exists the component log4j-jul, which allows exactly this. Still, in case you are using log4j 1, it has to be possible to upgrade to log4j2 in order to use this approach.
JDK Logging Adapter
Class LogManager
Migrate from log4j 1.x to log4j 2
The slf4j site I believe has a bridge for passing java.util.logging events via slf4j (and hence to log4j).
Yes, the SLF4J download contains jul-to-slf4j which I believe does just that. It contains a JUL handler to pass records to SLF4J.
#Yishai - Thanks for posting the link to my wiki. The example there redirects JUL to Log4J and I've had it running in a production system for a few years. JBoss 5.x already redirects JUL to Log4J, so I took it out when we upgraded. I have a newer one that redirects to SLF4J, which I use on a few things now. I'll post that when I get a chance.
However, SLF4J already has it:
http://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j
you should manually add blew at startup
SLF4JBridgeHandler.removeHandlersForRootLogger()
SLF4JBridgeHandler.install()
demo -> https://gist.github.com/jiahut/654ecc75a13b0a1d8f3b4d5d2d69dc6d