Logging in Cocoon 2.2

I’ve had to try and understand logging in Cocoon 2.2 for a project at work recently. It’s been “interesting,” so I thought I’d blog the process in case anybody else needs to o this…

Normally, logging in Java is quite simple: you add log4j to your classpath, then create a log4j.properties to say what gets logged. If you’re running as part of a webapp, you use something like Spring’s Log4jConfigListener to ensure that the configuration gets applied as soon as the webapp is active.

Cocoon is different. By default (in development) you run it using a combination of the cocoon-maven-plugin and mvn jetty:run. This is quite cunning (as Grzegorz explained in a comment a while back), it lets you edit all sorts of stuff and have it dynamically reloaded. In order to make the cocoon “block” work with jetty, the maven plugin creates things like web.xml for you automatically, so there’s no chance to edit things. Drat.

Now, if you follow the documentation for logging in Cocoon, it advises:

The usual Cocoon web application sets up Log4j through the Cocoon Spring Configurator.

Lovely advice, except that by the time Spring has started up, read your logging configuration and applied it, a great deal of interesting events have already occurred. You really need to enable logging as early as possible using a ServletContextListener.

Thankfully, it’s possible to do so, even when using mvn jetty:run.

First, you need to create a log4j configuration that’s suitable. I think it has to be XML, which is a shame as it’s more complicated than the properties file. I wanted to change the defaults to get my FlowScript calls to log to the console. This is what I ended up with in etc/log4j.xml.

  <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
      <param name="target" value="System.err"/>
      <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %c{2} %p - %m%n"/>
      </layout>
    </appender>
    <logger name="cocoon">
      <level value="INFO" />
    </logger>
    <logger name="org.apache.cocoon.components.flow.javascript.fom">
      <level value="INFO" />
    </logger>
    <root>
      <priority value="WARN"/>
      <appender-ref ref="stdout"/>
    </root>
  </log4j:configuration>

Note that we shut everything up (WARN) by default, and then explicitly enable messages for things we want to see (INFO). I’ve found that even in development, this helps to tell the wood from the trees.

With that in place, you have to edit pom.xml in order to tell the cocoon-maven-plugin to use that instead if its default. The default pom should have a build/plugins/plugin section, to which this stanza needs adding.

  <configuration>
    <!-- Gets copied to target/.../WEB-INF/log4j.xml -->
    <customLog4jXconf>etc/log4j.xml</customLog4jXconf>
  </configuration>

Finally, you need to arrange for the auto-generated web.xml to be patched with a reference to Log4jConfigListener. This is done through Cocoon’s slightly arcane mechanism, xpatch. Create a file src/main/resources/META-INF/cocoon/xpatch/log4j.xweb which looks like this.

  <xweb xpath="/web-app"
        unless="comment()[contains(., 'Log4j Configuration')]"
        insert-after="node()[1]">
    <!--Log4j Configuration-->
    <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>/WEB-INF/log4j.xml</param-value>
    </context-param>
    <listener>
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
  </xweb>

Now if you run mvn jetty:run in your block and inspect the generated web.xml, you should see the above patched in to place. Also, you should be able to generate messages on the console from within FlowScript by doing:

  cocoon.log.info("hello world");

The procedure above is a hassle. But the benefit of being able to see logging messages coming out on the console in front of you is significant.

One final point to note. When you do run mvn jetty:run, you’ll see a few log4j errors, i.e.

log4j:WARN No appenders could be found for logger (org.apache.commons.configuration.ConfigurationUtils).
log4j:WARN Please initialize the log4j system properly.

log4j:WARN No appenders could be found for logger (org.apache.commons.jci.stores.MemoryResourceStore).
log4j:WARN Please initialize the log4j system properly.

As far as I can tell these are completely ignorable, just very annoying. They appear to happen before jetty itself starts up, and are irrelevant to the web app (as far as I can see).

4 Comments to Logging in Cocoon 2.2

  1. dom says:

    @Andreas Hartmann Excellent — I’m glad that works for you. Apart from the log4j.xml. I much prefer log4j.properties. 🙂

  2. Andreas Hartmann says:

    The following configuration for the surefire plugin is needed to make the log4j configuration work with the tests:

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
              <systemProperties>
                <property>
                  <name>log4j.configuration</name>
                  <value>file://${project.basedir}/etc/log4j.xml</value>
                </property>
              </systemProperties>
            </configuration>
          </plugin>
  3. Arsène says:

    Inside NetBeans, as maven is launched from user.home not the project dir, you’d better use an absolute path ${project.build.directory}/../etc/log4j.xml or ${project.build.directory}/../src/main/resources/WEB-INF/log4j.xml (my choice).

  4. dom says:

    Mr. Haki has a nice post on adding request logging. The post targets cocoon but is applicable to any maven / jetty setup.