07 June 2011

Using SLF4J with WebLogic Server Logging

Just recently, I needed to take a look at the SLF4J (Simple Logging Facade 4 Java) API, and more specifically, to how it could be employed on WebLogic Server.

Simple Logging Facade for Java


SLF4J follows the Commons-Logging model in that it provides a "Simple Logging Facade" with an API that can be used to insert log messages into application code, without specifying the specific Logging framework that is to be used. The actual work of capturing and emitting the log message is handled by "pluggable" SLF4J binding, which are added at deployment/runtime time (or server start) and handle the actual logging implementation work. There are pre-built bindings for the common logging frameworks such as JDK logging, Log4J, LogBack, as well as a simple Logging implementation that appears to simply print log messages to stdout.

Basic Problem

Having a look at a project that was using SLF4J with WebLogic Server, the question that came up was how could we direct the SLF4J logs into the standard WebLogic Server log facilities.

In it's basic form, with the slf4j-simple-1.6.0.jar binding being used, the SLF4J logs were not consistent with the rest of the WebLogic Server logs.

<Jun 7, 2011 3:22:04 PM CST> <Notice> <WebLogicServer> <BEA-000395> <Following extensions directory contents added to the end of the classpath:
<Jun 7, 2011 3:22:05 PM CST> <Info> <WebLogicServer> <BEA-000377> <Starting WebLogic Server with Java HotSpot(TM) 64-Bit Server VM Version 19.1-b02-334 from Apple Inc.>
<Jun 7, 2011 3:22:05 PM CST> <Info> <Management> <BEA-141107> <Version: WebLogic Server Fri Apr 1 20:20:06 PDT 2011 1398638 >
. . .
. . .
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000331> <Started WebLogic Admin Server "myserver" for domain "mydomain" running in Development Mode>
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING>
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] INFO com.oracle.demo.wlsslf4j.TestServlet - *** This is an info: class com.oracle.demo.wlsslf4j.TestServlet
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN com.oracle.demo.wlsslf4j.TestServlet - *** This is a warning: class com.oracle.demo.wlsslf4j.TestServlet
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] ERROR com.oracle.demo.wlsslf4j.TestServlet - *** This is an error: class com.oracle.demo.wlsslf4j.TestServlet

The question was: without writing a specific WLS binding, is there a way to enable the SLF4J logs to be directed into the WebLogic Server logs so they are consistently formatted and able to be viewed in the WebLogic Server console with the rest of the logs?

One Solution

The solution was quite straightforward in the end, simply by combining two pieces of existing functionality:

1. The SLF4J distribution ships with a binding for JDK logging called slf4j-jdk14-1.6.0.jar. This results in the SLf4J logs being directed into the JDK logging framework.

2. WebLogic Server ships with a JDK logging handler which will pick up log messages emitted from JDK logging framework and direct them into the WebLogic Server logging system.

The weblogic.logging.ServerLoggingHandler is documented here: http://download.oracle.com/docs/cd/E14571_01/web.1111/e13739/logging_services.htm#CHDBBEIJ

To get it all working, all it required was making the relevant libraries available to WebLogic Server and wiring them together using a logging.properties file.

Copy Libraries into WebLogic Server

SLF4J basically comes as two components. There's the API which is used by applications, and then there's the bridgings that handle the actual production of log messages into the desired form.

To make SLF4J available to WebLogic Server, I employed the simplest way I know - copy them into the domain/lib directory. This has a certain set of limitations in terms of updating versions without a server restart, etc. but it's a really simple way to get things working in the first instance.

sbutton:~/Projects/Domains/wls1035 $ ls -l lib/
total 88
-rw-r--r-- 1 sbutton staff 702 6 Jun 13:15 readme.txt
-rw-r--r-- 1 sbutton staff 25496 7 Jun 15:19 slf4j-api-1.6.0.jar
-rw-r--r-- 1 sbutton staff 8887 7 Jun 15:34 slf4j-jdk14-1.6.0.jar

