<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml-stylesheet type="text/xsl" href="blog.xsl"?> <article> <title>Logging with BFO</title> <subtitle/> <excerpt>The release of 2.11.6 of the BFO PDF library sees the introduction of a consolidated logging API that can be integrated into customer's existing logging systems. Read on to find out how to configure BFO products to log with Log4J or java.util.logging.</excerpt> <time>2009-07-07T15:38:31</time> <author>jim</author> <category>pdf</category> <tags>logging log4j</tags> <body><p> The release of 2.11.6 of the BFO PDF library sees the introduction of a consolidated logging API that can be integrated into customer's existing logging systems. </p> <h3>Background</h3> <p> Prior to 2.11.6 logging from the BFO PDF library would simply write warnings (and debug, if enabled) to <code>System.err</code>. When things were running smoothly this would generate little or no output, but dodgy PDFs could throw a large number of messages, and although these could always be turned off, there was no way to manage the logging in more detail. </p> <h3>The new Logging API</h3> <p> Fixing this required some flexibility. Some of our API's still run on Java 1.3, so we can't rely on <a href="http://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html" rel="nofollow">java.util.logging</a>, and some customers prefer to use <a href="http://logging.apache.org/log4j/1.2/index.html" rel="nofollow">Log4j</a> instead. Apache Commons logging is <a href="http://www.qos.ch/logging/thinkAgain.jsp" rel="nofollow">broken by design</a>, so we've written our own wrapper which will log to <code>Log4j</code> if it's available and configured, falling back to <code>java.util.logging</code> if it's availabe or <code>System.err</code> if it's not. </p> <h3>Logging with Log4J</h3> <p> If Log4J is available and a <code>log4j.properties</code> file configured, BFO libraries will use it. If you've installed it we'll assume you know how to configure it, so we won't cover that here in too much detail - as a quick example, here's an example <code>log4j.properties</code> that would write output to the console. </p> <pre class="brush:plain">log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=\ %d{yyyy-MM-dd HH:mm:ss} %p %m %x%n log4j.rootLogger=debug, stdout </pre> <p> This would log all warnings, but in the event you want individual warning messages turned off or changed to a different level you can do that too. </p> <pre class="brush:plain">log4j.logger.org.faceless.pdf2.warning.PD6 = OFF log4j.logger.org.faceless.pdf2.warning.RD4 = FATAL</pre> <p> This would disable the "PD6" warnings and make RD4 warnings fatal. As you can see, all warnings from the PDF library wil be in the "org.faceless.pdf2" package, which makes them easy to redirect to a separate file if necessary. </p> <p> If you prefer to use the XML method for configuring Log4J, that will work too. The configuration file is called <code>log4j.xml</code> rather than <code>log4j.properties</code> - the equivalent of the above example is this:</p> <pre class="brush:xml"> <?xml version="1.0" encoding="UTF-8" ?> <!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="threshold" value="debug" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %p %m %x%n" /> </layout> </appender> <category name="org.faceless.pdf2.warning.PD6"> <priority value="off"/> </category> <category name="org.faceless.pdf2.warning.RD4"> <priority value="fatal"/> </category> <root> <priority value="debug" /> <appender-ref ref="stdout" /> </root> </log4j:configuration> </pre> <h3>Logging with java.util.logging</h3> <p> The <code>java.util.logging</code> API was added in Java 1.4, although it has a number of problems by design which make this package next to useless out of the box. Some highlights are:</p> <ul> <li>Classloader wide configuration, which makes logging multiple threads impractical without extending.</li> <li>Really ugly default formatting of messages</li> <li>An inability to write to multiple files or change this formatting easily</li> </ul> <p> However, as it is supplied with Java 1.4 we need to work around these points. Customers deploying in a servlet environment will typically have two of these points fixed by the servlet engine. Apache Tomcat, for example, provides their own <a href="http://tomcat.apache.org/tomcat-6.0-doc/logging.html" rel="nofollow">JULI</a> logging framework which will build on the default, however configuration can be cryptic. </p><p> You can configure JULI logging in a couple of ways, but while getting started the simplest approach is to place a file called <code>logging.properties</code> in the <code>WEB-INF/classes</code> folder of your web application. Here's a simple example: </p> <pre class="brush:plain">handlers = org.apache.juli.FileHandler org.apache.juli.FileHandler.directory = ${catalina.base}/logs org.apache.juli.FileHandler.prefix = mywebapp. </pre> <p> As with Log4J, all messages are logged under the "org.faceless.pdf2" logger and you can turn logging for individual message on or off very easily by setting the appropriate levels in <code>logging.properties</code>. We've included a <a href="http://docs.oracle.com/javase/8/docs/api/java/util/logging/Formatter.html" rel="nofollow">Formatter</a> so you can also format the output in the same way as the Log4J <a href="http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html" rel="nofollow">PatternLayout</a> (you can download it <a viewtext="true" href="patternform.java">here</a>, it's public domain). </p> <pre class="brush:plain">org.faceless.pdf2.warning.F4 = OFF org.faceless.pdf2.warning.RD4 = SEVERE org.apache.juli.FileHandler.formatter = \ org.faceless.util.log.PatternFormatter org.faceless.util.log.PatternFormatter.format = \ %d{yyyy-MM-dd HH:mm:ss} %p %m %x%n </pre> <p> This will turn off F4 warnings, make RD4 warnings fatal, and cause the remaining warning message to be formatted to fit on one line. </p> <p> That's fine if you're logging from within Tomcat, but what about if you're running a regular application from the command line? Here's an example showing how you might see a warning message from this: </p> <pre class="brush:plain; highlight:1">java org.faceless.pdf2.viewer2.PDFViewer corrupt.pdf WARNING RD6: Stream 4/0 is 8 bytes too long - PDF may be corrupt WARNING RD6: Stream 9/0 is 8 bytes too long - PDF may be corrupt </pre> <p> Although not obvious, these messages are logged using <code>java.util.logging</code> as well: by default if no custom configuration is found, we install our own <code>PatternFormatter</code> to format messages onto one line. </p><p> If you want more control over the logging, you can set up your own <code>logging.properties</code>. Here's an example that would log all <code>Token</code> debug entries to a file:</p> <pre class="brush:plain">java.util.logging.FileHandler.formatter = org.faceless.util.log.PatternFormatter java.util.logging.FileHandler.pattern = bfodebug.log org.faceless.util.log.PatternFormatter.format = %m%n org.faceless.pdf2.level = ALL org.faceless.pdf2.debug.Token = FINE org.faceless.pdf2.handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler org.faceless.pdf2.useParentHandlers = false </pre> <p>Save that file and make sure it's passed into Java</p> <pre class="brush:plain">java -Djava.util.logging.config.file=logging.properties ...</pre> <p> <h3>Logging without a Logger</h3> For customers still on Java 1.3, messages are sent to <code>System.err</code>, and you can turn messages on or off using System properties: </p><pre class="brush:plain"> java -Dorg.faceless.pdf2.warning.RD4=off ...</pre> <h3>The warning codes</h3> <p> Full details of the <code>org.faceless.pdf</code> system properties and warning messages can be found in Appendices A & B of the PDF Library <a href="/products/pdf/docs/userguide.pdf">user guide</a>. </p> </body> <include xmlns="http://www.w3.org/2001/XInclude" href="comments.xml"/> </article>