Showing posts with label slf4j. Show all posts
Showing posts with label slf4j. Show all posts

23 November 2012

Logging with SLF4J and Logback in Tomcat and TomEE

Logging in Java would be a lot easier if every open source project out there would use SLF4J logging API, but of course this will never happen.

The fact that java.util.logging made it into the JRE does not make it any better than it is, so there's a good reason to hide it behind a facade and never use it directly. But there is no reason for everyone to invent their own logging facades instead of simply using SLF4J.

In my own applications, I always use SLF4J with Logback. Working with Tomcat, this means I'm getting an ugly mixture of log messages in different formats from my web application logging to System.out with Logback, and from Tomcat itself logging to System.err via JULI and java.util.logging, not to mention any third-party libraries contained in my application which use log4j, Apache Commons Logging or whatever.

There are two independent but similar approaches of replacing the official Tomcat JULI libraries by an SLF4J or Logback adapter: tomcat-slf4j and tomcat-slf-logback.

I've tried both, but in the end, I think it's easier to use nothing but the JUL-to-SLF4J bridge and other standard artifacts.

18 December 2010

Glassfish Logging with slf4j (Part 2)

My previous post on the same subject is one of the most popular articles in this blog, and since Glassfish still does not officially support slf4j, this update may be helpful to anyone hitting my blog via Google trying to improve their logging experience with Glassfish.

To redirect (almost) all log messages from Glassfish and your applications to the same log file and/or console, follow this recipe:
  • Copy these JARs to [install-root]/lib/endorsed:
    • jul-to-slf4j.jar
    • slf4j-api.jar
    • logback-classic.jar
    • logback-core.jar
  • Build a JAR containing your logback.xml configuration at root level and put it in the same place. [Update 26 Jul 2011: Actually, this is no longer required: Simply create a logback configuration file somewhere in your local file system and add the system property -Dlogback.configurationFile=file:/path/to/my/logback.xml to your Glassfish configuration.]
  • Define a new system property in the jvm-options section of your domain.xml:
    -Djava.util.logging.config.file=${com.sun.aas.instanceRoot}/config/my_logging.properties
  • Create the corresponding file my_logging.properties with the following contents:
  • handlers = org.slf4j.bridge.SLF4JBridgeHandler
    com.sun.enterprise.server.logging.GFFileHandler.flushFrequency=1
    com.sun.enterprise.server.logging.GFFileHandler.file=${com.sun.aas.instanceRoot}/logs/server.log
    com.sun.enterprise.server.logging.GFFileHandler.rotationTimelimitInMinutes=0
    com.sun.enterprise.server.logging.GFFileHandler.logtoConsole=false
    com.sun.enterprise.server.logging.GFFileHandler.rotationLimitInBytes=2000000
    com.sun.enterprise.server.logging.GFFileHandler.alarms=false
    com.sun.enterprise.server.logging.GFFileHandler.formatter=com.sun.enterprise.server.logging.UniformLogFormatter
    com.sun.enterprise.server.logging.GFFileHandler.retainErrorsStasticsForHours=0
    
When you restart Glassfish, you should only see two log messages in the old java.util.logging format, all the rest goes via slf4j and logback to the appenders configured in your logback.xml. The two remaining log messages are issued before Glassfish reads the domain.xml configuration with the adapted configuration, so there is no easy way of avoiding them.

I have been using this approach on various Glassfish versions from 3.0.1 to 3.1-b33. It is rather ugly, but nonetheless effective.

All this may sound rather mysterious, so I'd better add some explanations:

jul-to-slf4j.jar is a bridge for redirecting java.util.logging via the slf4j API to any logging backend of your choice (logback in this case). To make the redirection work, the bridge has to be visible to the classloader evaluating the logging configuration. Since Glassfish starts logging very early in its bootstrapping phase, the lib/endorsed folder is the only place where the bridge has the desired effect.

Unfortunately, class file folders seem to be unsupported for endorsed libraries. Of course, it would be easier to simply put the logback.xml file in lib/endorsed/classes where you can directly edit it. But for a quick change to your logging configuration, you can always use an editor which supports editing ZIP/JAR file contents in place.

I created the contents of my_logging.properties by trial and error, starting with a one-liner containing only the handler. This caused a couple of exceptions as Glassfish seemed to be missing the GFFileHandler settings, so I copied them over from the original logging.properties.

The same procedure should also work for other slf4j backends, so you could replace the logback JARs by a log4j.jar.

13 May 2010

Glassfish Logging with slf4j

Update 18 Dec 2010: There is an extended version of this article.

It can be annoying to have each third-party library or framework in your application logging to a different logfile. slf4j with its various adapters enables you to collect logging events from various APIs (org.slf4j.api, java.util.logging, org.apache.commons.logging) and redirect them to a logging backend of your choice.

I've always preferred log4j or, more recently, logback over java.util.logging, so after working with Glassfish v3 for a while, I tried to tweak it to use logback over slf4j and the jul-to-slf4j bridge.

To redirect java.util.logging used by Glassfish v3, you put the following libs on your classpath:
  • jul-to-slf4j.jar
  • slf4j-api.jar
  • logback-classic.jar
  • logback-core.jar
(You can replace the logback libs by any other supported backend like log4j, see the slf4j docs for more details.)
The problem with Glassfish is:
  • You need to take care of its class loader hierarchy and make sure that the logging jars get picked up early enough.
  • Glassfish does some all-too-clever logger manipulation in its LogManagerService which will get in your way if you don't like the Glassfish defaults: It redirects all System.out messages to a logger.
I did not manage to fully supersede Glassfish logging by slf4j and logback, but at least I can copy all Glassfish log events to my logback application log like this:
  • Put the logging libs in [install-root]/lib/endorsed.
  • Build a JAR containing your logback.xml configuration and put it in the same place.
  • Edit an entry in [instance-root]/config/logging.properties, setting
    handlers = org.slf4j.bridge.SLF4JBridgeHandler
Now if you omit a ConsoleAppender from your logging configuration, the Glassfish server.log will no longer contain any log output from your application. It will still contain the log messages from Glassfish itself, but they will also be included in your own logback logfile.