Wednesday, November 7, 2007

How to setup Java Logging in OC4J

by Eduardo Rodrigues

Sometime ago I had a great idea for a web application logger that would basically log events as a feed (RSS or ATOM). After a lot of thinking of the best way to implement my idea, I decided to follow the Java Logging Framework. So, basically, all I had to do was to create my own FeedHandler extending java.util.logging.Handler and also create a new java.util.logging.Formatter extension for each specific standard feed format. I started implementing RSS20Formatter based on RSS 2.0 specification.

It took only 2 days for me to be completely satisfied with my own little Java Feed Logging library (which came to work really well, by the way) but then I had to make it work with my J2EE applications running in OC4J 10.1.3.x. And I wondered what would be the best way to do that.

I had read OC4J’s documentation on logging and saw that it is possible to define a log handler in OC4J’s logging configuration file j2ee-logging.xml declaring its class as a subclass of java.util.logging.Handler, but that’s all the information available on how to configure OC4J loggers using the Java Logging Framework because the focus seems to be on the Oracle Diagnostic Logging Framework instead.

I already use (and recommend) the ODL Framework in my J2EE applications running in OC4J mainly for its integration with web-based Oracle Enterprise Manager 10g Application Server Control Console which turns log viewing and analysis into a much more easy and comfortable experience. So, my first attempt was to add a new <log_handler> to <log_handlers> element in my j2ee-logging.xml file, basically copying my existing <log_handler> and changing its attributes and properties like that:

Original j2ee-logging.xml file

<?xml version = '1.0' encoding = 'iso-8859-1'?>
<logging_configuration>
   <log_handlers>
      <log_handler name="contaweb-handler"
                   class="oracle.core.ojdl.logging.ODLHandlerFactory"
                   formatter="oracle.core.ojdl.logging.ODLTextFormatter">
         <property name="path"
                   value="../application-deployments/log/ContaWeb"/>
         <property name="maxFileSize" value="10485760"/>
         <property name="maxLogSize" value="104857600"/>
         <property name="encoding" value="ISO-8859-1"/>
         <property name="useSourceClassAndMethod" value="true"/>
         <property name="supplementalAttributes"
                   value="J2EE_APP.name,J2EE_MODULE.name"/>
      </log_handler>
   </log_handlers>
   <loggers>
      <logger name="tim.contaweb" level="ALL" useParentHandlers="false">
         <handler name="contaweb-handler"/>
      </logger>
   </loggers>
</logging_configuration>


New j2ee-logging.xml file

<?xml version = '1.0' encoding = 'iso-8859-1'?>
<logging_configuration>
   <log_handlers>
      <log_handler name="contaweb-handler"
                   class="oracle.core.ojdl.logging.ODLHandlerFactory"
                   formatter="oracle.core.ojdl.logging.ODLTextFormatter">
         <property name="path"
                   value="../application-deployments/log/ContaWeb"/>
         <property name="maxFileSize" value="10485760"/>
         <property name="maxLogSize" value="104857600"/>
         <property name="encoding" value="ISO-8859-1"/>
         <property name="useSourceClassAndMethod" value="true"/>
         <property name="supplementalAttributes"
                   value="J2EE_APP.name,J2EE_MODULE.name"/>
      </log_handler>
      <log_handler name="contaweb-rss-handler"
                   class="oracle.br.logging.feed.FeedHandler"
                   formatter="oracle.br.logging.feed.RSS20Formatter">
         <property name="path" value="../applications/ContaWeb/ContaWeb/rss"/>
         <property name="level" value="INFO"/>
         <property name="title" value="ContaWeb Log Events"/>
         <property name="link" value="http://localhost:8888/em/"/>
         <property name="limit" value="100"/>
      </log_handler>

   </log_handlers>
   <loggers>
      <logger name="tim.contaweb" level="ALL" useParentHandlers="false">
         <handler name="contaweb-handler"/>
         <handler name="contaweb-rss-handler"/>
      </logger>
   </loggers>
</logging_configuration>


Note that I just added my new log handler to my existing logger named “tim.contaweb” which was already used by my application. In other words, every single log event generated by this logger will be directed to both handlers in a transparent way. The problem was it didn’t work at all :((((

Making the long story short, after googling a lot, I first found that the <property> elements and the “formatter” attribute in a <log_handler> are effective only if its class is oracle.core.ojdl.logging.ODLHandlerFactory. Then I also found that my answer was inside the ODL JAR file (ojdl.jar): the logging configuration DTD/Schema.

If you look inside ojdl.jar (which is generally located at $ORACLE_HOME/diagnostics/lib) you’ll find files oracle.core.ojdl.logging.logging-config.dtd and oracle.core.ojdl.logging.logging-config.xsd and, after examining them, you’ll notice the non-documented element <logging_properties> which is the first optional element inside the root element <logging_configuration>. In fact, this <logging_properties> only takes <property name=”” value””/> elements and has the exact same behavior as those properties declared in file $JAVA_HOME/jre/lib/logging.properties, and those are the ones needed by Java Logging Framework. So, my working j2ee-logging.xml file is:

<?xml version = '1.0' encoding = 'iso-8859-1'?>
<logging_configuration>
   <logging_properties>
      <property name="oracle.br.logging.feed.FeedHandler.formatter"
                value="oracle.br.logging.feed.RSS20Formatter"/>
      <property name="oracle.br.logging.feed.FeedHandler.path"
                value="../applications/ContaWeb/ContaWeb/rss"/>
      <property name="oracle.br.logging.feed.FeedHandler.level"
                value="INFO"/>
      <property name="oracle.br.logging.feed.FeedHandler.title"
                value="ContaWeb Log Events"/>
      <property name="oracle.br.logging.feed.FeedHandler.link"
                value="http://localhost:8888/em/"/>
      <property name="oracle.br.logging.feed.FeedHandler.limit"
                value="100"/>
   </logging_properties>

   <log_handlers>
      <log_handler name="contaweb-handler"
                   class="oracle.core.ojdl.logging.ODLHandlerFactory"
                   formatter="oracle.core.ojdl.logging.ODLTextFormatter">
         <property name="path"
                   value="../application-deployments/log/ContaWeb"/>
         <property name="maxFileSize" value="10485760"/>
         <property name="maxLogSize" value="104857600"/>
         <property name="encoding" value="ISO-8859-1"/>
         <property name="useSourceClassAndMethod" value="true"/>
         <property name="supplementalAttributes"
                   value="J2EE_APP.name,J2EE_MODULE.name"/>
      </log_handler>
      <log_handler name="contaweb-rss-handler"
                   class="oracle.br.logging.feed.FeedHandler"/>

   </log_handlers>
   <loggers>
      <logger name="tim.contaweb" level="ALL" useParentHandlers="false">
         <handler name="contaweb-handler"/>
         <handler name="contaweb-rss-handler"/>
      </logger>
   </loggers>
</logging_configuration>


It’s also very important to say that all log handlers declared in file j2ee-loggin.xml will be automatically instantiated during OC4J’s startup process. Because of this, all needed classes must be reachable from OC4J’s boot class loader. There were some options to achieve that but, in my case, the easiest was to deploy my new logging library to a JAR file and just put it in $ORACLE_HOME/j2ee/home/lib/ext directory.

That’s all for now. I hope it’s useful.