Mapped Diagnostic Context (MDC) in Weblogic 10.3.5

original post

##Problem: Mapped Diagnostic Context (MDC) values aren’t available in Weblogic 10.3.5 Logs

I am developing a web application that is deployed on Weblogic 10.3.5 and I am using SLF4J for logging. I used the Mapped Diagnositc Context (MDC) feature of SLF4J to include the current user and the current remote IP for each log message.

Log message from logback showing MDC values (IP and UserName)
[2012-03-02 12:56:29,770] INFO  ii.green.phillip.ImportantClass - IP[127.0.0.1] - UserName[pdgreen] - Info Message

I use a servlet filter to set the MDC values and everything worked just fine when I used log4j or logback as the SLF4J implementation. While the logs look fine with a file appender, they don’t show up in the Weblogic logs when I use the console appender.

##Attempt 1: Switch to java.util.logging (JUL) for SLF4J implementation

By default, Weblogic is configured to log with JUL. So, the easiest change I could make to my web application was to switch the SLF4J from logback to JUL. After the change, all of my logs showed up in Weblogic. The problems is that JUL does not support MDC, so my logs weren’t showing the MDC values.

Log message from Weblogic using JUL showing message without MDC values
<Mar 3, 2012 7:56:29 PM EST> <Info> <Server> <BEA-000000> <Info Message>

##Attempt 2: Switch Weblogic to use log4j Weblogic can be configured to use JUL or log4j. Since log4j supports MDC, I was hoping this would fix my problem. I followed the steps to configure Weblogic to use log4j. I created the following log4j.xml:

####log4j.xml ```xml <!DOCTYPE log4j:configuration SYSTEM “log4j.dtd” >

I tried moving `log4j.xml` around, but was not able to find the appropriate location and I kept seeing the following error:

```text
Errors I was not able to fix with log4j configuration for weblogic
log4j:WARN No appenders could be found for logger (ii.green.phillip.ImportantClass).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

Due to the previous error, none of my log messages ever showed up in Weblogic.

##Attempt 3: Enable stdout and stderr logging in Weblogic While trying the previous attempts, I noticed the following tick boxes in Weblogic:

Weblogic Logging Configuration

They can be found at Environment -> Servers -> ${servername} -> Logging -> Advanced.

First, I reset everything back to the original configuration: Weblogic back to using JUL and my app using logback. Next, I enabled the Redirect stdout logging and Redirect stderr logging. Finally, I configured two console appenders in logback so that INFO messages are logged through stdout and anything WARN or above is logged through stderr. I left out the exact configuration because it didn’t work as I wanted it to. The logs did show up, just not quite right:

'Redirect stdout logging' and 'Redirect stderr logging' log everything at the same level
<Mar 1, 2012 4:58:54 PM EST> <Notice> <Stdout> <BEA-000000> <INFO: Info Message>
<Mar 1, 2012 4:58:55 PM EST> <Notice> <StdErr> <BEA-000000> <WARN: Warn Message>
<Mar 1, 2012 4:58:56 PM EST> <Notice> <StdErr> <BEA-000000> <ERROR: Error Message>

If you look at the logs I was seeing, everything is logged as “Notice”. I can distinguish stdout and stderr, but not by using levels. Having different levels is important because I am not the person monitoring the logs in Weblogic. They need to see ERROR/SEVERE or WARN/WARNING level messages. I disabled Redirect stdout logging and Redirect stderr logging and went back to the drawing board.

##Final Attempt: LogbackToJulAppender After the last attempt, I was more familiar with logback and realized that I could make a custom appender that would take my logback log messages and route them through JUL. This is different from using the JUL implemenation for SLF4J. What I needed to do was have logback create the message portion of the log before it is routed into JUL.

####ii.green.phillip.LogbackToJulAppender ```java package ii.green.phillip;

import ch.qos.logback.classic.jul.JULHelper; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.Layout; import java.util.logging.LogRecord;

/** * Logback Appender that outputs logs to java.util.Logging.

  • This is different from slf4j-jdk14-*.jar because the message generation
  • is handled by SLF4J so things like MDC are supported.
  • Code was augmented from org.slf4j.impl.JDK14LoggerAdapter *
  • @author pdgreen */ public class LogbackToJulAppender extends AppenderBase {

    private Layout layout;

    public Layout getLayout() { return layout; }

    public void setLayout(Layout layout) { this.layout = layout; }

    @Override public void start() { if (this.layout == null) { addError(“No layout set for the appender named [” + name + “].”); return; } super.start(); }

    @Override protected void append(ILoggingEvent eventObject) { final String message = layout.doLayout(eventObject);

    final LogRecord record = new LogRecord(JULHelper.asJULLevel(eventObject.getLevel()), message); record.setLoggerName(eventObject.getLoggerName()); record.setThrown(retrieveThrowable(eventObject.getThrowableProxy()));

    fillCallerData(eventObject, record);

    JULHelper.asJULLogger(eventObject.getLoggerName()).log(record); }

    Throwable retrieveThrowable(final IThrowableProxy iThrowableProxy) { if (iThrowableProxy instanceof ThrowableProxy) { return ((ThrowableProxy) iThrowableProxy).getThrowable(); } return null; }

    final private void fillCallerData(ILoggingEvent eventObject, LogRecord record) { final StackTraceElement[] steArray = eventObject.getCallerData(); if (steArray.length > 0) { //I think this is correct final StackTraceElement ste = steArray[0]; // setting the class name has the side effect of setting // the needToInferCaller variable to false. record.setSourceClassName(ste.getClassName()); record.setSourceMethodName(ste.getMethodName()); } } } ```

####logback.xml ```xml

webconsole.log webconsole.%d{yyyy-ww}.zip 6 ${DEFAULT_PATTERN} INFO ${WEBLOGIC_PATTERN}

My application now has two appenders. The first is a `RollingFileAppender` that logs everything through logback with all of its glory. The second is the custom appender that only redirect logs of level `INFO` and above into JUL for Weblogic. Now the application admins can see errors in the Weblogic console.

```text
Log message from Weblogic using JUL and LogbackToJulAppender showing message wit MDC values
<Mar 3, 2012 7:56:29 PM EST> <Info> <Server> <BEA-000000> <IP[127.0.0.1] - UserName[pdgreen] - Info Message>
Written on March 5, 2012