tags:

views:

666

answers:

3

Introduction:

I'm trying to get additional fields to log with log4j, and its working but only when I create an appender in code and not in the log4j.properties

Progress:

  1. Used this article Adding Conversion Characters to PatternLayout for log4j 1.1.3
  2. Made a sample app for log4j 1.2

Problem:

using the properties file it will run but won't use AppServerPatternLayout so the custom fields aren't displayed.

Download Code

customlog.properties

log4j.rootLogger=FATAL
log4j.logger.some.log=INFO,stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=logging.AppServerPatternLayout
log4j.appender.stdout.layout.ConversionPattern=-----------------using log file------------------------%nTime:      %d%nHost:      %h%nServer:    %s%nComponent: %b%nVersion:   %v%nPriority:  %p%nThread Id: %t%nContext:   %x%nMessage:   %m%n

Main.java logging without a log4j properties file

AppServerLoggerFactory factory;
factory = new AppServerLoggerFactory("MyServer", "MyComponent", "1.0");
AppServerLogger.setFactory(factory);
Logger logger = AppServerLogger.getLogger("some.log");
PatternLayout layout = new AppServerPatternLayout( formatString );
logger.addAppender( new ConsoleAppender(layout) );
logger.info("Hello");

Main.java logging with a log4j properties file

PropertyConfigurator.configure("customlog.properties");
AppServerLoggerFactory factory;
factory = new AppServerLoggerFactory("MyServer", "MyComponent", "1.0");
AppServerLogger.setFactory(factory);
Logger logger = AppServerLogger.getLogger("some.log");
logger.info("Hello");

Expected output

----------------using in code appender----------------------
Time:      2009-11-06 12:55:05,785
Host:      M1330
Server:    MyServer
Component: MyComponent
Version:   1.0
Priority:  INFO
Thread Id: main
Context:   
Message:   logging config from code

Actual output

-----------------using log file------------------------
Time:      2009-11-06 12:56:17,983
Host:      
Server:    
Component: 
Version:   
Priority:  INFO
Thread Id: main
Context:   
Message:   logging config from customlog.properties

Solution

Using MDC you can add custom fields like

MDC.put("Version", versionName);
Logger log = LogManager.getLogger("some.log");        
log.info("Hello");

and pull it out in the log4j.properties with a UPPER case X

log4j.appender.stdout.layout.ConversionPattern=%X{Version}
+2  A: 

Can it be that when you load via properties the AppServerPatternLayout is instantiated before the AppServerLoggerFactory is created? If you pick up the values of your custom fields at creation as opposed to at first use that could be an explanation.

rsp
AppServerPatternLayour doesn't have a dependency on AppServerLoggerFactory. All its doing is creating a AppServerPatternParser which is then checking if its getting an instanceof AppServerLoggingEvent using its properties
Scott Cowan
@Scott, Your logger has a default value for the static factory with `null`'s as values. If the event is created from that factory the values will be empty. You can prove/disprove by moving the `.configure()` after you instantiate your custom factory. I beleive what you see is a race condition between creation of your factory and the event object.
rsp
you may be right, when I switched back to a PatternLayout in the properties it showed the %h in the log. so it must have been loading.
Scott Cowan
+2  A: 

From the example you posted, I can only guess that AppServerPatternLayout is not in the package logging. Everything else looks find. Add

log4j.DEBUG=true

to your properties file. log4j will then dump what it does while reading the properties. Maybe that gives you an idea what's wrong.

If that doesn't help, consider to use Nested Diagnostic Contexts.

Aaron Digulla
looking at his github link, it is in the package: http://github.com/scottcowan/spikes/blob/master/log4j/CustomFields/src/logging/AppServerPatternLayout.java
matt b
ya the package seems ok, it looks like MDC is the way to go, just trying to get it to work
Scott Cowan
+2  A: 

I'm not sure that you will achieve this with properties configuration, but I think you can do this with XML, which gives you a lot of customization options:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
  xmlns:log4j="http://jakarta.apache.org/log4j/"
  debug="true"
  reset="true"
>
  <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out"/>
    <layout class="your.package.AppServerPatternLayout">
      <param name="ConversionPattern" value="YOUR CONVERSION PATTERN"/>
    </layout>
  </appender>

  <root>
    <level value="info"/>
    <appender-ref ref="stdout" />
  </root>

  <loggerFactory class="your.package.AppServerLoggerFactory">
    <param name="server" value="MyServer"/>
    <param name="component" value="MyComponent"/>
    <param name="version" value="1.0r"/>
  </loggerFactory>

</log4j:configuration>

You will need to define setters on AppServerLoggerFactory for server, component and version. Also, read log4j.dtd on general layout of xml configuration file.

Save this file as log4j.xml and define -Dlog4j.configuration=/path/to/log4j.xml in your startup script, or replace log4j.properties with log4j.xml in your web-app.

I've never tried a custom LoggerFactory before, but it may be that because it is installed by the configurator you can use standard getLogger factory calls, e.g.

//Logger logger = AppServerLogger.getLogger("some.log");
Logger logger = Logger.getLogger("some.log");
Alexander Pogrebnyak