Create a logging.properties file

The logging.properties file is a standard way to control JDK logging. Since we have directed SLf4J logs into JDK logging framework by making the slf4j-jdk14-1.6.0 binding available, we can configure JDK logging to use the ServerLoggingHandler provided by WebLogic Server, so as to direct its logs into the WebLogic Server logging system.

The JDK logging framework and its configuration options are all well documented, starting from Nov 2001 here: http://download.oracle.com/javase/1.4.2/docs/guide/util/logging/overview.html

Here is the logging.properties file that was used:
# Specify the handlers to create in the root logger
handlers = weblogic.logging.ServerLoggingHandler

# Register handlers for the com.oracle.demo package and its child loggers
com.oracle.demo.handlers = weblogic.logging.ServerLoggingHandler

# Do not send the org.hibernate.validator log messages to the root handler
com.oracle.demo.useParentHandlers = false

# Set the default logging level for the root logger
.level = ALL
com.oracle.demo.level = ALL

# Set the default logging level for new ServerLoggingHandler instances
weblogic.logging.ServerLoggingHandler.level = ALL

To direct the JDK logging framework to use the logging.properties file, the standard System property java.util.logging.config.file is used. With WebLogic Server, this can be easily accomplished by setting the JAVA_OPTIONS System property with the corresponding value.

$ export JAVA_OPTIONS="-Djava.util.logging.config.file=/Users/sbutton/Projects/Domains/wls1035/logging.properties"

Start WebLogic Server and Observe

With the libraries copied into the domain/lib directory, the logging.properties file created and specified as $JAVA_OPTIONS, starting WebLogic Server should now allow the logs created via SLF4J to be directed into the WebLogic Server logging framework. This should let them be seen on stdout in a consistent form with the standard WebLogic Server log messages, as well as via the WebLogic Server console through it's log viewer.
SL4J logs in stdout
<Jun 7, 2011 3:52:01 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING>
<Jun 7, 2011 3:52:01 PM CST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
<Jun 7, 2011 3:52:07 PM CST> <Warning>
<com.oracle.demo.wlsslf4j.TestServlet> <BEA-000000> <*** This is a warning: class com.oracle.demo.wlsslf4j.TestServlet>
<Jun 7, 2011 3:52:07 PM CST> <Error> <com.oracle.demo.wlsslf4j.TestServlet> <BEA-000000> <*** This is an error: class com.oracle.demo.wlsslf4j.TestServlet>

SL4J logs in WebLogic Server console


wprager said...

Too bad you didn't write this blog, oh, about a year ago. I ended up using log4j because it gave us more felxibility for configuration. The configuration is very similar, but what we found out was that WL includes an older version of log4j. When you launch the console web app, it must have used this older log4j.jar, resulting in exceptions when using the ServerLoggingAppender. Initially our solution was to include the log4j.properties file as a jar in the domain/lib folder (the order in which log4j looks for its configuration file located the one included with the console web app before the one in lib). Eventually we went with an admin/managed setup and left the admin server (where the console app deploys) using java.util.logging.

tim vissers said...

For all you weblogic 12c users. Since weblogic 12c slf4j binding for jdk is loaded as a module at server startup. This will make it easier for you if you're trying to use the standard weblogic server log facilities.

But, it will make your life harder if you want another slf4j binding to be used, which is what I'm trying :)

Anonymous said...

I've got some pain with the WLS 12. If you deploy an EAR, this might help:
place a file weblogic-application.xml into the folder META-INF with this content:

<!DOCTYPE weblogic-application PUBLIC '-//BEA Systems, Inc.//DTD WebLogic Application 8.1.0//EN' 'http://www.bea.com/servers/wls810/dtd/weblogic-application_2_0.dtd'>

This tells the Weblogic Server to resolve every reference to the package org.slf4j with the deployed application prior to its own libraries.