
Author: seb Date: Thu Feb 1 15:44:05 2007 New Revision: 1304 Added: logback/trunk/logback-site/src/site/resources/access.html logback/trunk/logback-site/src/site/resources/bridge.html logback/trunk/logback-site/src/site/resources/bugreport.html logback/trunk/logback-site/src/site/resources/codes.html logback/trunk/logback-site/src/site/resources/css/print.css logback/trunk/logback-site/src/site/resources/css/site.css logback/trunk/logback-site/src/site/resources/demo.html logback/trunk/logback-site/src/site/resources/documentation.html logback/trunk/logback-site/src/site/resources/download.html logback/trunk/logback-site/src/site/resources/faq.html logback/trunk/logback-site/src/site/resources/images/logos/ logback/trunk/logback-site/src/site/resources/images/logos/lblogo.jpg (contents, props changed) logback/trunk/logback-site/src/site/resources/images/logos/qosLogo.png (contents, props changed) logback/trunk/logback-site/src/site/resources/index.html logback/trunk/logback-site/src/site/resources/jmxConfig.html logback/trunk/logback-site/src/site/resources/license.html logback/trunk/logback-site/src/site/resources/mailinglist.html logback/trunk/logback-site/src/site/resources/manual/appenders.html logback/trunk/logback-site/src/site/resources/manual/architecture.html logback/trunk/logback-site/src/site/resources/manual/contextSelector.html logback/trunk/logback-site/src/site/resources/manual/filters.html logback/trunk/logback-site/src/site/resources/manual/index.html logback/trunk/logback-site/src/site/resources/manual/introduction.html logback/trunk/logback-site/src/site/resources/manual/joran.html logback/trunk/logback-site/src/site/resources/manual/layouts.html logback/trunk/logback-site/src/site/resources/manual/mdc.html logback/trunk/logback-site/src/site/resources/news.html logback/trunk/logback-site/src/site/resources/repos.html logback/trunk/logback-site/src/site/resources/setup.html logback/trunk/logback-site/src/site/resources/team.html logback/trunk/logback-site/src/site/resources/templates/ logback/trunk/logback-site/src/site/resources/templates/base/ logback/trunk/logback-site/src/site/resources/templates/base/footer.js logback/trunk/logback-site/src/site/resources/templates/base/header.js logback/trunk/logback-site/src/site/resources/templates/base/left.js logback/trunk/logback-site/src/site/resources/templates/base/right.js logback/trunk/logback-site/src/site/resources/templates/footer.js logback/trunk/logback-site/src/site/resources/templates/header.js logback/trunk/logback-site/src/site/resources/templates/left.js logback/trunk/logback-site/src/site/resources/templates/right.js Removed: logback/trunk/logback-site/src/site/xdocTemplates/ Modified: logback/trunk/logback-site/pom.xml logback/trunk/logback-site/src/site/site.xml logback/trunk/pom.xml logback/trunk/src/site/site.xml Log: Changed the site organisation. We now use html pages and not the xdoc anymore. Modified: logback/trunk/logback-site/pom.xml ============================================================================== --- logback/trunk/logback-site/pom.xml (original) +++ logback/trunk/logback-site/pom.xml Thu Feb 1 15:44:05 2007 @@ -25,12 +25,12 @@ <build> <resources> <resource> - <directory>src/site/xdocTemplates</directory> + <directory>src/site/resources</directory> <!-- We're saving filtered xdocs in a temporary folder and telling the site plug in to get the xdocs there. --> - <targetPath>generated-site</targetPath> + <targetPath>generated-site</targetPath> <filtering>true</filtering> </resource> </resources> Added: logback/trunk/logback-site/src/site/resources/access.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/access.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,443 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback Access</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + <h2>Access log with logback, Jetty and Tomcat</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a> + . + + </p> + </td> + </tr> + </table> + + + <h2>Introduction</h2> + + <p> + Logback was designed as a modular framework from the start. Being + able to use logback's internal core in many situations, without + heavy coding or complex specific configuration was one of our + goals. + </p> + + <p> + Lobgack access integrates with Servlet containers such as Jetty + and Tomcat to provide HTTP-access log functionality. + </p> + + <h2>Logback Access and Tomcat</h2> + + <p> + To use logback-access with Tomcat, after downlading the logback + distribution, place the files <em>logback-core-VERSION.jar</em> + and <em>logback-access-VERSION.jar</em> under $TOMCAT_HOME/server/lib + directory, where $TOMCAT_HOME is the folder where you have + installed Tomcat. We have tested logback-access module with Tomcat + version 5.5.20. + </p> + + <h3>LogbackValve</h3> + + <p> + The <a href="xref/ch/qos/logback/access/tomcat/LogbackValve.html"> + <code>ch.qos.logback.access.tomcat.LogbackValve</code></a> + class extends Tomcat's <code><a href="http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/catalina/valves/ValveBase.html"> + ValveBase</a></code> + class. This class is used to allow external + components to be integrated into Tomcat. + </p> + + <p> + To configure Tomcat in order to use + <code>LogbackValve</code>, add the following lines + to the tomcat server configuration file, namely <em>$TOMCAT_HOME/conf/server.xml</em>: + </p> + <div class="source"><pre><Valve className="ch.qos.logback.access.tomcat.LogbackValve"/></pre></div> + <p> + This line need to be nested in an <em>Engine</em> element. + </p> + <p> + By default, <code>LogbackValve</code> looks for a logback + configuration file called <em>logback-access.xml</em>, in the + same folder where <em>server.xml</em> is located, that is + in <em>$TOMCAT_HOME/conf/</em>. This + configuration file contains directives for configuring logback + components. Among others, you can specify the appenders where + the logging requests will be sent, and their format. Please refer + to the description below about logback access configuration for examples. + </p> + + <h2>Logback Access and Jetty</h2> + + <p> + To use logback-access with Jetty, after downlading the logback + distribution, place the files <em>logback-core-VERSION.jar</em> + and <em>logback-access-VERSION.jar</em> under $JETTY_HOME/lib + directory, where $JETTY_HOME is the folder where you have + installed Jetty. We have tested logback-access module with Jetty + version 6.0.1. + </p> + + <h3>Logback's RequestLog implementation</h3> + + <p> + The <a href="xref/ch/qos/logback/access/jetty/RequestLogImpl.html"> + <code>ch.qos.logback.access.jetty.RequestLogImpl</code></a> + class implements jetty's <code><a href="http://jetty.mortbay.org/apidocs/org/mortbay/jetty/RequestLog.html">RequestLog</a></code> + interface. This interface is used by Jetty to allow external + components to manage request logging. + </p> + + <p> + In logback, logging destinations are called Appenders. These classes + can be attached directly to <code>RequestLogImpl</code>. + </p> + + + <p> + To configure jetty in order to use logback's + <code>RequestLogImpl</code>, add the following lines + to the jetty configuration file, namely <em>$JETTY_HOME/etc/jetty.xml</em>: + </p> + <div class="source"><pre><Ref id="requestLog"> + <Set name="requestLog"> + <New id="requestLogImpl" + class="ch.qos.logback.access.jetty.RequestLogImpl"> + </New> + </Set> +</Ref></pre></div> + <p> + These lines reference the requestLog functionnality of Jetty, setting + the actual class that will be called at each logging request. + </p> + <p> + By default, <code>RequestLogImpl</code> looks for a logback + configuration file called <em>logback-access.xml</em>, in the + same folder where <em>jetty.xml</em> is located. This + configuration file contains directives for configuring logback + components. Among others, you can specify the appenders where + the logging requests will be sent, and their format. + </p> + + <p>As long the path is specified, you can place the logback + configuration file in any location. Here is another example of + jetty configuration file, with a path to the logback access + configuration file. + </p> + <div class="source"><pre><Ref id="requestLog"> + <Set name="requestLog"> + <New id="requestLogImpl" + class="ch.qos.logback.access.jetty.RequestLogImpl"> + </New> + <Set name="fileName">path/to/myaccess.xml</Set> + </Set> +</Ref></pre></div> + + <h2>Logback Access Configuration</h2> + + <p>Altough similar, the <em>logback-access.xml</em> file is slightly + different than the usual logback classic configuration file. + Appenders and Layouts are declared the exact same way. However, in + the access module there is no notion of loggers and consequently + loggers elements are disallowed in configuraiton files for + logback-access. + </p> + + <h3>Example 1: basic logback-access configuration</h3> + <p> + Here is a sample <em>logback-access.xml</em> file that you can + immediately put to use: + </p> +<div class="source"><pre><configuration> + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout + class="ch.qos.logback.access.PatternLayout"> + <Pattern>%h %l %u %user %date "%r" %s %b</Pattern> + </layout> + </appender> + + <appender-ref ref="STDOUT" /> +</configuration></pre></div> + <p> + It declares a <code>ConsoleAppender</code> which directs its + output at the console. The <code>ConsoleAppender</code> contains + a <code>PatternLayout</code> format the output. The log format is + specied using the %h %l %u %user %date "%r" %s %b" pattern which + is the Commong Log Format (CLF). This format is recognized by log + analysers such as <a href="http://www.analog.cx/">Analog</a> or <a href="http://awstats.sourceforge.net/">AWStats</a>. + </p> + + <p>Instead of specifying the complete pattern, the word "common" + or "clf" can be used as a shorthand. Thus, the following are all + equivalent + </p> + + <div class="source"><pre><Pattern>%h %l %u %user %date "%r" %s %b</Pattern> +<Pattern>common</Pattern> +<Pattern>clf</Pattern></pre></div> + + <p>The so called "combined" format is also widely recognized. It is + defined as the '%h %l %u %t "%r" %s %b "%i{Referer}" + "%i{User-Agent}"' pattern. As a facilitator, you can use the + "combined" as a shorthand. Thus, the following directive + </p> + + <div class="source"><pre><layout class="ch.qos.logback.access.PatternLayout"> + <Pattern>%h %l %u %t "%r" %s %b "%i{Referer}" "%i{User-Agent}"</Pattern> +</layout></pre></div> + + <p>is equivalent to:</p> + + <div class="source"><pre><layout class="ch.qos.logback.access.PatternLayout"> + <Pattern>combined</Pattern> +</layout></pre></div> + + + <h3>Example 2: RollingFileAppender</h3> + + <p>Another configuration file, using logback' + <code>RollingFileAppender</code>, could be:</p> +<div class="source"><pre><configuration> + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <File>access.log"</File> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern>access.%d{yyyy-MM-dd}.log.zip</FileNamePattern> + </rollingPolicy> + + <layout class="ch.qos.logback.access.PatternLayout"> + <Pattern">combined</Pattern"> + </layout> + </appender> + + <appender-ref ref="FILE" /> +</configuration></pre></div> + + <p> + Here, there is no output to the console. Instead, logback access + logs to the file named access.log. This file will be rolled over + every 24 hours. We specify in the configuration the name of the file + where the actual logging is added, and the pattern that the archived + files must match. + The newly archived file will be automatically compressed. + </p> + + <p> + These two configuration examples should give you an idea of the + possibilities offered by the logback-access module. In + principle, most of the things that you can do with + logback-classic module are equally possible with logback-access. + </p> + + <h3>PatternLayout</h3> + + <p> + An http-specific implementation of <code>PatternLayout</code> is + included in the access module. The <a href="xref/ch/qos/logback/access/PatternLayout.html"> + <code>ch.qos.logback.access.PatternLayout</code></a> provides a + way to format the logging output that is just as easy and + flexible as the <code>PatternLayout</code> found in logback + classic. + </p> + + <p> + For detailled instructions on how to use the <code>PatternLayout</code> for + logback access, please refer to the + <a href="manual/layouts.html#AccessPatternLayout">corresponding chapter</a> + of the logback manual. + </p> + + <h2>JMX Components</h2> + + <p>Logback access easily integrates with JMX servers to publish useful information + about some of its components. + </p> + + <p> + Both <code>RequestLogImpl</code> and <code>LogbackValve</code> can be + published and modified via JMX. A special filter, that we will cover + further down this document, publishes statistical views of access logs. + </p> + + + <h3>Configuring Tomcat</h3> + + <p> + Accessing JMX components with Tomcat requires to add the following lines + to the <em>$TOMCAT_HOME/bin/catalina.sh</em> configuration file: + </p> + +<div class="source"><pre>CATALINA_OPTS="-Dcom.sun.management.jmxremote" +CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false" +CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"</pre></div> + + <p> + Once started with these options, Tomcat's JMX compoenents can be accessed + with JConsole by issuing the following command in a shell: + </p> +<div class="source"><pre>jconsole &</pre></div> + + <p> + The user might prefer to access her components via a web-based solution using MX4J. + In that case, here are the required steps: + </p> + + <p> + First, <a href="http://mx4j.sourceforge.net/">download MX4J</a>. + Place the <em>mx4j-impl.jar</em> file in + the <em>$TOMCAT_HOME/bin/</em> directory, and the <em>mx4j-tools.jar</em> + in the <em>$TOMCAT_HOME/common/lib/</em> directory. + </p> + + <p>Then, add the following lines to the + <em>$TOMCAT_HOME/bin/catalina.sh</em> configuration file: + </p> + +<div class="source"><pre><!-- at the beginning of the file --> +CATALINA_OPTS="-Dcom.sun.management.jmxremote" +CATALINA_OPTS="$CATALINA_OPTS -Djavax.management.builder.initial=mx4j.server.MX4JMBeanServerBuilder" + +<!-- in the "Add on extra jar files to CLASSPATH" section --> +CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/mx4j-impl.jar</pre></div> + + <p> + Finally, declare a new <code>Connector</code> in the + <em>$TOMCAT_HOME/conf/server.xml</em> file: + </p> + +<div class="source"><pre><Connector port="8050" + handler.list="mx" + mx.enabled="true" + mx.httpHost="localhost" + mx.httpPort="8082" + protocol="AJP/1.3" /></pre></div> + + <p> + Once Tomcat is started, you should be ableo to reach the JMX components by + pointing a browser to the following URL: + </p> + +<div class="source"><pre>http://host_name:8082/</pre></div> + + <h3>Configuring Jetty</h3> + + <p> + Configuring Jetty to publish JMX components requires a few modifications to the + <em>$JETTY_HOME/etc/jetty.xml</em> configuration file. Here are the elements that need to be + added: + </p> + +<div class="source"><pre><Call id="MBeanServer" class="java.lang.management.ManagementFactory" name="getPlatformMBeanServer"/> +<!-- initialize the Jetty MBean container --> +<Get id="Container" name="container"> + <Call name="addEventListener"> + <Arg> + <New class="org.mortbay.management.MBeanContainer"> + <Arg><Ref id="MBeanServer"/></Arg> + <Set name="managementPort">8082</Set> + <Call name="start" /> + </New> + </Arg> + </Call> +</Get></pre></div> + + <p> + Once Jetty is started with this configuration, all available components can be reviewed + at this address: + </p> +<div class="source"><pre>http://host_name:8082/</pre></div> + + <p> + Logback's <code>RequestLogImpl</code> is available, and its <code>start()</code> + and <code>stop()</code> method can be called. + </p> + + + <h3>CountingFilter</h3> + + <p> + Logback can provide a statistical view of the accesses to the server. This is done by using the + <a href="xref/ch/qos/logback/access/filter/CountingFilter.html"><code>CountingFilter</code></a> class. + </p> + + <p> + To use the <code>CountingFilter</code>, one just needs to declare it, like any + other filter: + </p> + +<div class="source"><pre><configuration> + <b><filter class="ch.qos.logback.access.filter.CountingFilter"> + <name>countingFilter</name> + </filter></b> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.access.PatternLayout"> + <Pattern>%h %l %u %t \"%r\" %s %b</Pattern> + </layout> + </appender> + + <appender-ref ref="STDOUT" /> +</configuration></pre></div> + + <p> + This component registers itself to the JMX server and publishes + the following statistical figures: + </p> + + <ul> + <p>Minute average</p> + <p>Last minute's count</p> + <p>Hourly average</p> + <p>Last hour's count</p> + <p>Daily average</p> + <p>Last day's count</p> + <p>Weekly average</p> + <p>Last week's count</p> + <p>Monthly average</p> + <p>Last month's count</p> + <p>Total accesses</p> + </ul> +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/bridge.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/bridge.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,94 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Log4j Bridge</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + <h2>Log4j bridge</h2> + + <p>As of version 0.7, logback ships with a new module called + <em>log4j-bridge</em>. It allows log4j users to use logback + without changing a single line of code in their application. All + that is needed to do is to replace the log4j.jar file with the + appropriate logback jars. + </p> + + <h3>How does it work?</h3> + + <p>The log4j-bridge module contains replacements of most widely + used log4j classes, namely <code>Category</code>, + <code>Level</code>, <code>Logger</code>, <code>MDC</code>, + <code>Priority</code>, <code>BasicConfigurator</code> + and <code>Log4jLoggerFactory</code>. These + replacement classes redirect loggging calls to the corresponding + logback methods. + </p> + + <p> + To use log4j-bridge in your own application, the first step is + to locate and remove the <em>log4j.jar</em> file and replace it + with <em>log4j-bridge.jar</em> which ships with logback. Note + that you still need logback-classic and its dependencies for the + log4j-bridge to work properly. In summary, here is a list of the + required jars: + </p> + + <ul> + <li> + log4j-bridge-<em>VERSION</em>.jar + </li> + <li> + logback-classic-<em>VERSION</em>.jar + </li> + <li> + logback-core-<em>VERSION</em>.jar + </li> + <li> + slf4j-api-<em>VERSION</em>.jar + </li> + </ul> + + + This is what it takes to make logback your logging implementation when using log4j. + It will use logback's automatic basic configuration, displaying the logging requests + in the console. More advanced use will require a configuration file and other jars + which are logback dependencies. A file called <em>logback.xml</em>, + placed in the application's classpath, will be automatically loaded by logback. + + + <h3>When does it not work?</h3> + + + The <em>log4-bridge</em> module does not work when the application calls + log4j components that are not present in the bridge. + For examples, direct creation of log4j <code>Appenders</code> or + <code>Filters</code> will not work. + + + + However, in most situations, log4j finds its configuration file and + configures itself. In these cases, the application will only issue calls + to the classes that are contained in the <em>log4j-bridge</em>. + + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/bugreport.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/bugreport.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,83 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Bug report</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + + <h2>Before you report a bug</h2> + +<p> +The logback community consists of those who use logback and its modules, +help answer questions on discussions lists, contribute documentation and patches, +and those who develop and maintain the code for logback and its modules. +Almost all those who assist on a day to day basis resolving bug reports do this for +a wide variety of reasons, and almost all of them do this on their own time. +</p> +<p> +Many bugs reported end up not being a bug in logback, but are due to misconfiguration, +problems caused by installed applications, the operating system, etc. +</p> +<p> +Before reporting a bug please make every effort to resolve the problem yourself. +Just reporting a bug will not fix it. A good bug report includes a detailed description +of the problem and a succinct test case which can reproduce the problem. +</p> + +<h3>Review the documentation</h3> +<p> +Review the documentation for the version of component you are using. +The problem you are having may already be addressed in the docs. +</p> + +<h3>Search the mailing list archives</h3> +<p> +It is very likely you are not the first to run into a problem. +Others may have already found a solution. +Our various mailing lists are likely to have discussed this problem before. +</p> + +<h3>Search Bugzilla</h3> + +<p> +Please search the bug database to see if the bug you are seeing has already been reported. +The bug may have already been fixed and is available in a later version. +If someone else has reported the same bug, you could add supporting information to help reproduce and resolve the bug. +</p> +<ul> + <li><a href="http://bugzilla.qos.ch/query.cgi">Search for logback bugs</a></li> +</ul> + +<h2>Reporting with Bugzilla</h2> + +Only after you have exhausted the aforementioned steps, should you file a formal report in bugzilla. + +<p> +Please make sure you provide as much information as possible. +Its very hard to fix a bug if the person looking into the problem can't reproduce it. +</p> +<ul> + <li><a href="http://bugzilla.qos.ch/enter_bug.cgi">Report new logback bug</a></li> +</ul> + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/codes.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/codes.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,269 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback FAQ</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + <h2><a name="top">Logback error messages and their meanings</a></h2><p><b>Generalities</b></p><ol type="1"><li><a href="#tbr_fnp_not_set"> + The + <b>FileNamePattern</b> + option must be set before using + <code>TimeBasedRollingPolicy</code> + or + <code>FixedWindowRollingPolicy</code> + . + </a></li><li><a href="#fwrp_parentFileName_not_set"> + <p>The File name option must be set before <code>FixedWindowRollingPolicy</code>.</p> + </a></li><li><a href="#socket_no_host"> + No remote host or address is set for + <code>SocketAppender</code> + . + </a></li><li><a href="#socket_no_port"> + No remote port is set for + <code>SocketAppender</code> + . + </a></li><li><a href="#smtp_no_layout"> + No + <code>Layout</code> + is set for appender + </a></li><li><a href="#sbtp_size_format"> + Specified number is not in proper int form, or + not expected format. + </a></li><li><a href="#rfa_no_tp"> + No <code>TriggeringPolicy</code> was set for the + <code>RollingFileAppender</code>. + </a></li><li><a href="#rfa_no_rp"> + No <code>RollingPolicy</code> was set for the + <code>RollingFileAppender</code>. + </a></li></ol> + + + + <div class="section"><h2>Generalities</h2><dl><dt><a name="tbr_fnp_not_set"> + The + <b>FileNamePattern</b> + option must be set before using + <code>TimeBasedRollingPolicy</code> + or + <code>FixedWindowRollingPolicy</code> + . + </a></dt><dd> + <p> + The + <b>FileNamePattern</b> + option for both + <code>TimeBasedRollingPolicy</code> + and + <code>FixedWindowRollingPolicy</code> + is mandatory. + </p> + <p> + See the + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.html"> + FixedWindowRollingPolicy javadoc + </a> + for more information. + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="fwrp_parentFileName_not_set"> + <p>The File name option must be set before <code>FixedWindowRollingPolicy</code>.</p> + </a></dt><dd> + <p> + The <span class="option">File</span> option is mandatory with <code>FixedWindowRollingPolicy</code>. Moreover, the File option must be + set before the declaration configuring <code>FixedWindowRollingPolicy</code>. + </p> + <p> + See the logback manual's chapter about + <a href="http://logback.qos.ch/manual/appenders.html#FixedWindowRollingPolicy"> + FixedWindowRollingPolicy + </a> + for more information. + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="socket_no_host"> + No remote host or address is set for + <code>SocketAppender</code> + . + </a></dt><dd> + <p> + A remote host or address is mandatory for + SocketAppender. + </p> + <p> + You can specify the remote host in the + configuration file like this: + </p> + <div class="source"><pre> +<appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> + ... + <param name="remoteHost" value="127.0.0.1"></param> + ... +</appender> + </pre></div> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="socket_no_port"> + No remote port is set for + <code>SocketAppender</code> + . + </a></dt><dd> + <p> + A remote port is mandatory for + SocketAppender. + </p> + <p> + You can specify the remote port in the + configuration file like this: + </p> + <div class="source"><pre> +<appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> + ... + <param name="port" value="4560"></param> + ... +</appender> + </pre></div> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="smtp_no_layout"> + No + <code>Layout</code> + is set for appender + </a></dt><dd> + <p> + A + <code>Layout</code> + is mandatory for + <code>SMTPAppender</code> + . It allows the appender format the logging + events before sending the email. Two layouts + are often used with + <code>SMTPAppender</code> + : + <code>PatternLayout</code> + and + <code>HTMLLayout</code> + . + </p> + <p> + You can specify the + <code>Layout</code> + in the configuration file like this: + </p> + <div class="source"><pre> +<appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender"> + ... + <layout class="ch.qos.logback.classic.PatternLayout"> + <param name="pattern" value="%-4relative [%thread] %-5level %class - %msg%n"></param> + </layout> + ... +</appender> + </pre></div> + <p> + You can see more examples in the + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/html/HTMLLayout.html"> + HTMLLayout javadoc + </a> + and the + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/PatternLayout.html"> + PatternLayout javadoc + </a> + . + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="sbtp_size_format"> + Specified number is not in proper int form, or + not expected format. + </a></dt><dd> + <p> + When you specify the MaxFileSize to be used + by the SizeBasedRollingPolicy, logback + expects a rather precise format: + </p> + <ul> + <li>The number has to be an integer</li> + <li> + You can add 'KB', 'MB' or 'GB' after the + number. + </li> + </ul> + <p> + Here are some correct values: 500KB, 15MB, + 2GB. + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="rfa_no_tp"> + No <code>TriggeringPolicy</code> was set for the + <code>RollingFileAppender</code>. + </a></dt><dd> + <p> + The <code>RollingFileAppender</code> must be set up with + a <code>TriggeringPolicy</code>. It permits the Appender + to know when the rollover must be activated. + </p> + <p> + To find more information about + <code>TriggeringPolicy</code> objects, please read the + following javadocs: + </p> + <ul> + <li> + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.html"> + <code>SizeBasedTriggeringPolicy</code> + </a> + </li> + <li> + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.html"> + <code>TimeBasedRollingPolicy</code> + </a> + </li> + </ul> + <p> + Please note that the <code>TimeBasedRollingPolicy</code> + is a TriggeringPolicy + <em>and</em> + and <code>RollingPolicy</code> at the same time. + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table><hr /></dd><dt><a name="rfa_no_rp"> + No <code>RollingPolicy</code> was set for the + <code>RollingFileAppender</code>. + </a></dt><dd> + <p> + The <code>RollingFileAppender</code> must be set up with + a <code>RollingPolicy</code>. It permits the Appender + to know what to do when a rollover is requested. + </p> + <p> + To find more information about + <code>RollingPolicy</code> objects, please read the + following javadocs: + </p> + <ul> + <li> + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.html"> + <code>FixedWindowRollingPolicy</code> + </a> + </li> + <li> + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.html"> + <code>TimeBasedRollingPolicy</code> + </a> + </li> + </ul> + <p> + Please note that the <code>TimeBasedRollingPolicy</code> + is a <code>TriggeringPolicy</code> + <em>and</em> + and RollingPolicy at the same time. + </p> + <table border="0"><tr><td align="right"><a href="#top">[top]</a></td></tr></table></dd></dl></div> + </div> + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/css/print.css ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/css/print.css Thu Feb 1 15:44:05 2007 @@ -0,0 +1,38 @@ +/* + * Print css. + * +*/ +body { + margin: 0px; + padding: 0px 0px 2px 0px; + line-height: 1.3em; + font-size: 12px; +} + +#leftColumn { + width: 0px; + height: 0px; + visibility:hidden; +} + +#bodyColumn { + margin-right: 1.5em; + margin-left: 0px; /*was: 197*/ +} + +pre { + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +table.bodyTable td { + vertical-align: text-top; +} + +table.bodyTable th { + vertical-align: text-top; + text-align:center; +} \ No newline at end of file Added: logback/trunk/logback-site/src/site/resources/css/site.css ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/css/site.css Thu Feb 1 15:44:05 2007 @@ -0,0 +1,261 @@ +html { + padding:0px; + margin:0px; +} + +body { + background-color: #fff; + font-family: Verdana, Arial, SunSans-Regular, Sans-Serif; + color:#564b47; + padding:0px; + margin:0px; + font-size: small; +} + +p, h2, pre { + margin: 0px; + padding-top: 5px; + padding-bottom: 5px; + /*padding-left: 1ex;*/ + /*padding: 5px 20px 5px 20px; */ +} + + +a { + color: navy; + /*font-size: smaller;*/ + background-color:transparent; + text-decoration: none; +} + +#content a:hover { + text-decoration: underline; +} + +.source { + border-top: 1px solid #DDDDDD; + border-bottom: 1px solid #DDDDDD; + background:#eee; + font-family: Courier, "MS Courier New", Prestige, Everson Monocourrier, monospace; + padding-bottom: 0.5ex; + padding-top: 0.5ex; + padding-left: 1ex; + /*white-space: pre;*/ +} + +pre { + color: #564b47; + background-color:transparent; + font-family: Courier, Monaco, Monospace; +} + +.alignright { + margin-top: 0; + text-align: right; + font-size: 10px; +} + +h1 { +} + +h2 { + padding-top:10px; + color: #564b47; + background-color: transparent; + font-weight: 900; + font-size: x-large; +} + +h3 { + padding-top:10px; + color: #564b47; + background-color: transparent; + font-weight: normal; + font-size: large; +} + +h4 { + padding-top:5px; + color: navy; + background-color: transparent; + font-weight: large; + font-size: normal; +} + +.footer { + text-align: right; + color: #564b47; + background-color: #90897a; + padding:0px; + margin:0px +} + + + +strong { + /*font-size: 13px;*/ + font-weight: bold; +} + +/* positioning-layers static and absolute */ + +#breadcrumbs { + padding: 3px 10px 3px 10px; + margin: 0px 4px 0px 4px; + font-size: small; + border: 1px solid #CCCCCC; + /*border-bottom: 1px solid #aaa; + /* background-color: #ccc; lime; + border-color: #663300;*/ + background-color: #ffd0a0; + /*max-width: 77em;*/ +} + +#left { + position: absolute; + left: 0px; + width: 15em; + color: #564b47; + margin: 4px 0px 0px 4px; + padding: 0px; + /* background-color: #ffffff; */ + border: 1px solid #cccccc; + /* background-color: #ffcc99; */ + background-color: #ffffff; +} + +#left a, #right a { + display: block; + width: 95.5%; + margin: 0px; + padding: 2px; + border: solid 1px #FFFFFF; + color: #0066cc; + text-decoration: none; +} + +p.menu_header { + margin: 0px; + padding: 2px; + font-weight: normal; + background-color: #ffd0a0; + border-top: solid 1px #CCCCCC; + border-bottom: solid 1px #CCCCCC; +} + +#left a:hover, #right a:hover { + border: solid 1px #FFFFFF; + background-color: #0066cc; + color: #ffffff; +} + +#content { + margin: 0px 15em 0px 16em; + padding: 0px; + background-color: #ffffff; +} + +#right { + position: absolute; + right: 0px; + width: 14em; + color: #564b47; + margin: 4px 4px 0px 0px; + padding: 0px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +#left img { + display: block; + margin: 20px 0 20px 17px; + border: none; + width: 90px; + height: 30px; +} + +#content img { + border:none; + margin-left: auto; + margin-right: auto; + display: block; +} + +table.bodyTable { + padding: 0px; + width: 100%; + margin-left: -2px; + margin-right: -2px; +} + +table.bodyTable th { + color: white; + background-color: #bbb; + font-weight: bold; +} + + +table.bodyTable tr.a { + background-color: #ddd; +} + +table.bodyTable tr.b { + background-color: #eee; +} + +.author { + text-align: left; + font-weight: bold; +} + +.definition { + padding-left: 5px; + padding-right: 5px; + margin: 5px 50px 5px 50px; + text-align: justify; + background-color: #E6E64C; +} + +.deftitle { + font-weight: bold; +} + +.green { + color: green; +} +.blue { + color: blue; +} + +.redBold { + color: red; + font-weight: bold; +} +.greenBold { + color: green; + font-weight: bold; +} + +code { + font-family: Courier, monospace; +} + + +.option { + border: 1px solid black; + font-family: Arial, sans-serif; +} + +.highlight { + width: 300px; + float: right; + display: inline; + font-weight: bolder; + border:1px solid #000; + background:#FFCC99; + padding-top: 0px; + padding-left: 1ex; + padding-right: 1ex; + margin-left: 3em; + margin-right: 3em; +} Added: logback/trunk/logback-site/src/site/resources/demo.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/demo.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,521 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback Demo</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + <h2>Logback Demo</h2> + +<p> + Welcome to the logback demo! This document will take you to a tour + that will show you some of the major possibilities of logback. +</p> + +<h3>Installation</h3> + +<p> + First, please download the logback demo. You will need to install a + <a href="http://subversion.tigris.org/">Subversion</a> client + and issue the following line in a command/terminal window: +</p> + +<div class="source"><pre>svn co http://svn.qos.ch/repos/logback-demo/trunk logback-demo</pre></div> + +<p> +This will checkout a copy of the logback demonstration web-app to a directory called +<em>logback-demo</em>. The logback demo can be packaged as a <em>war</em> file and +deployed to an application server. We strongly recommand the use of +<a href="http://maven.apache.org/">Maven 2</a> to do this +task, since all it will take to compile, package and run a server with the demo is +a single command. +</p> + +<p> + Using Maven, let's package the files and run the demo for the first time. + From the <em>logback-demo</em> directory, issue the following command: +</p> + +<div class="source"><pre>mvn package jetty:run</pre></div> + +<p> + Then, visit <em>http://localhost:8080/logback-demo/</em> to view the main page of the logback demo. +</p> + +<h3>Logback Classic</h3> + +<p> +For now, logback uses two components: one <code>ConsoleAppender</code> and one +<code>RollingFileAppender</code>. The <code>RollingFileAppender</code> sends logging events +to a file called <em>logFile.log</em> and will rollover +the active file every minute. The old file will be renamed and compressed to <em>zip</em> +file. The <code>ConsoleAppender</code> will output the logging requests to the console, +and shorten the logger names to gain some space in the console window, without making the +names unreadable. For example, <code>ch.qos.logback.demo.prime.NumberCruncherImpl +</code> will be displayed as <code>c.q.l.d.prime.NumberCruncherImpl</code>. +</p> + +<p>You can study the configuration file that is used by editing the +file called <em>logback.xml</em>, located in the <em>src/main/resources/</em> directory +of the demo. You might want to keep this file in an editor window, since we will +modify its content several times thoughout the demo. +</p> + +<p> +Let's now visit the <em>ViewStatii</em> page, via the navigation menu on the left hand +of the navigator window. This page contains the content of the <code>Status</code> objects that were +created until now. <code>Status</code> objects are a part of logback's powerful internal +reporting framework. They allow you to see what is going on in logback, and check +that a configuration file has been parsed correctly, or that a rollover has occured as +expected. +</p> + +<p> +After you're back to the main window, visiting the <em>View logs</em> page does +not impress much at the moment. Let us uncomment +the <strong>two</strong> parts of the config file that are below the <em>Cyclic buffer</em> comment. +A <code>CyclicBuffer</code> is a class that keeps track of logging events and holds these +objects for immediate or differed display. The first element that you will need to uncomment +is the <em>appender</em> element. This element describes and configures the <code>CyclicBuffer</code>. +The second element, found at the end of the configuration file, is a <em>appender-ref</em> element. +It is used to link the appender to a given logger. +Now reload the web-app by exiting the previous command with <em>CTRL-C</em> and issuing it +again: <em>mvn package jetty:run</em>. +</p> + +<p> +Now, the <em>View logs</em> page looks prettier. By virtue of the <code>CyclicBufferAppender</code>, +this page can fetch the last events and present them through a servlet. We see that each 3 seconds +a line is added to the logs. The formatting of this page is made with +a <code>HTMLLayout</code>. This component creates a nice and readable table containing the logging +events, based on a pattern that describes the information one wants to see in the table. +</p> + +<p> +Having the logs that we see on the web page cluttered with scheduled +<em>Howdydy-diddly-ho</em> messages +is not very comfortable. To get rid of these logs, now that we've verified that they +work, we can add an <code>EvaluatorFilter</code> to the Appender. Uncomment the +part named <em>Cyclic buffer with Evaluator</em>. You may then +comment or delete the first Basic Cyclic buffer <em>appender</em> element. +</p> +<p> +Let's take a look at the filter we've just added: +</p> + +<div class="source"><pre><filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="loggingTaskEval"> + <expression> + logger.getName().contains("LoggingTask") && + message.contains("Howdydy-diddly-ho") && + (timeStamp - event.getStartTime()) >= 20000 + </expression> + </evaluator> + <OnMatch>DENY</OnMatch> +</filter></pre></div> + +<p> +The expression element contains a familiar java statement. This expression +checks that the name of the logger contains the String <em>LoggingTask</em>, but +also that the message accompagnying the log contains <em>Howdydy-diddly-ho</em>. +Moreover, in order to be sure that the <em>Howdydy-diddly-ho</em> task actually +works, we add to the expression a last statement that allows logs to be processed +for the first 20 seconds after the application launch. +The variables used in this statement (<code>logger</code>, <code>message</code> and +<code>event</code>) are made available by logback before the filter +evaluates the expression. +The <em>OnMatch</em> element allows the user to choose the filter's behaviour once +the expression was evaluated to true. A similar <em>OnMismatch</em> element exists. +</p> + +<p> +After a restart, the <em>Vew logs</em> page shows the +<em>Howdydy-diddly-ho</em> logs for the first 20 seconds only. Trying a prime calculations +on the <em>Prime number</em> page will add several lines to the <em>View logs</em> page. +</p> + +<h4>Turbo Filters</h4> + +<p> +Logback ships with a special category of filters: <code>TurboFilter</code> objects +are ultra-fast, context-wide filters. They reveals themselves very useful to +test MDC values, for examples and to add context-wide conditions to allow or deny +logging events. Let's uncomment the part named +<em>TurboFilter: MDC value</em> in the <em>logback.xml</em> file. +</p> +<p> +This part adds a <code>TurboFilter</code> object to the context. It allows to +have a typical output for every client that uses the demo application, but a different +one for one given user. Here, the filter will accept all the requests that are +associated with a MDC value of <em>sebastien</em> bound to the <em>username</em> key. +</p> +<p> +To view the consequences of such a <code>TurboFilter</code>, we are going to +stop all logging activity, except for a specific user. To achieve that, the simplest +way is to set the root logger's level to <code>OFF</code>. Modify the <em>level</em> +element, nested inside the <em>root</em> element of <em>logback.xml</em>. Its <em>value</em> +attribute should be <em>OFF</em> instead of <em>DEBUG</em>. Next, restart the server +as we've done previously. +</p> + +<p> +Once on the demo main webpage again, perform a few actions (i.e. calculate +a few prime numbers) and watch the <em>View logs</em> page. The table should be +empty. +</p> + +<p> +Now log in the application using the username <em>sebastien</em> and perform a few +prime calculations again. The <em>View logs</em> page now shows the logs that were +generated by the calculation classes. Moreover, each log is associated with the name +of the user who provoked the logging event. Please log off before continuing the +demo, using the <em>logout</em> button on the left. +</p> + +<h4>Parametrized logging</h4> + +<p> +Parametrized logging is a feature that will be a great asset for any performance-critical +system. Usually, a logging request is issued like this: +</p> + +<div class="source"><pre>logger.debug("Hello, my name is" + username + ", I am " + age + " years old.");</pre></div> + +<p> +By issuing this line, the cost of constructing the String cannot be saved when the +log request is not processed. For example, using the <code>debug()</code> method +when, as we've just done, the root level is any value higher that <em>DEBUG</em> will +result in a loss of time because all calls to the <code>debug()</code> method will +eventually be dropped. +</p> + +<p> +Logback offers the following method: +</p> + +<div class="source"><pre>logger.debug("Hello, my name is {}, I am {} years old", username, age);</pre></div> + +<p> +As you can see, the variables are not inserted in the message yet. Both the message +and the values will be saved and used later, if the logging event is processed. +</p> + +<p> +Let us now run a test to see what kind of gain can we expect from this different +message formatting approach. First, go to the <em>Prime number</em> page and +run a few calculations. Check the time it takes to compute the results. To +see a clearer difference between the two formatting methods, you might want to +try the two big integers that are listed below the prime number textbox. +</p> + +<p> +Now let us edit the <code>NumberCruncherImpl</code> class, to switch the log methods. +You will find this class in the <em>src/main/java/ch/qos/logback/demo/prime/</em> +directory. On line 54 and 55, just uncomment the parametrized logging line and +comment out the other line. Restart the server with <em>mvn package jetty:run</em> +and re-run the calculations you tried beforehand. +</p> + +<p> +The durations should be obviously different. Remember that we had turned off all +logging in the previous step of this demo. With the initial formatting method, +we were constructing the logging message (<em>"Trying "+i+" as a factor."</em>) +a huge amount of times, actually each time a factor was tried for these big numbers. +With the paramatrized logging, the construction of the message was postponed and, since +logging was turned off, not processed. We see here that the cost of the <b>non-</b>logging +was taken down to a very small figure, dividing the total cost of the calculation +by a non-negligeable factor. +</p> + +<h4>Markers</h4> + +<p> +SLF4J allows the use of Marker objects. +For example, one could use <em>TRACE</em> markers, to enrich some +specific logging statements. In our demo applications, the <em>Howdydy-diddly-ho</em> +logging statements are bound to a <em>TRACE</em> marker. +On the other hand, one could want that such +marked statements be dropped and not logged anywhere. <code>TurboFilter</code> +objects can do that in an elegant and flexible way. Let us uncomment the +<em>TurboFilter: Marker value</em> section in the <em>logback.xml</em> file as +well as set the root logger's level back to <em>DEBUG</em>, +and reload via the <em>Reload configuration</em> page. +</p> +<p> +The logging statements that contained the <em>Howdydy-diddly-ho</em> do +not appear anymore because they were associated with a <em>TRACE</em> marker. You +can check that by visiting the <em>View Logs</em> page and reloading it every three +seconds for several times. +</p> + +<h3>Logback Access</h3> + +<p> +Access logging is another important feature offered by logback. Give a +look at what appears on the console while +browsing the logback-demo website. Each access is logged to the console, +with some information about the event. The configuration file +that we will edit in the next few steps is called <em>logback-access.xml</em> +and is located in the <em>src/etc/</em> directory. +The necessary configuration is listed below: +</p> + +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.access.PatternLayout"> + <Pattern>%h %l %u %t \"%r\" %s %b</Pattern> + </layout> + </appender> + + <appender-ref ref="STDOUT" /> + +</configuration></pre></div> + +<p> +To see more clearly the output produced by logback access, +you might want set the root logger's level to <em>OFF</em>, in the first +logback configuration file, called +<em>logback.xml</em> and located in <em>src/main/resources/</em>. It will clear +the console from the logs made by the demo application and only display those +that are generated by logback access. +</p> + +<p> +To see the logs produced by logback access, just visit a few pages and +look at your console. The information contained in each line has been +specified in the configuration file. The <code>ConsoleAppender</code> +named <em>STDOUT</em> contains a <code>PatternLayout</code> component. +This very component that one uses in logback classic to display either +the message, logger name or level of the request is used in logback +access to display the request method, requested page, status code and many others. +</p> + +<p>Here is a sample output of this appender.</p> + +<div class="source"><pre>127.0.0.1 - - 22/01/2007:14:35:40 +0100 GET /logback-demo/ViewStatii.do HTTP/1.1 200 3660 +127.0.0.1 - - 22/01/2007:14:35:41 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389 +127.0.0.1 - - 22/01/2007:14:35:42 +0100 GET /logback-demo/lastLog/ HTTP/1.1 200 948 +127.0.0.1 - - 22/01/2007:14:35:42 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389 +127.0.0.1 - - 22/01/2007:14:35:43 +0100 GET /logback-demo/prime.jsp HTTP/1.1 200 1296 +127.0.0.1 - - 22/01/2007:14:35:44 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389 +127.0.0.1 - - 22/01/2007:14:35:45 +0100 GET /logback-demo/lottery.jsp HTTP/1.1 200 1209 +127.0.0.1 - - 22/01/2007:14:35:46 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389 +127.0.0.1 - - 22/01/2007:14:35:48 +0100 GET /logback-demo/reload.jsp HTTP/1.1 200 1335 +127.0.0.1 - - 22/01/2007:14:35:49 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389 +127.0.0.1 - - 22/01/2007:14:35:54 +0100 GET /logback-demo/login.jsp HTTP/1.1 200 1214 +127.0.0.1 - - 22/01/2007:14:35:55 +0100 GET /logback-demo/Logout.do HTTP/1.1 200 1000</pre></div> + +<h4>Filtering</h4> + +<p> +In this next part, we are going to add some information to the console. +Let us imagine that we want to log the numbers that are tried on the +<em>Lottery</em> page. We will need a second <code>ConsoleAppender</code> +that will only print a given information (e.g. the guessed number, along +with some hints about the player). The appender will also have to +print that information only when a certain page is accessed. +</p> + +<p> +The configuration lines that are necessary are listed below. +</p> + +<div class="source"><pre><appender name="STDOUT_LOTTERY" + class="ch.qos.logback.core.ConsoleAppender"> + <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="lotto_eval"> + <Expression> + url.matches(event.getRequestURL().toString()) + </Expression> + <matcher name="url"> + <regex>Lottery.do</regex> + <caseSensitive>false</caseSensitive> + </matcher> + </evaluator> + <OnMatch>ACCEPT</OnMatch> + <OnMismatch>DENY</OnMismatch> + </filter> + <layout class="ch.qos.logback.access.PatternLayout"> + <Pattern> + LOTTERY: %A [%r] Guess=%reqParameter{guessed_number} + </Pattern> + </layout> +</appender></pre></div> + +<p> +This appender will use a <code>PatternLayout</code> to format its output. +The <em>%reqParameter</em> conversion word is used to extract the guessed number +from the request, and print it. +</p> +<p> +It also uses an <code>EvaluatorFilter</code> that will prevent the appender +to display anything when the access' request url does not match the +given expression. You can see that it is easy to specify a RegExp, name +it and use it in the expression that will be evaluated. In that case, we only +entered the name of the <em>lottery.do</em> action. +</p> + +<p> +Let us uncomment the two elements with the <em>Lottery to Console</em> comments and +restart the server. Now, try to play the lottery. You will see more lines in the +Console that you've seen until now. At every try, logback will produce a log +as shown below: +</p> + +<div class="source"><pre>LOTTERY: 192.168.1.6 [POST /logback-demo/Lottery.do HTTP/1.1] Guess=321</pre></div> + +<h4>Sending emails</h4> + +<p> +Logback access provides several components that are usually used by the classic +module. For example, a <code>SMTPAppender</code> can be used to send an email when +a specific event occurs. Here, we will contact the lottery administrator each time +a winner is detected. To achieve this, we will add a <code>SMTPAppender</code> to +the existing configuration. Please uncomment the part of <em>logback-access.xml</em> +named <em>Lottery to Email</em>. Do not forget to uncomment the +<em>appender-ref</em> element, at the end of the configuration file, referencing +the appender called <em>SMTP</em>. In the appender element, notice the use of a +<code>URLEvaluator</code>. This evaluator allows us to only specify one or more URLs +that have to be watched. When one of them are accessed, an email is sent. +</p> + +<p> +A reload of the configuration has to be done before we can test this new +component. Once done, try to play the lottery with the number <em>99</em>. +You should see a congratulation message but, most importantly, the +specified recipients should have a new mail in their mailbox. The content +of the email is a nicely formatted HTML table with informations about +the access that have occured before the triggering event. +</p> + +<h3>JMX</h3> + +<p> +Logback publishes several components via JMX. This allows you to see +the status of certain objects, and change several configuration parameters. +Publishing logback's components via JMX is possible with Jetty and Tomcat. +</p> + +<p> +To see logback access' components, visit the following page: +</p> + +<div class="source"><pre>http://localhost:8082/</pre></div> + +<p> +The domain <em>ch.qos.logback.access.jetty</em> contains an entry +that allows you to see the <em>RequestLogImpl</em> component. This component +is used to plug logback in Jetty's internal achitecture. Clicking on +it reveals the status of several parameters, such as the started status, and +two methods are available to start and stop the <em>RequestLogImpl</em>. +</p> + +<p> +To see the influence of the available operations, place the terminal window +and your web browser such that you can see them both. Stop the <em>RequestLogImlp</em> +and reload a few times the main page of the demo. Nothing should be displayed. If you start +the <em>RequestLogImpl</em> again, the requests will be shown in the terminal +window. +</p> + +<p> +In the <em>logback-access.xml</em> configuration file, uncomment the <em>JMX</em> +part and restart the server. A <code>CountingFilter</code> will now be available in +the <em>ch.qos.logback.access</em> domain. Clicking on it will display several statistical +figures corresponding to server accesses. Loading some application pages will make +these figures grow and show a time-sensitive picture of the server activity. +</p> + + +<p> +These is more to discover with logback and JMX. In the <em>logback.xml</em> file, +placed in the <em>src/main/resources</em> directory, uncomment the <code>JMXConfigurator</code> +element. Once done, restart the server. +</p> + +<p> +By refreshing the previously loaded JMX page, you should see a new component, +under the domain <em>ch.qos.logback.classic</em>. It is the <code>JMXConfigurator</code>. +Clicking on it reveals its content. Its possibilities are listed below: +</p> + +<ul> + <p> + Reload the configuration using the same file that was + previously used. + </p> + <p> + Reload the configuration using a file whose path is passed as + a parameter. + </p> + <p> + Reload the configuration using a file whose URL is passed as a + parameter. + </p> + <p>Get the level of a logger</p> + <p>Change the level setting of a specified logger.</p> + <p>Change a list of all declared loggers.</p> + <p>Change the level setting of a specified logger.</p> +</ul> + +<p> + In the last case, you must specify the name of the logger you + wish to alter, and its new level. +</p> + +<p> +Checking the level of a logger is an easy task. Enter the name of the logger in +the appropriate field and click the <em>Invoke</em> button. You should be able +to verify that the logger named <em>root</em> has its level set to <em>OFF</em>. +</p> + + +<p> +Let us test the level setting possibility of the configurator. +The <em>Prime Number</em> page requests two types of logs. When the +calculation checks if a number is a factor, a <em>DEBUG</em> log is displayed. When +the calculation has found a factor, a <em>INFO</em> log is displayed. +</p> + +<p> +Let us first set the level of the logger named <em>ch.qos.logback.demo.prime</em> +to <em>DEBUG</em>. Run a prime calculation directly, without restarting the server. The +<em>View logs</em> page should show the <em>DEBUG</em> and <em>INFO</em> logs. +</p> + +<p> +Now, if you set the level of the <em>ch.qos.logback.demo.prime</em> logger to +<em>INFO</em>, and run a prime calculation +again, you should not see the <em>DEBUG</em> level logs anymore. +</p> + +<p> +This demo of logback is now over. Do not hesitate to play around with the configuration files. +You might want to check the <a href="http://logback.qos.ch/documentation.html"> +logback documentation page</a> for more information about any component +you'd like to test. +</p> + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/documentation.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/documentation.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,78 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Documentation</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + <h2>Logback documentation</h2> + + <p>Below is a list of logback-related documentaiton currently + available.</p> + + <ul> + <li><a href="manual/index.html"><b>The logback manual</b></a></li> + + <li> + <a href="access.html">An introduction to logback-access for Jetty + and Tomcat</a> + </li> + <li> + <a href="faq.html">A Frequently Asked Questions list (FAQ)</a> + </li> + <li> + <a href="bridge.html">How to use the log4j bridge</a> + </li> + <li> + <a href="jmxConfig.html">How to use the logback JMX Configurator</a> + </li> + <li> + <a href="demo.html">A step-by-step document to experience the logback-demo webApp</a> + </li> + </ul> + + Source code related documentation: + + <ul> + <li> + <a href="apidocs/index.html"><b>Javadoc</b></a> + </li> + <li> + <a href="xref/index.html">Source code</a> + </li> + <li> + <a href="xref-test/index.html">Test classes source code</a> + </li> + </ul> + + + Recently, Ceki G�lc� presented the top 10 reasons for migrating your projects to logback. + Issues such as migration strategy, new APIs, SLF4J and Joran were be discussed. Emphasis was given to + practical aspects and a live demo rather than relatively theoretical considerations. If you were + not able to attend the presentation (or even if you were there), you can + <a href="10reasons.ppt">download the slides</a> that Ceki used. + + + + + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/download.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/download.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,55 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Download</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + <div class="section"> + <h2>Download links</h2> + </div> + + <p> + Logback modules are available as downloads including full source code, class files + and documentation. + </p> + + <ul> + <p> + <a href="dist/logback-${project.version}.zip"> + logback-${project.version}.zip + </a> + </p> + <p> + <a href="dist/logback-${project.version}.tar.gz"> + logback-${project.version}.tar.gz + </a> + </p> + </ul> + + + <p>If you wish to download an older version of logback, please visit the + <a href="http://logback.qos.ch/dist/">distributions directory</a>.</p> + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/faq.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/faq.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,258 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback FAQ</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + <h2> + <a name="top">Logback Frequently Asked Questions</a> + </h2> + + <p> + <b>Logback Classic</b> + </p> + + <ol type="1"> + <li> + <a href="#setup_jetty"> + How can I use logback-classic with Jetty ? + </a> + </li> + <li> + <a href="#auto_config"> + How does the automatic configuration work? + </a> + </li> + <li> + <a href="#intercept_calls_tomcat"> + How can I intercept JCL calls in Tomcat? + </a> + </li> + <li> + <a href="#intercept_calls_jetty"> + How can I intercept JCL calls in Jetty ? + </a> + </li> + </ol> + + <div class="section"> + <h2>Logback Classic</h2> + <dl> + <dt> + <a name="setup_jetty"> + How can I use logback-classic with Jetty ? + </a> + </dt> + <dd> + <p> + The Jetty application server uses SLF4J for its internal + logging. Here are the required steps to install logback + as SLF4J's underlaying implementation. + </p> + <p> + A few jars must be present in the + <em>JETTY_HOME/lib</em> + directory. + </p> + + <p> + Logback-classic is based on the SLF4J api. Therefore, + the + <em>slf4j-api-VERSION.jar</em> + jar must be present. This jar can be downloaded from the + <a href="http://www.slf4j.org/">SLF4J</a> + project. + </p> + <p> + Logback's own jars must also be present, namely + <em>logback-core-VERSION.jar</em> + and + <em>logback-classic-VERSION.jar</em> + . + </p> + + <p> + To configure logback-classic, a file called + <em>logback.xml</em> + should be placed in the + <em>JETTY_HOME/resources</em> + directory. You can find configuration samples in the + <em>examples/src/chapter4/conf/</em> + directory, in the distribution of logback. + </p> + <table border="0"> + <tr> + <td align="right"> + <a href="#top">[top]</a> + </td> + </tr> + </table> + <hr /> + </dd> + <dt> + <a name="auto_config"> + How does the automatic configuration work? + </a> + </dt> + <dd> + <p> + If a file called + <em>logback.xml</em> + is found in the classpath, then it is used. + </p> + <p> + In case it is not found, a + <em>logback-test.xml</em> + file is searched, and used if available. + </p> + <p> + If none of these files are available, logback uses its + <code>BasicConfigurator</code> + class to create a simple default configuration that will + only log to the console. + </p> + <table border="0"> + <tr> + <td align="right"> + <a href="#top">[top]</a> + </td> + </tr> + </table> + <hr /> + </dd> + <dt> + <a name="intercept_calls_tomcat"> + How can I intercept JCL calls in Tomcat? + </a> + </dt> + <dd> + <p> + When a dependency of your webapp logs using Jakarta + Commons Logging (for example Struts), you can intercept + these calls and redirect them to logback. + </p> + <p> + This can be done by using + <em>jcl104-over-slf4j.jar</em> + , a module that is shipped with + <a href="http://www.slf4j.org">SLF4J</a> + . + </p> + <p> + If you have only one webapp, its + <em>WEB-INF/lib</em> + directory should already contain the logback jars, + namely + <em>logback-core-VERSION.jar</em> + , + <em>logback-classic-VERSION.jar</em> + and + <em>slf4j-api-VERSION.jar</em> + . A logback configuration file, named + <em>logback.xml</em> + should be placed in the + <em>WEB-INF/classes/</em> + directory. + </p> + <p> + You now need to add + <code>jcl104-over-slf4j.jar</code> + to your + <em>WEB-INF/lib</em> + directory and remove + <code>commons-logging-1.0.4.jar</code> + . The logging that used to be directed to JCL should now + be handled by logback. + </p> + + <p> + In case several webapps share the logback jars, you + might place the previously mentionned jars in the + <em>common/lib/</em> + directory of your Tomcat installation. The + <em>logback.xml</em> + file should then be placed in + <em>common/classes</em> + . + </p> + <table border="0"> + <tr> + <td align="right"> + <a href="#top">[top]</a> + </td> + </tr> + </table> + <hr /> + </dd> + <dt> + <a name="intercept_calls_jetty"> + How can I intercept JCL calls in Jetty ? + </a> + </dt> + <dd> + <p> + Using logback as the logging implementation of choice + for frameworks depending on JCL can also be done in + Jetty. + </p> + <p> + In case you have only one webapp, the required steps are + exactly the same as + <a href="#intercept_calls_tomcat"> + those needed in Tomcat + </a> + . + </p> + <p> + In case several webapps share the logback jars, you + might place the necessary jars in the + <em>lib/</em> + directory of your Jetty installation. The + <em>logback.xml</em> + file should then be placed in the + <em>resources/</em> + directory. + </p> + <p> + However, due to + <a + href="http://docs.codehaus.org/display/JETTY/Classloading"> + Jetty's internal Classloading mechanism + </a> + , the + <em>logback-classic-VERSION.jar</em> + and + <em>slf4j-api-VERSION.jar</em> + files should also be placed in the + <em>WEB-INF/lib/</em> + directory of your webapps. + </p> + <table border="0"> + <tr> + <td align="right"> + <a href="#top">[top]</a> + </td> + </tr> + </table> + </dd> + </dl> + </div> + </div> + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/images/logos/lblogo.jpg ============================================================================== Binary file. No diff available. Added: logback/trunk/logback-site/src/site/resources/images/logos/qosLogo.png ============================================================================== Binary file. No diff available. Added: logback/trunk/logback-site/src/site/resources/index.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/index.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,50 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback Home</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + <h2>Logback Project</h2> + <p> + Logback is intended as a successor to the popular log4j + project. It was designed by Ceki G�lc�, the founder of the + log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. + </p> + <p> + Logback's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, logback is + divided into three modules, logback-core, logback-classic and + logback-access. + </p> + + <p> + The logback-core module lays the groundwork for the other two + modules. The logback-classic module can be assimilated to a + significantly improved version of log4j. Moreover, + logback-classic natively implements the <a href="http://www.slf4j.org">SLF4J API</a> so that you can + readily switch back and forth between logback and other logging + systems such as log4j or JDK14 Logging. + + The Access module integrates with Servlet containers to + provide HTTP-access log functionality. Note that you can + easily build your own modules on top of the Core module. + </p> + + + +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/jmxConfig.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/jmxConfig.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,208 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>JMX Configuration</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + <h2>JMX Configurator</h2> + + <p> + As of version 0.8, logback ships with a component that allows + configuration via JMX. Basically, it lets you reload the current + configuration, load a new one, list loggers and modify logger levels. + </p> + + <h3>Configuring your server</h3> + <p> + The first step is to make sure that your application server will + allow the JMX Configurator to publish itself. In this document, + we'll cover the necessary steps in Tomcat and Jetty. + </p> + + <h4>Configuring Tomcat</h4> + <p> + Accessing JMX components with Tomcat requires to add the following lines + to the <em>$TOMCAT_HOME/bin/catalina.sh</em> configuration file: + </p> + +<div class="source"><pre>CATALINA_OPTS="-Dcom.sun.management.jmxremote" +CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false" +CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"</pre></div> + + <p> + Once started with these options, Tomcat's JMX compoenents can be accessed + with JConsole by issuing the following command in a shell: + </p> +<div class="source"><pre>jconsole &</pre></div> + + <p> + You might prefer to access your components via a web-based solution using MX4J. + In that case, here are the required steps: + </p> + + <p> + First, <a href="http://mx4j.sourceforge.net/">download MX4J</a>. + Place the <em>mx4j-impl.jar</em> file in + the <em>$TOMCAT_HOME/bin/</em> directory, and the <em>mx4j-tools.jar</em> + in the <em>$TOMCAT_HOME/common/lib/</em> directory. + </p> + + <p>Then, add the following lines to the + <em>$TOMCAT_HOME/bin/catalina.sh</em> configuration file: + </p> + +<div class="source"><pre><!-- at the beginning of the file --> +CATALINA_OPTS="-Dcom.sun.management.jmxremote" +CATALINA_OPTS="$CATALINA_OPTS -Djavax.management.builder.initial=mx4j.server.MX4JMBeanServerBuilder" + +<!-- in the "Add on extra jar files to CLASSPATH" section --> +CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/mx4j-impl.jar</pre></div> + + <p> + Finally, declare a new <code>Connector</code> in the + <em>$TOMCAT_HOME/conf/server.xml</em> file: + </p> + +<div class="source"><pre><Connector port="8050" + handler.list="mx" + mx.enabled="true" + mx.httpHost="localhost" + mx.httpPort="8082" + protocol="AJP/1.3" /></pre></div> + + <p> + Once Tomcat is started, you should be ableo to reach the JMX components by + pointing a browser to the following URL: + </p> + +<div class="source"><pre>http://host_name:8082/</pre></div> + + <h4>Configuring Jetty</h4> + + <p> + Configuring Jetty to publish JMX components requires a few modifications to the + <em>$JETTY_HOME/etc/jetty.xml</em> configuration file. Here are the elements that need to be + added: + </p> + +<div class="source"><pre><Call id="MBeanServer" class="java.lang.management.ManagementFactory" name="getPlatformMBeanServer"/> +<!-- initialize the Jetty MBean container --> +<Get id="Container" name="container"> + <Call name="addEventListener"> + <Arg> + <New class="org.mortbay.management.MBeanContainer"> + <Arg><Ref id="MBeanServer"/></Arg> + <Set name="managementPort">8082</Set> + <Call name="start" /> + </New> + </Arg> + </Call> +</Get></pre></div> + + <p> + Once Jetty is started with this configuration, all available components can be reviewed + at this address: + </p> +<div class="source"><pre>http://host_name:8082/</pre></div> + + + <h3>Using the JMX Configurator</h3> + + <p> + The next step is to declare the JMX Configurator in the logback configuration + file. This is done by adding a single element, as shown below: + </p> + +<div class="source"><pre><configuration> + + <b><jmxConfigurator /></b> + + <appender name="console" class="ch.qos.logback.classic.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%date [%thread] %-5level %logger{25} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug"/> + <appender-ref ref="console" /> + </root> +</configuration></pre></div> + + <p> + Once the JMX Configurator is displayed on your screen, there are + several operations available. + </p> + + <ul> + <p>Display the logback Statuses + </p> + <p>Reload the configuration using the same file that was previously + used. + </p> + <p>Reload the configuration using a file whose path is passed as + a parameter.</p> + <p> + Reload the configuration using a file whose URL is passed as a + parameter. + </p> + <p> + Get the level of a logger + </p> + <p> + Change the level setting of a specified logger. + </p> + <p> + Change a list of all declared loggers. + </p> + <p> + Change the level setting of a specified logger. + </p> + </ul> + + <p> + In the last case, you must specify the name of the logger you wish to + alter, and its new level. + </p> + <p> + The level of a logger is a value that can be null, if no specific level + has been configured for said logger. Its effective level, on the other + hand, is given with respect to the parent loggers' levels. This value cannot + be null, since all loggers are direct or indirect children of the root + logger, whose level is always set. When trying to get the level or effective + level of a logger, the name of the logger has to be passed as a parameter. + Note that trying to get the level or effective level for a nonexistent logger + will not return any result. + </p> + + <p> + Displaying logback Statuses via JMX can help users check the internal state + of logback. It shows if anything has gone wrong, if rollovers occured + as expected, and many other useful informations. It is also very useful + when reloading a configuration, since the user can immediately see if + the configuration file was successfully processed. + </p> + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/license.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/license.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,58 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>License</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + <h2>Logback License</h2> + </div> + + <p> + Logback source code and binaries are distributed under the + <a href="http://www.gnu.org/licenses/lgpl.html"> + GNU Lesser General Public License + </a> + as published by the Free Software Foundation. + </p> + + <div class="source big"><pre>Logback: the reliable, generic, fast and flexible logging library for Java. + +Copyright (C) 2000-2006, QOS.ch + +This library is free software, you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation.</pre></div> + + <p>Please note that logback is intended to be used behind the + SLF4J API, which is licensed under <a href="http://www.slf4j.org/license.html">an X11 type license</a>. + </p> + + <p>If you wish to make a significant contribution to the logback + project, we invite you to file <a href="cla.txt">Contributor + License Agreement</a>. The purpose of this agreement is to + formalize the terms of your contribution and to protect the + project in case of litigation. + </p> + + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/mailinglist.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/mailinglist.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,155 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Mailing lists</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + <h2>Project Mailing Lists</h2> + + <p> + A mailing list is an electronic discussion forum that anyone + can subscribe to. When someone sends an email message to the + mailing list, a copy of that message is broadcast to + everyone who is subscribed to that mailing list. Mailing + lists provide a simple and effective communication + mechanism. With potentially thousands of subscribers, there + is a common set of etiquette guidelines that you should + observe. Please keep on reading. + </p> + <h3>Respect the mailing list type</h3> + <p> + The "User" lists where you can send questions and comments + about configuration, setup, usage and other "user" types of + questions. The "Developer" lists where you can send + questions and comments about the actual software source code + and general "development" types of questions. + </p> + <p> + Some questions are appropriate for posting on both the + "user" and the "developer" lists. In this case, pick one and + only one. Do not cross post. + </p> + <p> + Please do your best to ensure that you are not sending HTML + or "Stylelized" email to the list. If you are using Outlook + or Outlook Express or Eudora, chances are that you are + sending HTML email by default. There is usually a setting + that will allow you to send "Plain Text" email. + </p> + + + <table class="bodyTable"> + <tr class="a"> + <th>Name</th> + <th>Volume</th> + <th>Subscribe</th> + <th>Unsubscribe</th> + <th>Archives</th> + </tr> + <tr class="b"> + <td>Logback Announce List</td> + <td>Low</td> + <td> + <a href="http://qos.ch/mailman/listinfo/logback-announce"> + Subscribe + </a> + </td> + <td> + <a href="http://qos.ch/mailman/options/logback-announce"> + Unsubscribe + </a> + </td> + <td> + <a href="http://www.qos.ch/pipermail/logback-announce/"> + qos.ch + </a> | + <a href="http://marc.theaimsgroup.com/?l=logback-announce"> + MARC + </a> | + <a href="http://www.nabble.com/Logback-Announce-f16251.html"> + Nabble + </a> + </td> + </tr> + <tr class="a"> + <td>Logback User List</td> + <td>Medium</td> + <td> + <a href="http://qos.ch/mailman/listinfo/logback-user"> + Subscribe + </a> + </td> + <td> + <a href="http://qos.ch/mailman/options/logback-user"> + Unsubscribe + </a> + </td> + <td> + <a href="http://www.qos.ch/pipermail/logback-user/"> + qos.ch + </a> | + <a href="http://marc.theaimsgroup.com/?l=logback-user"> + MARC + </a> | + + <a href="http://www.nabble.com/Logback-User-f16252.html"> + Nabble + </a> + </td> + </tr> + <tr class="b"> + <td>Logback Dev List</td> + <td>Medium</td> + <td> + <a href="http://qos.ch/mailman/listinfo/logback-dev"> + Subscribe + </a> + </td> + <td> + <a href="http://qos.ch/mailman/options/logback-dev"> + Unsubscribe + </a> + </td> + <td> + <a href="http://www.qos.ch/pipermail/logback-dev/"> + qos.ch + </a> | + <a href="http://marc.theaimsgroup.com/?l=logback-dev"> + MARC + </a> | + <a href="http://www.nabble.com/Logback-Dev-f16253.html"> + Nabble + </a> + </td> + </tr> + </table> + + <h2>On IRC</h2> + + <p>We can also be reached by IRC at <code><span class="big">irc.freenode.net#logback</span>.</code> + </p> + + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/appenders.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/appenders.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,3172 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 4: Appenders</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + +<script src="../templates/left.js" language="JavaScript" type="text/javascript"> +</script> + +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> + <h2>Chapter 4: Appenders</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a>. + + </p> + </td> + </tr> + </table> + + <div class="highlight"> + <p> + In order to run the examples in this chapter, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + + <h2>What is an Appender</h2> + + <p> + Logback delegates the task of writing a logging event to appenders. + Appenders must implement the + <a href="../xref/ch/qos/logback/core/Appender.html"><code>ch.qos.logback.core.Appender</code></a> interface. + The salient methods of this interface are summarized below: + </p> + <div class="source"><pre>package ch.qos.logback.core; + +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.FilterAttachable; +import ch.qos.logback.core.spi.LifeCycle; + + +public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable { + + public String getName(); + <b>void doAppend(E event);</b> + public void setLayout(Layout<E> layout); + public Layout<E> getLayout(); + public void setName(String name); + +}</pre></div> + + <p> + Most of the methods in the <code>Appender</code> interface are made of setter + and getter methods. A notable exception is the <code>doAppend()</code> + method taking an Object instance as its only parameter. + This method is perhaps the most important in the logback framework. + It is responsible for outputting the logging events in a suitable format + to the appropriate output device. Appenders are named entities. + This ensures that they can be referenced by name, a quality confirmed + to be especially significant in configuration scripts. + An appender can contain multiple filters, thus the <code>Appender</code> + interface extending the <code>FilterAttachable</code> interface. + Filters are discussed in detail in a subsequent chapter. + </p> + + <p> + Appenders are ultimately responsible for outputting logging events. + However, they may delegate the actual formatting of the event to a + <code>Layout</code> object. + Each layout is associated with one and only one appender, referred to + as the containing appender. Some appenders have a built-in or fixed + event format, such that they do not require a layout. For example, the + <code>SocketAppender</code> simply serialize logging events before + transmitting them over the wire. + </p> + + <a name="AppenderBase"></a> + <h2>AppenderBase</h2> + + <p> + The <a href="../xref/ch/qos/logback/core/AppenderBase.html"> + <code>ch.qos.logback.core.AppenderBase</code></a> class is an abstract + class implementing the <code>Appender</code> interface. + It provides basic functionality shared by all appenders, + such as methods for getting or setting their name, their started status, + their layout and their filters. + It is the super-class of all appenders shipped with logback. + Although an abstract class, <code>AppenderBase</code> actually implements the + <code>doAppend()</code> method in the <code>Append</code> interface. + Perhaps the clearest way to discuss <code>AppenderBase</code> class is by + presenting a bit of its actual source code. + </p> + +<div class="source"> + <pre>public synchronized void doAppend(E eventObject) { + + // prevent re-entry. + if (guard) { + return; + } + + try { + guard = true; + + if (!this.started) { + if (statusRepeatCount++ < ALLOWED_REPEATS) { + addStatus(new WarnStatus( + "Attempted to append to non started appender [" + name + "].",this)); + } + return; + } + + if (getFilterChainDecision(eventObject) == FilterReply.DENY) { + return; + } + + // ok, we now invoke derived class' implementation of append + this.append(eventObject); + + } finally { + guard = false; + } +}</pre> +</div> + + <p> + This implementation of the <code>doAppend()</code> method is synchronized. + It follows that logging to the same appender from different + threads is safe. While a thread, say <em>T</em>, is executing the <code>doAppend()</code> + method, subsequent calls by other threads are queued until <em>T</em> + leaves the <code>doAppend()</code> method, ensuring + <em>T</em>'s exclusive access to the appender. + </p> + + <p> + The first thing the <code>doAppend()</code> method does is to set the + <code>guard</code> variable to <code>true</code>. This ensures that the method will not + call itself and create an infinite loop. Just imagine that a component, called somewhere + beyond the <code>append()</code> + method, wants to log something. Its call could be directed to the very same appender + that just called it, which would then call it again. + </p> + + <p> + The first statement of the <code>doAppend()</code> method, once the <code>try</code> block + is reached, is to check whether the <code>started</code> field is true. + If it is not, <code>doAppend()</code> will send a warning message and return. + In other words, once stopped, it is impossible to write to a closed appender. + <code>Appender</code> objects implement the <code>LifeCycle</code> interface, + which implies that they implement <code>start()</code>, <code>stop()</code> + and <code>isStarted()</code> methods. After setting all the options of an appender, + Joran, logback's configuration framework, calls the <code>start()</code> + method to signal the appender to bind or activate its options. + Indeed, depending on the appender, certain options cannot be activated because + of interferences with other options, or appenders can even not start at all if + some options are missing. + For example, since file creation depends on truncation mode, + <code>FileAppender</code> cannot act on the value of its <code>File</code> option + until the value of the Append option is also known for certain. + </p> + + <p> + If a warning message is sent due to incorrect calls to the <code>doAppend()</code> + method, logback's powerful <code>Status</code> error reporting system is used. In case + several incorrect calls on <code>doAppend()</code> are issued, <code>AppenderBase</code> + does not send an unlimited number of warnings. Once a certain limit is reached, the + <code>AppenderBase</code> instance stops its warnings. + </p> + + <p> + The next <code>if</code> statement checks the result + of the attached <code>Filter</code> objects. + Depending on the decision resulting from the filter chain, events can be denied or + alternatively accepted. + In the absence of a decision by the filter chain, events are accepted by default. + </p> + + <p> + Lastly, the <code>doAppend()</code> method invoke the derived classes' implementation + of the <code>append()</code> method, which does the actual work of appending the + event to the appropriate device. + </p> + + <p>In appenders, the term option or property is reserved for named attributes + that are dynamically inferred using JavaBeans introspection. </p> + + <h2>Logback Core</h2> + + <p> + Core is logback's central module. It offers functionnalities that are available + to any other module based on logback core. The <code>Appender</code> classes + contained in the core module are can be used by any module without any customization. + </p> + + <a name="WriterAppender"></a> + <h3>WriterAppender</h3> + + <p> + <a href="../xref/ch/qos/logback/core/WriterAppender.html"><code>WriterAppender</code></a> + appends events to a <code>java.io.Writer</code>. + This class provides basic services that other appenders build upon. + Users do not usually instantiate <code>WriterAppender</code> objects directly. + Since <code>java.io.Writer</code> type cannot be mapped to a string, there is no + way to specify the target <code>Writer</code> object in a configuration script. + Simply put, you cannot configure a <code>WriterAppender</code> from a script. + However, this does not mean that <code>WriterAppender</code> lacks configurable options. + These options are described next. + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b><span class="option">Encoding</span></b></td> + <td><code>String</code></td> + <td> + The encoding specifies the method of conversion between 16-bit Unicode + characters into raw 8-bit bytes. This appender will use the local platform's + default encoding unless you specify otherwise using the + <span class="option">Encoding</span> option. + According to the <code>java.lang</code> package documentation, acceptable values + are dependent on the VM implementation although all implementations are + required to support at least the following encodings: + <em>US-ASCII</em>, <em>ISO-8859-1</em>, <em>UTF-8</em>, <em>UTF-16BE</em>, + <em>UTF-16LE</em> and <em>UTF-16</em>. + By default, the <span class="option">Encoding</span> option is + <code>null</code> such + that the platform's default encoding is used. + </td> + </tr> + <tr class="b"> + <td><b><span class="option">ImmediateFlush</span></b></td> + <td><code>boolean</code></td> + <td> + If set to true, each write of a logging event is followed by a flush operation + on the underlying <code>Writer</code> object. Conversely, if the option is set to false, + each write will not be followed by a flush. + In general, skipping the flush operation improves logging throughput by roughly 15%. + The downside is that if the application exits abruptly, the unwritten characters + buffered inside the <code>Writer</code> might be lost. + This can be particularly troublesome as those unwritten characters may contain + crucial information needed in identifying the reasons behind a crash. + By default, the <span class="option">ImmediateFlush</span> option is set to true. + </td> + </tr> + </table> + + <p> + In general, if you disable immediate flushing, then make sure to flush + any output streams when your application exits. Otherwise, log messages + will be lost as illustrated by the next example. + </p> + + <em>Example 4.1: Exiting an application without flushing (<a href="../xref/chapter4/ExitWoes1.html">logback-examples/src/main/java/chapter4/ExitWoes1.java</a>)</em> +<div class="source"><pre>package chapter4; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.WriterAppender; +import ch.qos.logback.core.layout.EchoLayout; + +public class ExitWoes1 { + + public static void main(String[] args) throws Exception { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + WriterAppender<LoggingEvent> writerAppender = new WriterAppender<LoggingEvent>(); + writerAppender.setContext(lc); + writerAppender.setLayout(new EchoLayout<LoggingEvent>()); + + OutputStream os = new FileOutputStream("exitWoes1.log"); + writerAppender.setWriter(new OutputStreamWriter(os)); + writerAppender.setImmediateFlush(false); + writerAppender.start(); + + Logger logger = LoggerFactory.getLogger(ExitWoes1.class); + + logger.debug("Hello world."); + } +}</pre></div> + + <p> + This example creates a <code>WriterAppender</code> that uses an + <code>OutputStreamWriter</code> + wrapping a <code>FileOutputStream</code> as its underlying <code>Writer</code> object, + with immediate flushing disabled. It then proceeds to log a single debug message. + According to <code>OutputStreamWriter</code> javadocs, each invocation of a + <code>write()</code> + method causes the encoding converter to be invoked on the given character(s). + The resulting bytes are accumulated in a buffer before being written + to the underlying output stream. As astonishing as this may seem, + running <code>ExitWoes1</code> will not produce any output in the file + <em>exitWoes1.log</em> + because the Java VM does not flush output streams when it exits. + Calling the <code>shutdownAndReset()</code> method of a <code>LoggerContext</code> + ensures that all + appenders in the hierarchy are closed and their buffers are flushed. The + <code>ExitWoes2</code> class uses this statement and outputs a logging + request. + </p> + + <p> + The <code>WriterAppender</code> is the super class of four other appenders, + namely <code>ConsoleAppender</code>, <code>FileAppender</code> which in turn is + the super class of <code>RollingFileAppender</code>. The next figure illustrates + the class diagram for <code>WriterAppender</code> and its subclasses. + </p> + + <img src="images/chapter4/fileAppenderUML.png" alt="A UML diagram showing FileAppender"></img> + + <a name="ConsoleAppender"></a> + <h3>ConsoleAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/core/ConsoleAppender.html"> + <code>ConsoleAppender</code></a>, as the name indicates, appends on the console, + or more precisely on <em>System.out</em> or <em>System.err</em>, the former + being the default target. <code>ConsoleAppender</code> formats events with + a layout specified by the user. Both <em>System.out</em> and <em>System.err</em> + are <code>java.io.PrintStream</code> objects. + Consequently, they are wrapped inside an <code>OutputStreamWriter</code> + which buffers I/O operations but not character conversions. + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">Encoding</span></b></td> + <td><code>String</code></td> + <td>See <code>WriterAppender</code> options.</td> + </tr> + <tr class="a"> + <td><b><span class="option">ImmediateFlush</span></b></td> + <td><code>boolean</code></td> + <td>See <code>WriterAppender</code> options.</td> + </tr> + <tr class="b"> + <td><b><span class="option">Target</span></b></td> + <td><code>String</code></td> + <td> + One of the String values <em>System.out</em> or + <em>System.err</em>. The default target is <em>System.out</em>. + </td> + </tr> + </table> + + <p> + Here is a sample configuration that uses <code>ConsoleAppender</code>. + </p> + +<em>Example 4.2: ConsoleAppender configuration (logback-examples/src/main/java/chapter4/conf/logback-Console.xml)</em> +<div class="source"><pre><configuration> + + <b><appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</Pattern> + </layout> + </appender></b> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + To run this example, just issue the following command, + once in the <em>logback-examples</em> directory: + </p> + +<div class="source"><pre>java chapter4.ConfigurationTester src/main/java/chapter4/conf/logback-Console.xml</pre></div> + + <a name="FileAppender"></a> + <h3>FileAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/core/FileAppender.html"><code>FileAppender</code></a>, + a subclass of <code>WriterAppender</code>, + appends log events into a file. The file to write to is specified by + the <span class="option">File</span> option. + If the file already exists, it is either appended to, or truncated + depending on the value of the <span class="option">Append</span> option. + It uses a <code>FileOutputStream</code> which is wrapped by an <code>OutputStreamWriter</code>. + Note that <code>OutputStreamWriter</code> buffers I/O operations + but not character conversions. To optimize character conversions one + can set the <span class="option">BufferedIO</span> option to true + which effectively wraps the <code>OutputStreamWriter</code> with + a <code>BufferedWriter</code>. Options for <code>FileAppender</code> are summarized below. + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">Append</span></b></td> + <td><code>boolean</code></td> + <td>If true, events are appended at the end of an existing file. + Otherwise, if <span class="option">Append</span> is false, any existing + file is truncated. The <span class="option">Append</span> option is set to true by default.</td> + </tr> + <tr class="a"> + <td><b><span class="option">Encoding</span></b></td> + <td><code>String</code></td> + <td>See <code>WriterAppender</code> options.</td> + </tr> + <tr class="b"> + <td><b><span class="option">BufferedIO</span></b></td> + <td><code>boolean</code></td> + <td> + The <span class="option">BufferedIO</span> option is set to false by default. + If set to true, the underlying <code>OutputStreamWriter</code> is wrapped + by a <code>BufferedWriter</code> object. + Setting <span class="option">BufferedIO</span> to true automatically + sets the <span class="option">ImmediateFlush</span> option to false. + The name <span class="option">BufferedIO</span> is slightly misleading because + buffered IO is already supported by <code>OutputStreamWriter</code>. + Setting <span class="option">BufferedIO</span> to true has the effect of + buffering I/O as well as character to raw byte conversions, saving a few + CPU cycles in the process. + </td> + </tr> + <tr class="a"> + <td><b><span class="option">BufferSize</span></b></td> + <td><code>int</code></td> + <td>Size of <code>BufferedWriter</code> buffer. The default value is 8192.</td> + </tr> + <tr class="b"> + <td><b><span class="option">File</span></b></td> + <td><code>String</code></td> + <td> + The name of the file to write to. If the file does not exist, it is created. <br></br> + On the MS Windows platform users frequently forget to escape back slashes. + For example, the value <em>c:\temp\test.log</em> is not likely to be interpreted + properly as <em>'\t'</em> is an escape sequence interpreted as a single + tab character <em>(\u0009)</em>. + Correct values can be specified as <em>c:/temp/test.log</em> or + alternatively as <em>c:\\temp\\test.log</em>. + The <span class="option">File</span> option has no default value. + </td> + </tr> + <tr class="a"> + <td><b><span class="option">ImmediateFlush</span></b></td> + <td><code>boolean</code></td> + <td> + See <code>WriterAppender</code> options. + </td> + </tr> + </table> + + <p> + By default, <code>FileAppender</code> performs a flush operation for + each event, ensuring that events are immediately written to disk. + Setting the <span class="option">ImmediateFlush</span> option to false can drastically reduce + I/O activity by letting <code>OutputStreamWriter</code> buffer bytes + before writing them on disk. For short messages, we have observed 2 or 3 + fold increases in logging throughput, i.e. the number of logs output + per unit of time. For longer messages, the throughput gains are somewhat + less dramatic, and range between 1.4 and 2 fold. Enabling the + <span class="option">BufferedIO</span> + option, that is buffering character to byte conversions, increases + performance by an additional 10% to 40% compared to only disk + I/O buffering (<span class="option">ImmediateFlush</span>=false). + Performance varies somewhat depending on the host machine as well as JDK version. + Throughput measurements are based on the <code>chapter4.IO</code> application. + Please refer to <a href="../xref/chapter4/IO.html"> + <em>logback-examples/src/main/java/chapter4/IO.java</em></a> + for actual source code. + </p> + + <p> + Configuring <code>FileAppender</code> can be done the following way: + </p> + +<em>Example 4.3: FileAppender configuration (logback-examples/src/main/java/chapter4/conf/logback-fileAppender.xml)</em> +<div class="source"><pre><configuration> + + <b><appender name="FILE" class="ch.qos.logback.core.FileAppender"> + <File>testFile.log</File> + <Append>true</Append> + <Encoding>UTF-8</Encoding> + <BufferedIO>false</BufferedIO> + <ImmediateFlush>true</ImmediateFlush> + + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern> + </layout> + </appender></b> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + <p> + To run this example, use the provided <code>ConfigurationTester</code> by + issuing the following command, once in the <em>logback-examples/target/classes</em>: + </p> + +<div class="source"><pre>java chapter4.ConfigurationTester src/main/java/chapter4/conf/logback-fileAppender.xml</pre></div> + + + <a name="RollingFileAppender"></a> + <h3>RollingFileAppender</h3> + + <p> + <a href="../xref/ch/qos/logback/core/rolling/RollingFileAppender.html"><code>RollingFileAppender</code></a> + extends <code>FileAppender</code> by + allowing rolling from a log file to another. For example, + <code>RollingFileAppender</code> can log to a <em>log.txt</em> file and, + once a certain condition is met, change its logging target to another file. + </p> + <p> + There are two important logback componenents that interact with + <code>RollingFileAppender</code>. First, <code>RollingPolicy</code> + implementations define the procedure that will be followed when + the rollover happens. The second componenent is + <code>TriggeringPolicy</code> implementations that are used + to check wether the rollover must happen or not at a given time. + </p> + + <p> + To be of any use, a <code>RollingFileAppender</code> must have + both a <code>RollingPolicy</code> and a <code>TriggeringPolicy</code> + set up. However, if its <code>RollingPolicy</code> also implements the + <code>TriggeringPolicy</code> interface, then only the former needs to be + set up. + </p> + + <p>Here are the available options for <code>RollingFileAppender</code>:</p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b><span class="option">Append</span></b></td> + <td><code>boolean</code></td> + <td>See <code>FileAppender</code> options.</td> + </tr> + <tr class="b"> + <td><b><span class="option">BufferedIO</span></b></td> + <td><code>boolean</code></td> + <td>See <code>FileAppender</code> options.</td> + </tr> + <tr class="a"> + <td><b><span class="option">BufferSize</span></b></td> + <td><code>int</code></td> + <td>See <code>FileAppender</code> options.</td> + </tr> + <tr class="b"> + <td><b><span class="option">Encoding</span></b></td> + <td><code>String</code></td> + <td>See <code>WriterAppender</code> options.</td> + </tr> + <tr class="a"> + <td><b><span class="option">File</span></b></td> + <td><code>String</code></td> + <td>See <code>FileAppender</code> options.</td> + </tr> + <tr class="b"> + <td><b><span class="option">ImmediateFlush</span></b></td> + <td><code>boolean</code></td> + <td>See <code>WriterAppender</code> options.</td> + </tr> + <tr class="a"> + <td><b><span class="option">RollingPolicy</span></b></td> + <td><code>RollingPolicy</code></td> + <td> + This option is the component that will dictate + <code>RollingFileAppender</code>'s behaviour when rollover + occurs. See more information below. + </td> + </tr> + <tr class="b"> + <td><b><span class="option">TriggeringPolicy</span></b></td> + <td><code>TriggeringPolicy</code></td> + <td> + This option is the component that will tell + <code>RollingFileAppender</code> when to activate the rollover + procedure. See more information below. + </td> + </tr> + </table> + + <h3>Rolling policies</h3> + + <p><a href="../xref/ch/qos/logback/core/rolling/RollingPolicy.html"><code>RollingPolicy</code></a> + implementations are responsible for the + rollover procedure. They manage file renaming and in occasion file deleting.</p> + + <p>The <code>RollingPolicy</code> interface is presented below:</p> + +<div class="source"><pre>package ch.qos.logback.core.rolling; + +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.spi.LifeCycle; + +public interface RollingPolicy extends LifeCycle { + + <b>public void rollover() throws RolloverFailure;</b> + public String getNewActiveFileName(); + public void setParent(FileAppender appender); +}</pre></div> + + <p> + The <code>rollover</code> method proceeds to the file change, renaming or deletion. + The <code>getNewActiveFileName()</code> method is called to compute a new file name, with + respect to the configuration elements that were injected in the <code>RollingPolicy</code>. + Lastly, a <code>RollingPolicy</code> knows about its parent. + </p> + + <a name="FixedWindowRollingPolicy"></a> + <h4>FixedWindowRollingPolicy</h4> + + <p> + When rolling over, <a href="../xref/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.html"> + <code>FixedWindowRollingPolicy</code></a> + renames files according to a fixed window algorithm as described below. + </p> + <p> + The <span class="option">File</span> option, which is configured in the + <code>FileAppender</code> element, is required. It represents the name of the file + to write to. The <span class="option">FileNamePattern</span> + option represents the file name pattern for the archived (rolled over) log files. + The <span class="option">FileNamePattern</span> option, which is also required, must include + an integer token, that is the string <em>%i</em> + somewhere within the pattern. + </p> + + <p> + Here are the available options for <code>FixedWindowRollingPolicy</code> + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">FileNamePattern</span></b></td> + <td><code>String</code></td> + <td> + <p> + This option represents the pattern that will be followed by + the <code>FixedWindowRollingPolicy</code> when renaming the + log files. If must contain the string <em>%i</em>, which will + indicate the position where to insert the file's index. + </p> + <p> + For example, using <em>MyLogFile%i.log</em>, associated with + minimum and maximum values of <em>1</em> and <em>3</em> will produce + files named <em>MyLogFile1.log</em>, <em>MyLogFile2.log</em> and + <em>MyLogFile3.log</em>. + </p> + <p> + File compression is also specified in the + <span class="option">FileNamePattern</span> option. + <em>MyLogFile%i.log.zip</em> will indicate to the + <code>FixedWindowRollingPolicy</code> that the archived file + must be compressed using the <em>zip</em> format. The <em>gz</em> + format is also supported. + </p> + </td> + </tr> + <tr class="a"> + <td><b><span class="option">MaxIndex</span></b></td> + <td><code>int</code></td> + <td> + <p> + This option represents the maximum border of the window algorithm. + </p> + </td> + </tr> + <tr class="b"> + <td><b><span class="option">MinIndex</span></b></td> + <td><code>int</code></td> + <td> + <p> + This option represents the minimum border of the window algorithm. + </p> + </td> + </tr> + </table> + + <p> + Given that this rollover algorithm requires as many file + renaming operations as the window size, large window sizes are + discouraged. The current implementation will automatically + reduce the window size to 12 when larger values are specified by + the user. + </p> + + <p> + Here is an example of file handling by <code>FixedWindowRollingPolicy</code>. + We suppose that the <span class="option">MinIndex</span> is set to <em>1</em> and + <span class="option">MaxIndex</span> is set to <em>3</em>. The + <span class="option">FileNamePattern</span> option is set to <em>foo%i.log</em>, and + the <span class="option">FileNamePattern</span> + option is set to <em>foo.log</em>. + </p> + + <table class="bodyTable"> + <tr class="a"> + <th> + Steps + </th> + <th> + Active file name + </th> + <th> + Archived file names + </th> + <th>Description</th> + </tr> + <tr class="b"> + <td> + 0 + </td> + <td> + foo.log + </td> + <td> + - + </td> + <td> + No rollover has happened yet, logback logs + into the initial file. + </td> + </tr> + <tr class="a"> + <td> + 1 + </td> + <td> + foo.log + </td> + <td> + foo1.log + </td> + <td> + First rollover. <em>foo.log</em> is renamed into <em>foo1.log</em> and + a new <em>foo.log</em> file is created and used for the output. + </td> + </tr> + <tr class="b"> + <td> + 2 + </td> + <td> + foo.log + </td> + <td> + foo2.log, foo1.log + </td> + <td> + Second rollover. <em>foo.log</em> is renamed into <em>foo1.log</em> and + the old <em>foo1.log</em> is renamed into <em>foo2.log</em>. + Again, a new <em>foo.log</em> file is created and used for the output. + </td> + </tr> + <tr class="a"> + <td> + 3 + </td> + <td> + foo.log + </td> + <td> + foo3.log, foo2.log, foo1.log + </td> + <td> + Third rollover. <em>foo.log</em> is renamed into <em>foo1.log</em> and + the old <em>foo1.log</em> is renamed into <em>foo2.log</em>. As well, the + old <em>foo2.log</em> is renamed into <em>foo3.log</em>. + A new <em>foo.log</em> file is created and used for the output. + </td> + </tr> + <tr class="b"> + <td> + 4 + </td> + <td> + foo.log + </td> + <td> + foo3.log, foo2.log, foo1.log + </td> + <td> + From the fourth rollover, the old <em>foo3.log</em> file is deleted. The files + are all renamed with an increment to their index, and a new <em>foo.log</em> + file is created and used for the output. + From this moment on, there will always be 4 log files available, each being present + for the time of 3 rollovers and being deleted afterwards. + </td> + </tr> + </table> + + <p> + Here is a sample configuration to use <code>RollingFileAppender</code> + and <code>FixedWindowRollingPolicy</code>. + </p> + +<em>Example 4.4: Sample configuration of a <code>RollingFileAppender</code> using a +<code>FixedWindowRollingPolicy</code> (logback-examples/src/main/java/chapter4/conf/logback-RollingFixedWindow.xml)</em> +<div class="source"><pre><configuration> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <File>testFile.log</File> + <b><rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <FileNamePattern>testFile.%i.log.zip</FileNamePattern> + <MinIndex>1</MinIndex> + <MaxIndex>3</MaxIndex> + </rollingPolicy></b> + + <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> + <MaxFileSize>5MB</MaxFileSize> + </triggeringPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + <a name="TimeBasedRollingPolicy"></a> + <h4>TimeBasedRollingPolicy</h4> + <p> + <a href="../xref/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.html"> + <code>TimeBasedRollingPolicy</code></a> is both easy to configure and quite powerful. + It allows the rollover to be made based on time conditions. It is possible to specify + that the rollover must occur each day, or month, for example. + </p> + <p> + <code>TimeBasedRollingPolicy</code>'s only option is the + <span class="option">FileNamePattern</span>. + </p> + + <p> + In order to use + <code>TimeBasedRollingPolicy</code>, the + <span class="option">FileNamePattern</span> option must be set. It basically + specifies the name of the rolled log files. The value + <span class="option">FileNamePattern</span> should consist of the name of the file, + plus a suitably placed <em>%d</em> conversion specifier. + The <em>%d</em> conversion specifier may contain a date and time pattern as + specified by the <code>java.text.SimpleDateFormat</code> class. + If the date and time pattern is omitted, then the default pattern + of <em>yyyy-MM-dd</em> is assumed. The following examples should + clarify the point. + </p> + <table class="bodyTable"> + <tr class="a"> + <th> + <span class="option">FileNamePattern</span> + </th> + <th>Roll-over schedule</th> + <th>Example</th> + </tr> + <tr class="b"> + <td> + <em>/wombat/folder/foo.%d</em> + </td> + <td> + Daily rollover (at midnight). Due to the omission of the + optional time and date pattern for the <em>%d</em> token + specifier, the default pattern of <em>yyyy-MM-dd</em> is + assumed, which corresponds to daily rollover. + </td> + <td> + During November 23rd, 2006, logging output will go to + the file <em>/wombat/foo.2006-11-23</em>. + At midnight and for the rest of the 24th, logging + output will be directed to <em>/wombat/foo.2006-11-24</em>. + </td> + </tr> + <tr class="a"> + <td> + <em>/wombat/foo.%d{yyyy-MM}.log</em> + </td> + <td>Rollover at the beginning of each month.</td> + <td> + During the month of October 2006, logging output will go + to <em>/wombat/foo.2006-10.log</em>. + After midnight of October 31st and for the rest of + November, logging output will be directed to + <em>/wombat/foo.2006-11.log</em>. + </td> + </tr> + <tr class="b"> + <td> + <em>/wombat/foo.%d{yyyy-ww}.log</em> + </td> + <td>Rollover at the first day of each week. Note that the first + day of the week depends on the locale.</td> + <td> + During the 23rd week of 2006, the file <em>/wombat/foo.2006-23.log</em> + will contain the actual logging output. + Logging for the 24th week of 2006 will be output to + <em>/wombat/foo.2006-24.log</em> + until it is rolled over at the beginning of the next week. + </td> + </tr> + <tr class="a"> + <td> + <em>/wombat/foo.%d{yyyy-MM-dd-a}.log</em> + </td> + <td>Rollover at midnight and midday of each day.</td> + <td> + During the first 12 hours of November 3rd, 2006, the logging + will be output to <em>/wombat/foo.2006-11-03-AM.log</em>. + After noon, and until midnight, the logging will be output to + <em>/wombat/foo.2006-11-03-PM.log</em>. + </td> + </tr> + <tr class="b"> + <td> + <em>/wombat/foo.%d{yyyy-MM-dd_HH}.log</em> + </td> + <td>Rollover at the top of each hour.</td> + <td> + Between 11.00,001 and 11.59,999, on November 3rd, 2006, the logging + will be output to <em>/wombat/foo.2006-11-03_11.log</em>. + After that, and until 12.59,999, the logging will be output to + <em>/wombat/foo.2006-11-03_12.log</em>. + </td> + </tr> + <tr class="a"> + <td> + <em>/wombat/foo.%d{yyyy-MM-dd_HH-mm}.log</em> + </td> + <td>Rollover at the beggining of every minute.</td> + <td> + Between 11.32,001 and 11.32,999, on November 3rd, 2006, the logging + will be output to <em>/wombat/foo.2006-11-03_11-32.log</em>. + After that, and until 12.33,999, the logging will be output to + <em>/wombat/foo.2006-11_12-33.log</em>. + </td> + </tr> + </table> + + <p> + Any characters in the pattern outside the ranges <em>['a'..'z']</em> and <em>['A'..'Z']</em> + will be treated as quoted text. For instance, characters like <em>'.'</em>, <em>' '</em>, + <em>'#'</em> and <em>'@'</em> will appear in the resulting time text even when they are not + enclosed within single quotes. Nevertheless, we would recommend against + using the colon <em>":"</em> character anywhere within the + <span class="option">FileNamePattern</span> option. + The text before the colon is interpreted as the protocol specification of a + URL, which is most probably not what you intend. The slash <em>"/"</em> character, a + common date field separator, must also be avoided. It is taken as a file + separator causing the rollover operation to fail because the target file cannot + be created. Although less common, the backslash character <em>"\"</em> is equally troublesome. + </p> + + <p> + Just like <code>FixedWindowRollingPolicy</code>, <code>TimeBasedRollingPolicy</code> + supports automatic file compression. + This feature is enabled if the value of the <span class="option">FileNamePattern</span> option + ends with <em>.gz</em> or <em>.zip</em>. + </p> + <table class="bodyTable"> + <tr class="b"> + <th><span class="option">FileNamePattern</span></th> + <th>Rollover schedule</th> + <th>Example</th> + </tr> + <tr class="a"> + <td><em>/wombat/foo.%d.gz</em></td> + <td>Daily rollover (at midnight) with automatic GZIP compression of the + arcived files.</td> + <td>During November 23rd, 2004, logging output will go to + the file <em>/wombat/foo.2004-11-23</em>. However, at midnight that + file will be compressed to become <em>/wombat/foo.2004-11-23.gz</em>. + For the 24th of November, logging output will be directed to + <em>/wombat/folder/foo.2004-11-24</em> until its rolled over at the + beginning of the next day. + </td> + </tr> + </table> + + <p> + As we have seen, the <span class="option">FileNamePattern</span> serves two purposes. First, + by studying the pattern, logback computes the requested rollover periodicity. Second, + it computes each files' name based on the pattern. It is entirely possible for two different + file name patterns to specify the same periodicity. + The date patterns <em>yyyy-MM</em> and <em>yyyy@MM</em> both specify monthly + rollover periodicity, although the rolled files will carry different names. + </p> + + <p> + Given the use of the <span class="option">FileNamePattern</span>, we see that the + <code>TimeBasedRollingPolicy</code> is responsible for the rollover as well as for + the triggering of said rollover. Therefore, <code>TimeBasedTriggeringPolicy</code> + implements both <code>RollingPolicy</code> and <code>TriggeringPolicy</code> + interfaces. A <code>RollingFileAppender</code> that uses + <code>TimeBasedRollingPolicy</code> can be started and used correctly even + if its configuration does not contain any reference to a <code>TriggeringPolicy</code>. + </p> + + <p> + With <code>TimeBasedRollingPolicy</code>, it is possible to + decouple the location of the active log file and the archived log files + </p> + <p> + The <span class="option">File</span> option defines the log file + for the current period whereas <em>archived files</em> are those files + which have been rolled over in previous periods. + </p> + <p> + By setting the <span class="option">File</span> option you can + decouple the location of the active log file and the location + of the archived log files. The actual logging will be done in the + file specified by the <span class="option">File</span> option. This way, + the active file name will never change. By not setting the + <span class="option">File</span> option, logback uses the + <span class="option">FileNamePattern</span> to name the active file, + whose name will change each time a rollover occurs. + </p> + + <p> + For various efficiency reasons, rollovers are not time-driven + but depend on the arrival of logging events. For example, on 8th of March 2002, + assuming the <span class="option">FileNamePattern</span> is set to + <em>yyyy-MM-dd</em> (daily rollover), the arrival of the first + event after midnight will trigger rollover. If there are no logging events + during, say 23 minutes and 47 seconds after midnight, + then rollover will occur at 00:23'47 AM on March 9th and not at 0:00 AM. + Thus, depending on the arrival rate of events, rollovers might be triggered + with some latency. However, regardless of the delay, the rollover algorithm + is known to be correct, in the sense that all logging events generated + during a certain period will be output in the correct file delimiting that period. + </p> + + <p>Here is a sample configuration of a <code>RollingFileAppender</code> which + uses a <code>TimeBasedRollingPolicy</code> + </p> + +<em>Example 4.5: Sample configuration of a <code>RollingFileAppender</code> using a +<code>TimeBasedRollingPolicy</code> (logback-examples/src/main/java/chapter4/conf/logback-RollingTimeBased.xml)</em> +<div class="source"><pre><configuration> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <File>logFile.log</File> + <b><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern>logFile.%d{yyyy-MM-dd}.log</FileNamePattern> + </rollingPolicy></b> + + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + <a name="TriggeringPolicy"></a> + <h3>Triggering policies</h3> + + <p><a href="../xref/ch/qos/logback/core/rolling/TriggeringPolicy.html"><code>TriggeringPolicy</code></a> + implementations are responsible for instructing + the <code>RollingFileAppender</code> to rollover.</p> + + <p>The <code>TriggeringPolicy</code> interface is pretty simple.</p> + +<div class="source"><pre>package ch.qos.logback.core.rolling; + +import java.io.File; +import ch.qos.logback.core.spi.LifeCycle; + +public interface TriggeringPolicy extends LifeCycle { + + <b>public boolean isTriggeringEvent(final File activeFile, final Object event);</b> +}</pre></div> + + <p> + The + <code>isTriggeringEvent()</code> + method takes the active file, and the currently processed + logging event. It's implementation decides, based on these + parameters, whether the rollover must occur or not, by + returning a boolean value. + </p> + + <a name="SizeBasedTriggeringPolicy"></a> + <h4>SizeBasedTriggeringPolicy</h4> + + <p> + <a href="../xref/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.html"> + <code>SizeBasedTriggeringPolicy</code></a> + looks at size of the file being currently written to. If it + grows larger than the specified size, the + <code>FileAppender</code> using the + <code>SizeBasedTriggeringPolicy</code> + will proceed to the rollover of the current file and log to + a new one. + </p> + + <p> + This <code>TriggeringPolicy</code> + only accepts one parameter, that is the + <span class="option">MaxFileSize</span> + option. This option's default value is 10 MB. + </p> + + <p> + The <span class="option">MaxFileSize</span> + option can be specified in a simple and easy way, by + specifying the unit that should be used. One can enter any + numeric value, with three possible units, namely <em>KB</em>, + <em>MB</em> and <em>GB</em>. Consequently, values like + <em>5MB</em>, <em>500KB</em> or <em>2GB</em> are all valid. + </p> + <p> + Here is a sample configuration with a <code>RollingFileAppender</code> + using a <code>SizeBasedTriggeringPolicy</code>. + </p> + +<em>Example 4.6: Sample configuration of a <code>RollingFileAppender</code> using a +<code>SizeBasedTriggeringPolicy</code> (logback-examples/src/main/java/chapter4/conf/logback-RollingSizeBased.xml)</em> +<div class="source"><pre><configuration> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <File>testFile.log</File> + <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <FileNamePattern>testFile.%i.log.zip</FileNamePattern> + <MinIndex>1</MinIndex> + <MaxIndex>3</MaxIndex> + </rollingPolicy> + + <b><triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> + <MaxFileSize>5MB</MaxFileSize> + </triggeringPolicy></b> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + <p> + <code>TriggeringPolicy</code> implementations do not only serve with + <code>RollingFileAppender</code> objects. They can also be used to tell + <code>SMTPAppender</code>, which will be covered soon, when to send an email + containing the last logging events. + </p> + + <p> + In that case, the <code>isTriggeringEvent()</code> method takes <em>null</em> + as its first parameter (of type <code>File</code>) and takes the logging event + as its second parameter. It is based on that last element that the decision is + made to send the email or not. By default, a <code>TriggeringPolicy</code> is + included with <code>SMTPAppender</code> that triggers the mail each time an event + with a <code>Level</code> of <em>ERROR</em> or more is issued. + </p> + + <a name="Classic"></a> + <h2>Logback Classic</h2> + + + <p>While logging event are declared as <code>Object</code> in logback core, + they are instances of the <code>LoggingEvent</code> class in logback classic.</p> + + <a name="SocketAppender"></a> + <h3>SocketAppender</h3> + + <p> + The appenders covered this far were only able to log on local resources. + In contrast, the <a href="../xref/ch/qos/logback/classic/net/SocketAppender.html"> + <code>SocketAppender</code></a> is designed to log to a + remote entity by transmitting serialized <code>LoggingEvent</code> objects over the wire. + Remote logging is non-intrusive as far as the logging event is concerned. + On the receiving end after de-serialization, the event can be logged as + if it were generated locally. Multiple <code>SocketAppender</code> instances + running of different machines can direct their logging output + to a central log server whose format is fixed. + <code>SocketAppender</code> does not admit an + associated layout because it sends serialized events to a remote server. + <code>SocketAppender</code> operates above the + <em>Transmission Control Protocol (TCP)</em> + layer which provides a reliable, sequenced, flow-controlled end-to-end octet stream. + Consequently, if the remote server is reachable, then log events + will eventually arrive there. Otherwise, if the remote server is down or + unreachable, the logging events will simply be dropped. If and when the server + comes back up, then event transmission will be resumed transparently. + This transparent reconnection is performed by a connector thread which + periodically attempts to connect to the server. + </p> + + <p> + Logging events are automatically buffered by the native TCP implementation. + This means that if the link to server is slow but still faster than the + rate of event production by the client, the client will not be affected by + the slow network connection. However, if the network connection is slower + then the rate of event production, then the client can only progress at the + network rate. In particular, in the extreme case where the network link + to the server is down, the client will be eventually blocked. + Alternatively, if the network link is up, but the server is down, + the client will not be blocked, although the log events will be + lost due to server unavailability. + </p> + + <p> + Even if a <code>SocketAppender</code> is no longer attached to any logger, + it will not be garbage collected in the presence of a connector thread. + A connector thread exists only if the connection to the server is down. + To avoid this garbage collection problem, you should close the <code>SocketAppender</code> + explicitly. Long lived applications which create/destroy many + <code>SocketAppender</code> instances should be aware of this + garbage collection problem. Most other applications can safely ignore it. + If the JVM hosting the <code>SocketAppender</code> exits before the + <code>SocketAppender</code> is closed, either explicitly or subsequent + to garbage collection, then there might be untransmitted data in the + pipe which may be lost. This is a common problem on Windows based systems. + To avoid lost data, it is usually sufficient to <code>close()</code> the + <code>SocketAppender</code> either explicitly or by calling the + <code>LoggerContext</code>'s <code>shutdownAndReset()</code> method before exiting the application. + </p> + + <p> + The remote server is identified by the <span class="option">RemoteHost</span> and + <span class="option">Port</span> options. + <code>SocketAppender</code> options are listed in the following table. + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b><span class="option">IncludeCallerData</span></b></td> + <td><code>boolean</code></td> + <td> + <p> + The <span class="option">IncludeCallerData</span> option takes a boolean value. + If true, the caller data will be available to the remote host. + By default no caller data is sent to the server. + </p> + </td> + </tr> + <tr class="b"> + <td><b><span class="option">Port</span></b></td> + <td><code>int</code></td> + <td> + <p> + The port number of the remote server. + </p> + </td> + </tr> + <tr class="a"> + <td><b><span class="option">ReconnectionDelay</span></b></td> + <td><code>int</code></td> + <td> + The <span class="option">ReconnectionDelay</span> option takes a + positive integer representing the number of milliseconds to wait between + each failed connection attempt to the server. + The default value of this option is 30'000 which corresponds to 30 seconds. + Setting this option to zero turns off reconnection capability. + Note that in case of successful connection to the server, there will be no + connector thread present. + </td> + </tr> + <tr class="b"> + <td><b><span class="option">RemoteHost</span></b></td> + <td><code>String</code></td> + <td> + The host name of the server. + </td> + </tr> + </table> + + <p> + The standard logback distribution includes a simple log server application named + <code>ch.qos.logback.classic.net.SimpleSocketServer</code> that can service multiple + <code>SocketAppender</code> clients. It waits for logging events from + <code>SocketAppender</code> clients. After reception by + <code>SimpleSocketServer</code>, the events are logged according to local server policy. + The <code>SimpleSocketServer</code> application takes two parameters: + port and configFile; where port is the port to listen on and configFile is + configuration script in XML format. + </p> + + <p> + Assuming you are in the <em>logback-examples/</em> directory, + start <code>SimpleSocketServer</code> with the following command: + </p> + +<div class="source"><pre>java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ + src/main/java/chapter4/socket/server1.xml +</pre></div> + + <p> + where 6000 is the port number to listen on and <em>server1.xml</em> is a + configuration script that adds a <code>ConsoleAppender</code> and a + <code>RollingFileAppender</code> to the root logger. + After you have started <code>SimpleSocketServer</code>, you can send it + log events from multiple clients using <code>SocketAppender</code>. + The examples associated with this manual include two such clients: + <code>chapter4.SocketClient1</code> and <code>chapter4.SocketClient2</code> + Both clients wait for the user to type a line of text on the console. + The text is encapsulated in a logging event of level debug and then sent + to the remote server. The two clients differ in the configuration of the + <code>SocketAppender</code>. <code>SocketClient1</code> configures the appender + programmatically while <code>SocketClient2</code> requires a configuration file. + </p> + + <p> + Assuming <code>SimpleSocketServer</code> is running on the local host, + you connect to it with the following command: + </p> + +<div class="source"><pre>java chapter4.socket.SocketClient1 localhost 6000</pre></div> + + <p> + Each line that you type should appear on the console of the + <code>SimpleSocketServer</code> + launched in the previous step. If you stop and restart the + <code>SimpleSocketServer</code> + the client will transparently reconnect to the new server + instance, although the events generated while disconnected + will be simply (and irrevocably) lost. + </p> + + <p> + Unlike + <code>SocketClient1</code>, the sample application + <code>SocketClient2</code> does not configure logback by itself. + It requires a configuration file in XML format. + The configuration file <em>client1.xml</em> + shown below creates a <code>SocketAppender</code> + and attaches it to the root logger. + </p> + + <em>Example 4.7: SocketAppender configuration (logback-examples/src/main/java/chapter4/socket/client1.xml)</em> +<div class="source"><pre><configuration> + + <appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> + <RemoteHost>${host}</RemoteHost> + <Port>${port}</Port> + <ReconnectionDelay>10000</ReconnectionDelay> + <IncludeCallerData>${includeCallerData}</IncludeCallerData> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="SOCKET" /> + </root> + +</configuration></pre></div> + + + <p> + Note that in the above configuration scripts the values for the + <span class="option">RemoteHost</span>, <span class="option">Port</span> and + <span class="option">IncludeCallerData</span> options + are not given directly but as substituted variable keys. The values for the variables + can be specified as system properties: + </p> + +<div class="source"><pre>java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \ + chapter4.socket.SocketClient2 src/main/java/chapter4/socket/client1.xml +</pre></div> + + <p> + This command should give similar results to the previous + <code>SocketClient1</code> + example. + </p> + + <p> + Allow us to repeat for emphasis that serialization of logging events is not + intrusive. A de-serialized event carries the same information as any other + logging event. It can be manipulated as if it were generated locally; + except that serialized logging events by default do not include caller + data. Here is an example to illustrate the point. First, start + <code>SimpleSocketServer</code> with the following command: + </p> + +<div class="source"><pre> java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ + src/main/java/chapter4/socket/server2.xml +</pre></div> + + <p> + The configuration file <em>server2.xml</em> creates a <code>ConsoleAppender</code> + whose layout outputs the callers file name and line number along with other + information. If you run <code>SocketClient2</code> with the configuration file + <em>client1.xml</em> as previously, you will notice that the output on the + server side will contain two question marks between parentheses instead of + the file name and the line number of the caller: + </p> + +<div class="source"><pre>2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapter4.socket.SocketClient2 - Hi</pre></div> + + <p> + The outcome can be easily changed by instructing the <code>SocketAppender</code> + to include caller data by setting the <span class="option">IncludeCallerData</span> + option to true. Using the following command will do the trick: + </p> + +<div class="source"><pre>java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \ + chapter4.socket.SocketClient2 src/main/java/chapter4/socket/client1.xml +</pre></div> + + <p> + As deserialized events can be handled in the same way as locally + generated events, they even can be sent to a second server for further treatment. + As an exercise, you may wish to setup two servers where the first server + tunnels the events it receives from its clients to a second server. + </p> + + <a name="JMSAppenderBase"></a> + <h3>JMSAppenderBase</h3> + + <p> + The <a href="../xref/ch/qos/logback/core/net/JMSAppenderBase.html"> + <code>JMSAppenderBase</code></a> subclasses conceptually accomplishes + the same task as the <code>SocketAppender</code> but as the name + suggests it is based on the JMS API instead of TCP sockets. + JMS or the Java Message Service API + provides an abstraction for Message-Oriented Middleware (MOM) products. + One of the key architectural concepts in JMS is the decoupling of message + producers and message consumers. Senders do not have to wait for receivers + to handle messages and conversely the receiver consumes messages as they + become available; messages are said to be delivered asynchronously. Just as + importantly, consumers as well as producers can be added or removed at will + to a JMS channel. The set of the message producers and message consumers can + vary independently and transparently over time, with both sets oblivious + to each other. + </p> + + <p> + The JMS specification provides for two types of messaging models, + publish-and-subscribe and point-to-point queuing. Logback supports the former + model with <code>JMSTopicAppender</code> and the latter with <code>JMSQueueAppender</code> + Both appenders extend the <code>JMSAppenderBase</code> class and + publish serialized events to a topic or queue specified by the user. + </p> + + <p> + One or more <code>JMSTopicSink</code> or <code>JMSQueueSink</code> applications + can register to a JMS server and consume the serialized events. + The consumer of JMS appenders generated events need not be only <code>JMSTopicSink</code> + or <code>JMSQueueSink</code> applications. Any application or MessageDrivenBean + capable of subscribing to the appropriate topic or queue and consuming serialized + logging event messages would be suitable. + Additional consumers could be quickly built based on the <code>JMSTopicSink</code> or + <code>JMSQueueSink</code> model. + </p> + + <p> + Here are <code>JMSAppenderBase</code>'s options: + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">InitialContextFactoryName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The class name of the initial JNDI context factory. There is no need + to set this option if you have a properly configured <em>jndi.properties</em> + file or if <code>JMSAppenderBase</code> subclass is running + within an application server. + </p> + <p> + If you set this option, you should + also set the <span class="option">ProviderURL</span> option. + </p> + </td> + </tr> + <tr class="a"> + <td><b><span class="option">ProviderURL</span></b></td> + <td><code>String</code></td> + <td> + <p> + This option specifies configuration information for the + JNDI service provider. The value of the property should contain a + URL string (e.g. <em>ldap://somehost:389</em>). + </p> + <p> + The <span class="option">ProviderURL</span> option is taken into + account only if the <span class="option">InitialContextFactoryName</span> + option is specified. It is ignored otherwise. + </p> + </td> + </tr> + <tr class="b"> + <td><b><span class="option">URLPkgPrefixes</span></b></td> + <td><code>String</code></td> + <td> + <p> + This option contains the list of package prefixes to + use when loading in URL context factories. The value of the + property should be a colon-separated list of package + prefixes for the class name of the URL context factory class. + </p> + <p> + For JBoss the value of this option should be: + org.jboss.naming:org.jnp.interfaces + This option is not needed under Weblogic. + </p> + <p> + This option is taken into account only if the + <span class="option">InitialContextFactoryName</span> + option is specified. It is ignored otherwise. + </p> + </td> + </tr> + <tr class="a"> + <td><b><span class="option">SecurityPrincipalName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The security principal name to use when accessing the JNDI namespace. + This option is usually not required. + </p> + <p> + This option is taken into account only if the + <span class="option">InitialContextFactoryName</span> + option is specified. It is ignored otherwise. + </p> + </td> + </tr> + <tr class="b"> + <td> + <b> + <span class="option">SecurityCredentials</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + <p> + The security credentials to use when accessing the + JNDI namespace. This option is usually not required. + </p> + <p> + This option is taken into account only if the + <span class="option"> + InitialContextFactoryName + </span> + option is specified. It is ignored otherwise. + </p> + </td> + </tr> + <tr class="a"> + <td> + <b> + <span class="option">UserName</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + <p> + The username to use when creating a topic or queue connection. + </p> + </td> + </tr> + <tr class="b"> + <td> + <b> + <span class="option">Password</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + <p> + The password to use when creating a topic or queue connection. + </p> + </td> + </tr> + </table> + + <p> + JMS topics, queues and connection factories are administered objects that are obtained + using the JNDI API. This in turn implies the necessity of retrieving a JNDI Context. + There are two common methods for obtaining a JNDI Context. If a file resource named + <em>jndi.properties</em> is available to the JNDI API, it will use the information + found therein to retrieve an initial JNDI context. + To obtain an initial context, one simply calls: + </p> + +<div class="source"><pre>InitialContext jndiContext = new InitialContext();</pre></div> + + <p> + Calling the no-argument <code>InitialContext()</code> constructor will also work + from within Enterprise Java Beans (EJBs). + Indeed, it is part of the EJB contract for application servers to provide + each enterprise bean an environment naming context (ENC). + </p> + + <p> + In the second approach, several predetermined properties are specified. + These properties are passed to the <code>InitialContext</code> constructor + to connect to the naming service provider. + For example, to connect to an + <a href="http://www.activemq.org/site/home.html"><code>ActiveMQ</code></a> + naming server one would write: + </p> + +<div class="source"><pre>Properties env = new Properties(); +env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); +env.put(Context.PROVIDER_URL, "tcp://<em>hostname</em>:61616"); +Context ctx = new InitialContext(env);</pre></div> + + <p> + where <em>hostname</em> is the host where the ActiveMQ server is running. + </p> + + <p> + Other JNDI providers will obviously require different values. + As mentioned previously, the initial JNDI context can be obtained by calling + the no-argument <code>InitialContext()</code> constructor from within EJBs. + Only clients running in a separate JVM need to be concerned about + the <em>jndi.properties</em> file or setting the different properties + before calling <code>InitialContext</code> constructor taking a + Properties (i.e. Hashtable) parameter. + </p> + + <h4>Comments on JMS appenders</h4> + + <p> + Transmitting a packet of information using JMS is certain to be substantially + slower then sending the same packet using raw TCP sockets. JMS vendors bragging + about the performance of their messaging platform tend to omit this simple fact. + Guaranteed store and forward messaging comes at a hefty price. + In return for increased cost, JMS messaging provides decoupling of + sender and receiver. As long as the JMS provider is reachable, messages + will eventually arrive at destination. + However, what if the JMS server is down or simply unreachable? + </p> + + <p> + According to the JMS specification, producers can mark a message as either + persistent or non-persistent. The persistent delivery mode instructs the JMS provider + to log the message to stable storage as part of the client's send operation, allowing + the message to survive provider crashes. JMS appenders do not set the delivery + mode of messages it produces because according to the JMS specification, + the delivery mode is considered as an administered property. + </p> + + <p> + Once a message reaches the JMS provider, the provider assumes the responsibility + of delivering it to its destination, relieving the client from this chore. + What if the JMS server is unreachable? The JMS API provides an + <code>ExceptionListener</code> interface to deal with this situation. + When the client runtime of the JMS provider detects a lost connection to the JMS server, + it calls the <code>onException()</code> method of the registered + <code>ExceptionListener</code>. Once notified of the problem, client code can attempt + to reestablish the connection. According to the section 4.3.8 of the JMS specification, + the provider should attempt to resolve connection problems prior to notifying the client. + The JMS appenders do not implement the <code>ExceptionListener</code> interface. + </p> + + <a name="JMSTopicAppender"></a> + <h3>JMSTopicAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/classic/net/JMSTopicAppender.html"> + <code>JMSTopicAppender</code></a> acts as a message producer to a publish and subscribe + Topic. + </p> + + <p> + Its most important method, <code>doAppend()</code> is listed below: + </p> + +<div class="source"><pre>public void append(LoggingEvent event) { + if (!isStarted()) { + return; + } + + try { + ObjectMessage msg = topicSession.createObjectMessage(); + + msg.setObject(event); + topicPublisher.publish(msg); + successiveFailureCount = 0; + } catch (Exception e) { + successiveFailureCount++; + if (successiveFailureCount > SUCCESSIVE_FAILURE_LIMIT) { + stop(); + } + addError("Could not publish message in JMSTopicAppender [" + name + "].", e); + } +}</pre></div> + + <p> + The <code>isStarted()</code> method allows the appender to check whether + prerequisite conditions for its proper functioning, in particular the + availability of a valid and open <code>TopicConnection</code> and a + <code>TopicSession</code>, are fulfilled. If that is not the case, + the append method returns without performing any work. + If the prerequisite conditions are fulfilled, then the method + proceeds to publish the logging event. This is done by obtaining a + <code>javax.jms.ObjectMessage</code> from the <code>TopicSession</code> + and then setting its payload to the logging event received as + the input parameter. Once the payload of the message is set, it is + published. The fact that <code>LoggingEvent</code> is serializable + has its importance, as only Serializable objects can be + transported within an <code>ObjectMessage</code>. + </p> + + <p> + In summary, the <code>JMSTopicAppender</code> broadcasts messages consisting + of a serialized <code>LoggingEvent</code> payload over a user-specified + JMS topic. These events can be processed by a + <a href="../xref/ch/qos/logback/classic/net/JMSTopicSink.html"> + <code>JMSTopicSink</code></a> + or a similar consumer. According to JMS specification, the provider + will asynchronously call the <code>onMessage()</code> of duly registered + and subscribed <code>javax.jms.MessageListener</code> objects. + The <code>onMessage()</code> method in <code>JMSTopicSink</code> + is implemented as follows: + </p> + +<div class="source"><pre>public void onMessage(javax.jms.Message message) { + LoggingEvent event; + try { + if (message instanceof ObjectMessage) { + ObjectMessage objectMessage = (ObjectMessage) message; + event = (LoggingEvent) objectMessage.getObject(); + Logger log = (Logger) LoggerFactory.getLogger(event.getLoggerRemoteView().getName()); + log.callAppenders(event); + } else { + logger.warn("Received message is of type " + message.getJMSType() + + ", was expecting ObjectMessage."); + } + } catch (JMSException jmse) { + logger.error("Exception thrown while processing incoming message.", jmse); + } +}</pre></div> + + <p> + The <code>onMessage()</code> method begins by retrieving the logging event's payload. + It then obtains a Logger with the same name as the logger name of the incoming event. + The event is then logged through this logger as if it were generated locally, + by calling its <code>callAppenders()</code> method. The <code>SocketNode</code> class used by + <code>SimpleSocketServer</code> handles incoming logging events essentially in the same way. + </p> + + <p> + Some options are proper to <code>JMSTopicAppender</code>. They are + listed below. + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">TopicConnectionFactoryBindingName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The name of the topic factory. There is no default value for this mandatory option. + </p> + </td> + </tr> + <tr class="a"> + <td><b><span class="option">TopicBindingName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The name of the topic to use. There is no default value for this mandatory option. + </p> + </td> + </tr> + </table> + + <p> + <code>JMSTopicAppender</code> is rather straightforward to configure: + </p> + + <em>Example 4.8: JMSTopicAppender configuration (logback-examples/src/main/java/chapter4/conf/logback-JMSTopic.xml)</em> +<div class="source"><pre><configuration> + + <appender name="Topic" + class="ch.qos.logback.classic.net.JMSTopicAppender"> + <InitialContextFactoryName> + org.apache.activemq.jndi.ActiveMQInitialContextFactory + </InitialContextFactoryName> + <ProviderURL>tcp://localhost:61616</ProviderURL> + <TopicConnectionFactoryBindingName> + ConnectionFactory + </TopicConnectionFactoryBindingName> + <TopicBindingName>MyTopic</TopicBindingName> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="Topic" /> + </root> +</configuration></pre></div> + + <a name="JMSQueueAppender"></a> + <h3>JMSQueueAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/classic/net/JMSQueueAppender.html"> + <code>JMSQueueAppender</code></a> acts as a message producer to a point-to-point + Queue. + </p> + + <p> + It works in a very similar manner to the <code>JMSTopicAppender</code>. + </p> + + <p> + Some options are proper to <code>JMSQueueAppender</code>. They are + listed below. + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b><span class="option">QueueConnectionFactoryBindingName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The name of the queue factory. There is no default value for this mandatory option. + </p> + </td> + </tr> + <tr class="b"> + <td><b><span class="option">QueueBindingName</span></b></td> + <td><code>String</code></td> + <td> + <p> + The name of the queue to use. There is no default value for this mandatory option. + </p> + </td> + </tr> + </table> + + <p> + A typical <code>JMSQueueAppender</code> configuration file looks very + similar to that of a <code>JMSTopicAppender</code>. + </p> + <em>Example 4.9: JMSQueueAppender configuration (logback-examples/src/main/java/chapter4/conf/logback-JMSQueue.xml)</em> +<div class="source"><pre><configuration> + + <appender name="Queue" + class="ch.qos.logback.classic.net.JMSQueueAppender"> + <InitialContextFactoryName> + org.apache.activemq.jndi.ActiveMQInitialContextFactory + </InitialContextFactoryName> + <ProviderURL>tcp://localhost:61616</ProviderURL> + <QueueConnectionFactoryBindingName> + ConnectionFactory + </QueueConnectionFactoryBindingName> + <QueueBindingName>MyQueue</QueueBindingName> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="Queue" /> + </root> +</configuration></pre></div> + + <a name="SMTPAppender"></a> + <h3>SMTPAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/classic/net/SMTPAppender.html"><code>SMTPAppender</code></a> + accumulates logging events in a fixed-size buffer and sends them in an email when a + user specified event occurs. + By default, the email is sent as the reception of an event + of level <em>ERROR</em> or higher. + </p> + + <p> + The various options for <code>SMTPAppender</code> are summarized in the following table. + </p> + + <table class="bodyTable"> + <tr class="a"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b><span class="option">SMTPHost</span></b></td> + <td><code>String</code></td> + <td> + The host name of the SMTP server. This parameter is mandatory. + </td> + </tr> + <tr class="a"> + <td><b><span class="option">To</span></b></td> + <td><code>String</code></td> + <td> + The email address of the recipient. Multiple recipients can + be specified by using several <To> elements. + </td> + </tr> + <tr class="b"> + <td><b><span class="option">From</span></b></td> + <td><code>String</code></td> + <td> + The stated originator of the email messages sent by + <code>SMTPAppender</code>. + </td> + </tr> + <tr class="a"> + <td><b><span class="option">Subject</span></b></td> + <td><code>String</code></td> + <td> + <p> + The subject of the email. The String can contain a <code>Pattern</code> + that <code>PatternLayout</code> uses. In that case, the subject + is created just before the transmission of the email, with information + about the last logging event that was issued. + </p> + <p> + For example, setting <em>Log: %logger - %msg</em> as the + <span class="option">Subject</span> option will send an email with + the logger name and message string of the event that triggered the + email transmission. + </p> + <p> + By default, <code>SMTPAppender</code> will form a subject with + logger name and the message of the last logging event. + </p> + </td> + </tr> + <tr class="b"> + <td><b><span class="option">BufferSize</span></b></td> + <td><code>String</code></td> + <td> + The <span class="option">BufferSize</span> option takes a positive + integer representing the maximum number of logging events to collect in a + cyclic buffer. When the <span class="option">BufferSize</span> is reached, + oldest events are deleted as new events are added to the buffer. + The default size of the cyclic buffer is 512. + </td> + </tr> + <tr class="a"> + <td><b><span class="option">Evaluator</span></b></td> + <td><code>String</code></td> + <td> + <p>This option is declared by creating a new <code><EventEvaluator/></code> + element. The name of the class that the user wishes to use as the + <code>SMTPAppender</code>'s <code>Evaluator</code> can be given + by adding an attribute to the newly created element. + </p> + <p> + More details about the use of event evaluators with <code>SMTPAppender</code> + follow further down this document. + </p> + <p>In the absence of this option, + <code>SMTPAppender</code> is assigned a default event evaluator which triggers + email transmission as a response to any event of level <em>ERROR</em> or higher. + </p> + </td> + </tr> + </table> + + <p> + The SMTPAppender keeps only the last <span class="option">BufferSize</span> logging events + in its cyclic buffer, throwing away older events when its buffer becomes full. + The number of logging events delivered in any e-mail sent by <code>SMTPAppender</code> + is upper-bounded by <span class="option">BufferSize</span>. This keeps memory + requirements bounded while still delivering a reasonable amount of application context. + </p> + + <p> + The <code>SMTPAppender</code> relies on the JavaMail API. + It has been tested with JavaMail API version 1.4. + The JavaMail API requires the JavaBeans Activation Framework package. + You can download the <a href="http://java.sun.com/products/javamail/">JavaMail API</a> + and the <a href="http://java.sun.com/beans/glasgow/jaf.html">Java-Beans Activation Framework</a> + from their respective websites. + Make sure to place these two jar files in the classpath before + trying the following examples. + </p> + + <p> + A sample application called <code>chapter4.mail.EMail</code> takes two parameters. + The first parameter is an integer corresponding to the number of logging events + to generate. The second parameter is the logback configuration file in XML format. + The last logging event generated by chapter4.mail.Email application is always an + <em>ERROR</em> event which triggers the transmission of an email message. + </p> + + <p> + Here is a sample configuration file you can supply to chapter4.mail.Email: + </p> + +<em>Example 4.10: A sample <code>SMTPAppender</code> configuration (logback-examples/src/main/java/chapter4/mail/mail1.xml)</em> +<div class="source"><pre><configuration> + + <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> + <SMTPHost>ADDRESS-OF-YOUR-SMTP-HOST</SMTPHost> + <To>DESTINATION-EMAIL</To> + <From>SENDER-EMAIL</From> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%date %-5level %logger{35} - %message%n</Pattern> + </layout> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="EMAIL" /> + </root> +</configuration></pre></div> + + <p> + Before trying out <code>chapter4.mail.Email</code> application with the above + configuration file, you must set the <span class="option">SMTPHost</span>, + <span class="option">To</span> and <span class="option">From</span> options + to values appropriate for your environment. Once you have set the proper values, + change directory to <em>logback-examples</em> and execute the following command: + </p> + +<div class="source"><pre>java chapter4.mail.EMail 300 src/main/java/chapter4/mail/mail.xml</pre></div> + + <p> + The chosen recipient should see an email message containing 300 logging events + formatted by <code>PatternLayout</code>. + </p> + + <p> + In another configuration file <em>mail2.xml</em>, the values for the + <span class="option">SMTPHost</span>, <span class="option">To</span> + and <span class="option">From</span> options are determined by variable + substitution. Here is the relevant part of <em>mail2.xml</em>. + </p> + +<div class="source"><pre> + <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> + <SMTPHost>${smtpHost}</SMTPHost> + <To>${to}</To> + <From>${from}</From> + <layout class="ch.qos.logback.classic.html.HTMLLayout"/> + </appender> +</pre></div> + + <p> + You can supply the various values on the command line: + </p> + +<div class="source"><pre>java -Dfrom=source@xyz.com -Dto=recipient@xyz.com + -DsmtpHost=some_smtp_host src/main/java/chapter4.mail.EMail 10000 chapter4/mail/mail2.xml +</pre></div> + + <p> + Be sure to replace with the correct values appropriate for your environment. + </p> + + <p> + Given that the default size of the cyclic buffer is 512, + the recipient should see an email message containing 512 events conveniently + formatted in an HTML table. Note that this run of the <code>chapter4.mail.Email</code> + application generated 10'000 events of which only the last 512 were included in the email. + </p> + + <p> + By default, the <code>SMTPAppender</code> will initiate the transmission of an email + message as a response to an event of level <em>ERROR</em> or higher. + However, it is possible to override this default behavior by providing a custom + implementation of the <code>EventEvaluator</code> interface. + </p> + + <p> + The <code>SMTPAppender</code> submits each incoming event to its evaluator + by calling <code>evaluate()</code> method in order to check whether + the event should trigger an email or just be placed in the cyclic buffer. + When the evaluator gives a positive answer to its evaluation, an email is sent. + The <code>SMTPAppender</code> contains one and only one evaluator object. + This object may possess its own state. For illustrative purposes, + the <code>CounterBasedEvaluator</code> class listed next, implements an + event evaluator whereby every 1024th event triggers an email message. + </p> + +<em>Example 4.11: A <code>EventEvaluator</code> implementation +that evaluates to <code>true</code> every 1024th event (<a href="../xref/chapter4/mail/CounterBasedEvaluator.html">logback-examples/src/main/java/chapter4/mail/CounterBasedEvaluator.java</a>)</em> +<div class="source"><pre>package chapter4.mail; + +import ch.qos.logback.core.boolex.EvaluationException; +import ch.qos.logback.core.boolex.EventEvaluator; +import ch.qos.logback.core.spi.ContextAwareBase; + +public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator { + + static int LIMIT = 1024; + int counter = 0; + String name; + + <b>public boolean evaluate(Object event) throws NullPointerException, + EvaluationException { + counter++; + + if (counter == LIMIT) { + counter = 0; + + return true; + } else { + return false; + } + }</b> + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}</pre></div> + + <p> + Note that this implementation extends <code>ContextAwareBase</code> and + implements <code>EventEvaluator</code>. This allows the user to concentrate + on the core functions of her <code>EventEvaluator</code> and let the base class + provide the common functionnality. + </p> + + <p> + Setting the <span class="option">EventEvaluator</span> option of + <code>SMTPAppender</code> instructs it to use a custom evaluator. + The next configuration file attaches a <code>SMTPAppender</code> to the root logger. + This appender has a buffer size of 2048 and uses a <code>CounterBasedEvaluator</code> instance + as its event evaluator. + </p> + +<em>Example 4.12: <code>SMTPAppender</code> with custom +<code>Evaluator</code> and buffer size (logback-examples/src/main/java/chapter4/mail/mail3.xml)</em> + +<div class="source"><pre><configuration> + <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> + <b><Evaluator class="chapter4.mail.CounterBasedEvaluator" /></b> + <BufferSize>1050</BufferSize> + <SMTPHost>${smtpHost}</SMTPHost> + <To>${to}</To> + <From>${from}</From> + <layout class="ch.qos.logback.classic.html.HTMLLayout"/> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="EMAIL" /> + </root> +</configuration></pre></div> + + + + <a name="DBAppender"></a> + <h3>DBAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/classic/db/DBAppender.html"><code>DBAppender</code></a> + inserts loggin events into three database tables in a format + independent of the Java programming language. + </p> + <p> + These three tables are <em>logging_event</em>, <em>logging_event_property</em> and + <em>logging_event_exception</em>. They all must exist before <code>DBAppender</code> + can be used. Logback ships with SQL scripts that will create the tables. + They can be found in the found in the + <em>logback-classic/src/main/java/ch/qos/logback/classic/db/dialect</em> directory. There + is a specific script for each of the most popular database systems. + If the script for your particular type of database system is missing, it should be + quite easy to write one, taking example on the already existing scripts. If + you send them to us, we will gladly include missing scripts in future releases. + </p> + + <p> + If your JDBC driver supports the + <code>getGeneratedKeys</code> method introduced in + JDBC 3.0 specification, then no more steps are required, excluding usual + configuration. + </p> + <p> + Otherwise, there must be an <code>SQLDialect</code> appropriate for your + database system. Currently, we have dialects for PostgreSQL, + MySQL, Oracle and MsSQL. As mentioned previously, an + <code>SQLDialect</code> is required only if the JDBC driver for your + database system does not support the <code>getGeneratedKeys</code> + method. + </p> + + <p> + The table below summarizes the database types and their support of the + <code>getGeneratedKeys()</code> method. + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>RDBMS</th> + <th> + supports + <br></br> + <code>getGeneratedKeys()</code> + method + </th> + <th> + specific + <br></br> + SQLDialect support + </th> + </tr> + <tr class="a"> + <td>PostgreSQL</td> + <td>NO</td> + <td>present and used</td> + </tr> + <tr class="b"> + <td>MySQL</td> + <td>YES</td> + <td>present, but not actually needed or used</td> + </tr> + <tr class="a"> + <td>Oracle</td> + <td>YES</td> + <td>present, but not actually needed or used</td> + </tr> + <tr class="b"> + <td>DB2</td> + <td>YES</td> + <td>not present, and not needed or used</td> + </tr> + <tr class="a"> + <td>MsSQL</td> + <td>YES</td> + <td>not present, and not needed or used</td> + </tr> + <tr class="b"> + <td>HSQL</td> + <td>NO</td> + <td>present and used</td> + </tr> + </table> + + <p> + Experiments show that writing a single event + into the database takes approximately 10 milliseconds, on a + "standard" PC. If pooled connections are used, this figure + drops to around 1 milliseconds. Note that most JDBC drivers + already ship with connection pooling support. + </p> + + <p> + Configuring logback to use <code>DBAppender</code> can be done + in several different ways, depending on the tools one has to + connect to the database, and the database itself. All manners of + configuring <code>DBAppender</code> are about setting its + <code>ConnectionSource</code> object, which we will cover in + a short moment. + </p> + + <p> + Once logback is configured properly, the logging events are sent to + the specified database. As stated previously, there are three tables + used by logback to store logging event data. + </p> + + <p> + The <em>logging_event</em> table contains the following fields: + </p> + <table class="bodyTable"> + <tr class="a"> + <th>Field</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b>timestmp</b></td> + <td><code>big int</code></td> + <td>The timestamp that was valid at the logging event's creation.</td> + </tr> + <tr class="a"> + <td><b>formatted_message</b></td> + <td><code>text</code></td> + <td>The message that has been added to the logging event, after formatting with + <code>org.slf4j.impl.MessageFormatter</code>, in case object were passed + along with the message.</td> + </tr> + <tr class="b"> + <td><b>logger_name</b></td> + <td><code>varchar</code></td> + <td>The name of the logger used to issue the logging request.</td> + </tr> + <tr class="a"> + <td><b>level_string</b></td> + <td><code>varchar</code></td> + <td>The level of the logging event.</td> + </tr> + <tr class="b"> + <td><b>reference_flag</b></td> + <td><code>smallint</code></td> + <td> + <p> + This field is used by logback to identify logging events that + have an exception or <code>MDC</code>property values associated. + </p> + <p> + It's value is computed by + <code>ch.qos.logback.classic.db.DBHelper</code>. A logging event that + contains <code>MDC</code> or <code>Context</code> + properties has a flag number of <em>1</em>. One + that contains an exception has a flag number of <em>2</em>. A logging + event that contains both elements has a flag number of <em>3</em>. + </p> + </td> + </tr> + <tr class="a"> + <td><b>caller_filename</b></td> + <td><code>varchar</code></td> + <td>The name of the file where the logging request was issued.</td> + </tr> + <tr class="b"> + <td><b>caller_class</b></td> + <td><code>varchar</code></td> + <td>The class where the logging request was issued.</td> + </tr> + <tr class="a"> + <td><b>caller_method</b></td> + <td><code>varchar</code></td> + <td>The name of the method where the logging request was issued.</td> + </tr> + <tr class="b"> + <td><b>caller_line</b></td> + <td><code>char</code></td> + <td>The line number where the logging request was issued.</td> + </tr> + <tr class="a"> + <td><b>event_id</b></td> + <td><code>int</code></td> + <td>The database id of the logging event.</td> + </tr> + </table> + + <p> + The <em>logging_event_property</em> is used to store the keys and values + contained in the <code>MDC</code> or the <code>Context</code>. + It contains these fields: + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Field</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b>event_id</b></td> + <td><code>int</code></td> + <td>The database id of the logging event.</td> + </tr> + <tr class="b"> + <td><b>mapped_key</b></td> + <td><code>varchar</code></td> + <td>The key of the <code>MDC</code> property</td> + </tr> + <tr class="a"> + <td><b>mapped_value</b></td> + <td><code>text</code></td> + <td>The value of the <code>MDC</code> property</td> + </tr> + </table> + + <p> + The <em>logging_event_exception</em> table contains the following fields: + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Field</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b>event_id</b></td> + <td><code>int</code></td> + <td>The database id of the logging event.</td> + </tr> + <tr class="b"> + <td><b>i</b></td> + <td><code>smallint</code></td> + <td>The index of the line in the full stack trace.</td> + </tr> + <tr class="a"> + <td><b>trace_line</b></td> + <td><code>varchar</code></td> + <td>The corresponding line</td> + </tr> + </table> + + <p> + To give a more visual example of the work done by <code>DBAppender</code>, here + is a screenshot of a MySQL database with content provided by <code>DBAppender</code>. + </p> + + <p>The <em>logging_event</em> table:</p> + + <img src="images/chapter4/dbAppenderLE.gif" alt="Logging Event table"></img> + + <p>The <em>logging_event_exception</em> table:</p> + + <img src="images/chapter4/dbAppenderLEException.gif" alt="Logging Event Exception table"></img> + + <p>The <em>logging_event_property</em> table:</p> + + <img src="images/chapter4/dbAppenderLEProperty.gif" alt="Logging Event Property table"></img> + + + <h4>ConnectionSource</h4> + + <p> + The <id>ConnectionSource</id> interface provides a pluggable means of + transparently obtaining JDBC Connections for logback classes that + require the use of a <code>java.sql.Connection</code>. There are currently + three implementations of <code>ConnectionSource</code>, namely + <code>DataSourceConnectionSource</code>, <code>DriverManagerConnectionSource</code> + and <code>JNDIConnectionSource</code>. + </p> + + <p> + The first example that we will review is a configuration using + <code>DriverManagerConnectionSource</code> and a MySQL database. + The following configuration file is what one would need. + </p> + +<em>Example 4.13: <code>DBAppender</code> configuration (logback-examples/src/main/java/chapter4/db/append-toMySQL-with-driverManager.xml)</em> +<div class="source"><pre><configuration> + + <b><appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> + <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> + <driverClass>com.mysql.jdbc.Driver</driverClass> + <url>jdbc:mysql://host_name:3306/datebase_name</url> + <user>username</user> + <password>password</password> + </connectionSource> + </appender></b> + + <root> + <level value="debug" /> + <appender-ref ref="DB" /> + </root> +</configuration></pre></div> + + <p> + The correct driver must be declared. Here, the <code>com.mysql.jdbc.Driver</code> + class is used. The <span class="option">url</span> must begin with <em>jdbc:myslq://</em>. + </p> + + <p> + The + <a href="../xref/ch/qos/logback/core/db/DriverManagerConnectionSource.html"> + <code>DriverManagerConnectionSource</code></a> is an implementation of + <code>ConnectionSource</code> that obtains the connection in the + traditional JDBC manner based on the connection URL. + </p> + <p> + Note that this class will establish a new <code>Connection</code> for + each call to <code>getConnection()</code>. It is recommended + that you either use a JDBC driver that natively supports + connection pooling or that you create your own + implementation of <code>ConnectionSource</code> that taps into + whatever pooling mechanism you are already using. (If you + have access to a JNDI implementation that supports + <code>javax.sql.DataSource</code>, e.g. within a J2EE application + server, see <code>JNDIConnectionSource</code>). + </p> + + + <p> + Connecting to a database using a <code>DataSource</code> is rather similar. + The configuration now uses + <a href="../xref/ch/qos/logback/core/db/DataSourceConnectionSource.html"> + <code>DataSourceConnectionSource</code></a>, + which is an implementation of <code>ConnectionSource</code> that obtains the + <code>Connection</code> in the recommended JDBC manner based on a + <code>javax.sql.DataSource</code>. + </p> + +<em>Example 4.14: <code>DBAppender</code> configuration (logback-examples/src/main/java/chapter4/db/append-with-datasource.xml)</em> +<div class="source"><pre><configuration> + + <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> + <b><connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> + + <dataSource class="${dataSourceClass}"> + </b><!-- Joran cannot substitute variables + that are not attribute values. Therefore, we cannot + declare the next parameter like the others. + --> + <b><param name="${url-key:-url}" value="${url_value}"/> + <serverName>${serverName}</serverName> + <databaseName>${databaseName}</databaseName> + </dataSource></b> + + <user>${user}</user> + <password>${password}</password> + </connectionSource> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="DB" /> + </root> +</configuration></pre></div> + + <p> + Not that in this configuration sample, we make heavy use of substitution variables. + They are sometimes handy when connection details have to be centralised in a + single configuration file and shared by logback and other frameworks. + </p> + + + + <p> + The third implementation of <code>ConnectionSource</code> that is shipped with + logback is the <code>JNDIConnectionSource</code>. + </p> + + <p> + The + <a href="../xref/ch/qos/logback/core/db/JNDIConnectionSource.html"> + <code>JNDIConnectionSource</code></a> + is an implementation of <code>ConnectionSource</code> that + obtains a <code>javax.sql.DataSource</code> from a JNDI provider + and uses it to obtain a <code>java.sql.Connection</code>. It is + primarily designed to be used inside of J2EE application + servers or application server clients, assuming the + application server supports remote access of <code>javax.sql.DataSource</code>. + In this way one can take advantage of connection pooling and whatever other goodies the + application server provides. + </p> + +<div class="source"><pre><connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> + <param name="jndiLocation" value="jdbc/MySQLDS" /> + <param name="username" value="myUser" /> + <param name="password" value="myPassword" /> +</connectionSource></pre></div> + + <p> + Note that this class will obtain an + <code>javax.naming.InitialContext</code> + using the no-argument constructor. This will usually work + when executing within a J2EE environment. When outside the + J2EE environment, make sure that you provide a + <em>jndi.properties</em> + file as described by your JNDI provider's documentation. + </p> + + <h4>Connection pooling</h4> + + <p> + Logging events can be created at a rather fast pace. To keep up + with the flow of events that must be inserted into a database, + it is recommanded to use connection pooling with + <code>DBAppender</code>. + </p> + + <p> + Experiment shows that using connection pooling with <code>DBAppender</code> + gives a big performance boost. With the following + configuration file, logging events are sent to a MySQL database, + without any pooling. + </p> +<em>Example 4.15: <code>DBAppender</code> configuration without pooling (logback-examples/src/main/java/chapter4/db/append-toMySQL-with-datasource.xml)</em> +<div class="source"><pre><configuration> + + <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> + <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> + <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"> + <serverName>${serverName}</serverName> + <port>${port$</port> + <databaseName>${dbName}</databaseName> + <user>${user}</user> + <password>${pass}</password> + </dataSource> + </connectionSource> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="DB" /> + </root> +</configuration</pre></div> + + <p> + With this configuration file, sending 500 logging events to + a MySQL database takes a whopping 5 seconds, that is + 10 miliseconds per requests. This figure is + unacceptable when dealing with large applications. + </p> + + <p> + A dedicated external library is necessary to use connection pooling + with <code>DBAppender</code>. The next example uses + <a href="http://sourceforge.net/projects/c3p0">c3p0</a>. To be able + to use c3p0, one must download it and place <em>c3p0-VERSION.jar</em> + in the classpath. + </p> + +<em>Example 4.16: <code>DBAppender</code> configuration with pooling (logback-examples/src/main/java/chapter4/db/append-toMySQL-with-datasource-and-pooling.xml)</em> +<div class="source"><pre><configuration> + + <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> + <connectionSource + class="ch.qos.logback.core.db.DataSourceConnectionSource"> + <b><dataSource + class="com.mchange.v2.c3p0.ComboPooledDataSource"> + <driverClass>com.mysql.jdbc.Driver</driverClass> + <jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl> + <user>${user}</user> + <password>${password}</password> + </dataSource></b> + </connectionSource> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="DB" /> + </root> +</configuration></pre></div> + + <p> + With this new configuration, sending 500 logging requests to + the same MySQL database as previously used takes around 0.5 seconds, + for an average time of 1 milisecond per request. + The gain is a <em>10</em> factor. + </p> + + <a name="SyslogAppender"></a> + <h3>SyslogAppender</h3> + + <p> + The syslog protocol is a very simple protocol: a syslog sender sends a small + message to a syslog receiver. + The receiver is commonly called <em>syslog daemon</em> or <em>syslog server</em>. + Logback can send messages to a remote syslog daemon. This is achieved by using + <a href="../xref/ch/qos/logback/classic/net/SyslogAppender.html"><code>SyslogAppender</code></a>. + </p> + + <p> + Here are its options: + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td> + <b> + <span class="option">SyslogHost</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + The host name of the syslog server. + </td> + </tr> + <tr class="b"> + <td> + <b> + <span class="option">Port</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + The port number on the syslog server to connect to. Normally, one would not want + to change the default value, that is <em>514</em>. + </td> + </tr> + <tr class="a"> + <td> + <b> + <span class="option">Facility</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + <p> + The <span class="option">Facility</span> is meant to identify + the source of a message. + </p> + <p> + The <span class="option">Facility</span> option must be set one + of the strings <em>KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, + CRON, AUTHPRIV, FTP, NTP, AUDIT, ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, + LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7</em>. Case is not important. + </p> + </td> + </tr> + <tr class="b"> + <td> + <b> + <span class="option">SuffixPattern</span> + </b> + </td> + <td> + <code>String</code> + </td> + <td> + <p> + The <span class="option">SuffixPattern</span> option specifies the format of the + non-standardized part the message sent to the syslog server. By default, its value + is <em>[%thread] %logger %msg %exception</em>. Any value that a <code>PatternLayout</code> + could use is a correct <span class="option">SuffixPattern</span>. + </p> + </td> + </tr> + </table> + + <p> + The syslog severity of a logging event is converted from the level of the logging event. + The <em>DEBUG</em> level is converted to <em>7</em>, <em>INFO</em> is converted to + <em>6</em>, <em>WARN</em> is converted to <em>4</em> and <em>ERROR</em> is converted + to <em>3</em>. + </p> + + <p> + Since the format of a syslog request follows rather strict rules, there is no layout + to be used with <code>SyslogAppender</code>. However, the using the + <span class="option">SuffixPattern</span> option lets the user display whatever + information she wishes. + </p> + + <p> + Here is a sample configuration using a <code>SyslogAppender</code>. + </p> + +<em>Example 4.17: <code>SyslogAppender</code> configuration (logback-examples/src/main/java/chapter4/conf/logback-syslog.xml)</em> +<div class="source"><pre><configuration> + + <appender name="SYSLOG" + class="ch.qos.logback.classic.net.SyslogAppender"> + <SyslogHost>remote_home</SyslogHost> + <Facility>AUTH</Facility> + <SuffixPattern>%-4relative [%thread] %-5level - %msg</SuffixPattern> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + When testing this configuration, one should verify that the remote syslog daemon + accepts requests from an external source. Experience shows that syslog daemons + usually deny such requests by default. + </p> + + + <a name="Access"></a> + <h2>Logback Access</h2> + + <p> + Most of the appenders found in logback classic can be used within + logback access. They function mostly in the same way as their logback + classic counterpart. In the next section, we will cover their use, but will + focuse on the differences with the classic appenders. + </p> + + <a name="AccessSocketAppender"></a> + <h3>SocketAppender</h3> + + <p> + The <a href="../xref/ch/qos/logback/access/net/SocketAppender.html"> + <code>SocketAppender</code></a> is designed to log to a + remote entity by transmitting serialized <code>AccessEvent</code> objects over the wire. + Remote logging is non-intrusive as far as the access event is concerned. + On the receiving end after de-serialization, the event can be logged as + if it were generated locally. + </p> + <p> + The options of access' <code>SocketAppender</code> are the same as those available + for classic's <code>SocketAppender</code>. + </p> + + <a name="AccessSMTPAppender"></a> + <h3>SMTPAppender</h3> + + <p> + Access' <a href="../xref/ch/qos/logback/access/net/SMTPAppender.html"> + <code>SMTPAppender</code></a> works in the same way as its Classic counterpart. + However, the <span class="option">evaluator</span> option is rather different. + By default, a <code>URLEvaluator</code> object + is used by <code>SMTPAppender</code>. This evaluator contains a list of URLs that are + checked agains the current request's URL. When one of the pages given to the + <code>URLEvaluator</code> is requested, <code>SMTPAppender</code> sends an email. + </p> + + <p> + Here is a sample configuration of a <code>SMTPAppender</code> in the access environnement. + </p> +<em>Example 4.18: <code>SMTPAppender</code> configuration (logback-examples/src/main/java/chapter4/conf/access/logback-smtp.xml)</em> +<div class="source"><pre><appender name="SMTP" + class="ch.qos.logback.access.net.SMTPAppender"> + <layout class="ch.qos.logback.access.html.HTMLLayout"> + <Pattern>%h%l%u%t%r%s%b</Pattern> + </layout> + + <b><Evaluator class="ch.qos.logback.access.net.URLEvaluator"> + <URL>url1.jsp</URL> + <URL>directory/url2.html</URL> + </Evaluator></b> + <From>sender_email@host.com</From> + <SMTPHost>mail.domain.com</SMTPHost> + <To>recipient_email@host.com</To> +</appender></pre></div> + + <p> + This way of triggering the email lets user select pages that are important steps + in a specific process, for example. + When such a page is accessed, the email is sent with the pages + that were accessed previously, and any information the user wants to be included + in the email. + </p> + + + + <a name="AccessDBAppender"></a> + <h3>DBAppender</h3> + + <p> + <a href="../xref/ch/qos/logback/access/db/DBAppender.html"><code>DBAppender</code></a> + is used to insert the access events into a database. + </p> + <p> + Two tables are used by <code>DBAppender</code>: <em>access_event</em> and + <em>access_event_header</em>. They all must exist before <code>DBAppender</code> + can be used. Logback ships with SQL scripts that will create the tables. + They can be found in the found in the + <em>logback-access/src/main/java/ch/qos/logback/access/db/dialect</em> directory. There + is a specific script for each of the most popular database systems. + If the script for your particular type of database system is missing, it should be + quite easy to write one, taking example on the already existing scripts. If + you send them to us, we will gladly include missing scripts in future releases. + </p> + + <p>The <em>access_event</em> table's fields are described below:</p> + + <table class="bodyTable"> + <tr class="a"> + <th>Field</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="b"> + <td><b>timestmp</b></td> + <td><code>big int</code></td> + <td>The timestamp that was valid at the access event's creation.</td> + </tr> + <tr class="a"> + <td><b>requestURI</b></td> + <td><code>varchar</code></td> + <td>The URI that was requested.</td> + </tr> + <tr class="b"> + <td><b>requestURL</b></td> + <td><code>varchar</code></td> + <td>The URL that was requested. This is a string composed of the request method, + the request URI and the request protocol. + </td> + </tr> + <tr class="a"> + <td><b>remoteHost</b></td> + <td><code>varchar</code></td> + <td>The name of the remote host.</td> + </tr> + <tr class="b"> + <td><b>remoteUser</b></td> + <td><code>varchar</code></td> + <td> + The name of the remote user. + </td> + </tr> + <tr class="a"> + <td><b>remoteAddr</b></td> + <td><code>varchar</code></td> + <td>The remote IP address.</td> + </tr> + <tr class="b"> + <td><b>protocol</b></td> + <td><code>varchar</code></td> + <td>The request protocol, like <em>HTTP</em> or <em>HTTPS</em>.</td> + </tr> + <tr class="a"> + <td><b>method</b></td> + <td><code>varchar</code></td> + <td>The request method, usually <em>GET</em> or <em>POST</em>.</td> + </tr> + <tr class="b"> + <td><b>serverName</b></td> + <td><code>varchar</code></td> + <td>The name of the server that issued the request.</td> + </tr> + <tr class="a"> + <td><b>event_id</b></td> + <td><code>int</code></td> + <td>The database id of the access event.</td> + </tr> + </table> + + <p> + The <em>access_event_header</em> table contains the header of each + requests. The information is organised as shown below: + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Field</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td><b>event_id</b></td> + <td><code>int</code></td> + <td>The database id of the corresponding access event.</td> + </tr> + <tr class="b"> + <td><b>header_key</b></td> + <td><code>varchar</code></td> + <td>The header name, for example <em>User-Agent</em>.</td> + </tr> + <tr class="a"> + <td><b>header_value</b></td> + <td><code>varchar</code></td> + <td>The header value, for example <em>Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0</em></td> + </tr> + </table> + + <p> + All options of classic's <code>DBAppender</code> are available + in access' <code>DBAppender</code>. The latter offers one more option, + described below. + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Option Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td> + <b> + <span class="option">insertHeaders</span> + </b> + </td> + <td> + <code>boolean</code> + </td> + <td> + Tells the <code>DBAppender</code> to populate the database with the header + information of all incoming requests. + </td> + </tr> + </table> + + <p> + Here is a sample configuration that uses <code>DBAppender</code>. + </p> +<em>Example 4.19: DBAppender configuration (logback-examples/src/main/java/chapter4/conf/access/logback-DB.xml)</em> +<div class="source"><pre><configuration> + + <appender name="DB" class="ch.qos.logback.access.db.DBAppender"> + <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> + <driverClass>com.mysql.jdbc.Driver</driverClass> + <url>jdbc:mysql://localhost:3306/logbackdb</url> + <user>logback</user> + <password>logback</password> + </connectionSource> + <insertHeaders>true</insertHeaders> + </appender> + + <appender-ref ref="DB" /> +</configuration></pre></div> + + + <a name="WriteYourOwnAppender"></a> + <h2>Writing your own Appender</h2> + + + <p>You can easily write your appender by sub-classing <code>AppenderBase</code>. + It handles support for filters, status among other functionality shared by most appenders. + The derived class only needs to implement one method, namely + <code>append(Object eventObject)</code>. + </p> + + <p>The <code>CountingConsoleAppender</code>, which we list next, appends a limited + number of incoming events on the console. It shuts down after the limit is reached. + It uses a <code>Layout</code> to format the events and accepts a parameter, + thus a few more methods are needed. + </p> + +<em>Example 4.20: <code>CountingConsoleAppender</code> (logback-examples/src/main/java/chapter4/CountingConsoleAppender.java)</em> +<div class="source"><pre>package chapter4; + +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; + + +public class CountingConsoleAppender extends AppenderBase<LoggingEvent> { + static int DEFAULT_LIMIT = 16; + int counter = 0; + int limit = DEFAULT_LIMIT; + + private Layout<LoggingEvent> layout; + + public CountingConsoleAppender() { + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getLimit() { + return limit; + } + + @Override + public void start() { + if (this.layout == null) { + addError("No layout set for the appender named ["+ name +"]."); + return; + } + + super.start(); + } + + public void append(LoggingEvent event) { + + if (counter >= limit) { + return; + } + + // output the events as formatted by our layout + System.out.print(this.layout.doLayout(event)); + + // prepare for next event + counter++; + } + + public Layout<LoggingEvent> getLayout() { + return layout; + } + + public void setLayout(Layout<LoggingEvent> layout) { + this.layout = layout; + } +}</pre></div> + + <p> + The <code>start()</code> method checks for the presence of a <code>Layout</code>. + In case none is found, the appender is not started. + </p> + + <p> + This custom appender illustrates a two points: + </p> + + <ul> + <li> + All options that follow the setter/getter JavaBeans conventions + are handled transparently. The <code>start()</code> method, that is + called automatically, has the responsability to check that the given + options are coherent. + </li> + <li> + The <code>AppenderBase.doAppend()</code> method invokes the append() + method of its derived classes where actual output operations occur. + It is in this method that appenders format events by invoking their layouts. + </li> + </ul> + + + The <code>CountingConsoleAppender</code> can be configured like any appender. + See sample file <em>logback-examples/src/main/java/chapter4/countingConsole.xml</em> + for an example. + + + +<script src="../templates/footer.js"></script> + + +</div> + +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/architecture.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/architecture.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,815 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 2: Architecture</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"><br /> + <h2>Chapter 2: + Architecture</h2> + <div class="author">Authors: Ceki Gülcü, + Sébastien Pennec </div> + <table class="bodyTable"> + <tbody> + <tr class="a"> + <td><a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"><img alt="Creative Commons License" style="border-width: 0pt;" src="http://creativecommons.org/images/public/somerights20.png" /> </a></td> + <td><p>Copyright © 2000-2006, QOS.ch</p> + <p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/">Creative + Commons + Attribution-NonCommercial-ShareAlike 2.5 + License </a>. </p></td> + </tr> + </tbody> + </table> + <h2>Logback architecture</h2> + <p>Logback's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, logback is + divided into three modules, Core, Classic and Access. </p> + <p> The <em>core</em> module lays the groundwork for the other + two + modules. The <em>classic</em> module extends <em>core</em>. + The + classic module corresponds to a significantly improved + version of log4j. Logback-classic natively implements the <a href="http://www.slf4j.org">SLF4J API</a> so that you + can + readily switch back and forth between logback and other logging + systems such as log4j or JDK14 Logging. The third module called <em>access</em> integrates with Servlet containers to + provide + HTTP-access log functionality. The access module will be covered + in a <a href="../access.html">separate document</a>. </p> + <p>In the reminder of this document, we will write "logback" to + refer to the + logback classic module. </p> + <h2>Logger, Appenders and Layouts</h2> + <p>Logback has three main types: <code>Logger</code>, <code>Appender</code> and <code>Layout</code>. + These three types of components work + together to enable developers to log messages according to + message type and level, and to control at runtime how these + messages are formatted and where they are reported. </p> + <p> The Logger class is part of the classic module. On the other + hand, the <code>Appender</code> and <code>Layout</code> interfaces are part of the core module. For the sake of genericity, + logback-core has no notion of loggers. </p> + <a name="LoggerContext"></a> + <h3>Logger context</h3> + <p>The first and foremost advantage of any logging API over plain <code>System.out.println</code> resides in its ability to + disable + certain log statements while allowing others to print + unhindered. This capability assumes that the logging space, that + is, the space of all possible logging statements, is categorized + according to some developer-chosen criteria. In logback, this + categorization is an inherent part of loggers. </p> + <p> Loggers are named entities. Their names are case-sensitive and + they follow the hierarchical naming rule: </p> + <div class="definition"> + <div class="deftitle">Named Hierarchy</div> + <p>A logger is said to be an ancestor of another logger if + its name followed by a dot is a prefix of the descendant + logger name. A logger is said to be a parent of a child + logger if there are no ancestors between itself and the + descendant logger. </p> + </div> + <p>For example, the logger named <code>"com.foo"</code> is a parent of the logger named <code>"com.foo.Bar"</code>. + Similarly, <code>"java"</code> is a parent of <code>"java.util"</code> and an ancestor of <code>"java.util.Vector"</code>. This naming scheme should + be familiar to most developers. </p> + <p> The root logger resides at the top of the logger hierarchy. It + is exceptional in that it is part of every hierarchy at its + inception. Like every logger, it can be retrieved by its name, + as follows: </p> + <div class="source"> + <pre>Logger rootLogger = LoggerFactory.getLogger(<a href="../apidocs/constant-values.html#ch.qos.logback.classic.LoggerContext.ROOT_NAME">LoggerContext.<em>ROOT_NAME</em></a>);</pre> + </div> + <p>All other loggers are also retrieved with the class static <code>getLogger</code> method found in the <a href="http://www.slf4j.org/api/org/slf4j/Logger.html">org.slf4j.LoggerFactory</a> class. This method takes the name of the desired logger as a + parameter. Some of the basic methods in the <code>Logger</code> interface are listed below. </p> + <div class="source"> + <pre>package org.slf4j; public interface Logger {<br /> // Printing methods: public void debug(String message);<br /> public void info(String message); public void warn(String message); <br /> public void error(String message); public void fatal(String message); <br />}</pre> + </div> + <p>Loggers may be assigned levels. The set of possible levels, + that + is DEBUG, INFO, WARN and ERROR are defined in the <code>ch.qos.logback.classic.Level</code> class. Note that + in + logback, the level class is final and cannot be derived, as a + much more flexible approach exist in the form of Marker objects. </p> + <p> If a given logger is not assigned a level, then it inherits + one from its closest ancestor with an assigned level. More + formally: </p> + <div class="definition"> + <div class="deftitle">Level Inheritance</div> + <p>The effective level for a given logger <em>L</em>, + is equal to + the first non-null level in its hierarchy, starting at <em>L</em> itself and proceeding upwards in the hierarchy + towards the root logger. </p> + </div> + <p>To ensure that all loggers can eventually inherit a level, the + root logger always has an assigned level. By default, this level + is DEBUG. </p> + <p> Below are four examples with various assigned level values and + the resulting effective (inherited) levels according to the + level inheritance rule. </p> + <em>Example 1</em> + <table class="bodyTable"> + <tbody> + <tr class="b"> + <th>Logger name </th> + <th> Assigned level </th> + <th> Effective level </th> + </tr> + <tr class="a"> + <td>root</td> + <td>DEBUG</td> + <td>DEBUG</td> + </tr> + <tr class="b"> + <td>X</td> + <td>none</td> + <td>DEBUG</td> + </tr> + <tr class="a"> + <td>X.Y</td> + <td>none</td> + <td>DEBUG</td> + </tr> + <tr class="b"> + <td>X.Y.Z</td> + <td>none</td> + <td>DEBUG</td> + </tr> + </tbody> + </table> + <p> In example 1 above, only the root logger is assigned a level. + This level value, <code>DEBUG</code>, is inherited by the + other + loggers <code>X</code>, <code>X.Y</code> and <code>X.Y.Z</code> </p> + <em>Example 2</em> + <table class="bodyTable"> + <tbody> + <tr class="a"> + <th>Logger name </th> + <th> Assigned level </th> + <th> Effective level </th> + </tr> + <tr class="b"> + <td>root</td> + <td>ERROR</td> + <td>ERROR</td> + </tr> + <tr class="a"> + <td>X</td> + <td>INFO</td> + <td>INFO</td> + </tr> + <tr class="b"> + <td>X.Y</td> + <td>DEBUG</td> + <td>DEBUG</td> + </tr> + <tr class="a"> + <td>X.Y.Z</td> + <td>WARN</td> + <td>WARN</td> + </tr> + </tbody> + </table> + <p>In example 2 above, all loggers have an assigned level value. + Level inheritence does not come into play. </p> + <em>Example 3</em> + <table class="bodyTable"> + <tbody> + <tr class="b"> + <th>Logger name </th> + <th> Assigned level </th> + <th> Effective level </th> + </tr> + <tr class="a"> + <td>root</td> + <td>DEBUG</td> + <td>DEBUG</td> + </tr> + <tr class="b"> + <td>X</td> + <td>INFO</td> + <td>INFO</td> + </tr> + <tr class="a"> + <td>X.Y</td> + <td>none</td> + <td>INFO</td> + </tr> + <tr class="b"> + <td>X.Y.Z</td> + <td>ERROR</td> + <td>ERROR</td> + </tr> + </tbody> + </table> + <p>In example 3 above, the loggers <code>root</code>, <code>X</code> and <code>X.Y.Z</code> are assigned the levels <code>DEBUG</code>, <code>INFO</code> and <code>ERROR</code> respectively. Logger <code>X.Y</code> inherits its level value from its parent <code>X</code>. </p> + <em>Example 4</em> + <table class="bodyTable"> + <tbody> + <tr class="a"> + <th>Logger name </th> + <th> Assigned level </th> + <th> Effective level </th> + </tr> + <tr class="b"> + <td>root</td> + <td>DEBUG</td> + <td>DEBUG</td> + </tr> + <tr class="a"> + <td>X</td> + <td>INFO</td> + <td>INFO</td> + </tr> + <tr class="b"> + <td>X.Y</td> + <td>none</td> + <td>INFO</td> + </tr> + <tr class="a"> + <td>X.Y.Z</td> + <td>none</td> + <td>INFO</td> + </tr> + </tbody> + </table> + <p>In example 4 above, the loggers <code>root</code> and <code>X</code> and are assigned the levels <code>DEBUG</code> and <code>INFO</code> respectively. The loggers <code>X.Y</code> and <code>X.Y.Z</code> inherit their level value from their + nearest + parent <code>X</code>, which has an assigned level. </p> + <a name="PrintintMethods"></a> + <h3>Printing methods</h3> + <p>By definition, the printing method determines the level of a + logging request. For example, if <code>L</code> is a + logger + instance, then the statement <code>L.info("..")</code> is + a + logging statement of level INFO. </p> + <p>A logging request is said to be <em>enabled</em> if its level + is higher than or equal to the level of its logger. Otherwise, the + request is said to be <em>disabled</em>. A logger without + an + assigned level will inherit one from the context. This rule is + summarized below. </p> + <div class="definition"> + <div class="deftitle">Basic Selection Rule</div> + <p>A log request of level <em>p</em> in a logger + with an + effective level <em>q</em>, is enabled if <em>p + >= q</em>. </p> + </div> + <p>This rule is at the heart of logback. It assumes + that levels are ordered as follows: <code>DEBUG < INFO + < WARN < ERROR< OFF</code>. </p> + <p>In a more graphic way, here is how the selection + rule works. In + the following table, the vertical header shows the the level of + the logging request, designated by <em>p</em>, while the + horizontal header shows effective level of the logger, designated + by <em>q</em>. </p> + <table class="bodyTable"> + <tbody> + <tr class="b"> + <th><span style=""><em>p</em>/<em>q</em></span></th> + <th>DEBUG</th> + <th>INFO</th> + <th>WARN</th> + <th>ERROR</th> + <th>OFF</th> + </tr> + <tr class="a"> + <th>DEBUG</th> + <td><span class="greenBold">YES</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + </tr> + <tr class="b"> + <th>INFO</th> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + </tr> + <tr class="a"> + <th>WARN</th> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="redBold">NO</span></td> + <td><span class="redBold">NO</span></td> + </tr> + <tr class="b"> + <th>ERROR</th> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="greenBold">YES</span></td> + <td><span class="redBold">NO</span></td> + </tr> + </tbody> + </table> + <p>Here is an example of + the basic selection rule.</p> + <div class="source"> + <pre>// get a logger instance named "com.foo", with an <span class="blue">INFO</span> level. <br />Logger logger = LoggerFactory.getLogger("com.foo");<br />//set its Level to <span class="blue">INFO</span><br />logger.setLevel(Level. <span class="blue">INFO</span>);<br />Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");<br />// This request is enabled, because <span class="green bold">WARN</span> >= <span class="blue">INFO</span><br />logger.<span class="green bold">warn</span>("Low fuel level.");<br />// This request is disabled, because <span class="green bold">DEBUG</span> < <span class="blue">INFO</span>. <br />logger.<span class="green bold">debug</span>("Starting search for nearest gas station.");<br />// The logger instance barlogger, named "com.foo.Bar", <br />// will inherit its level from the logger named <br />// "com.foo" Thus, the following request is enabled // because <span class="green bold">INFO</span> >= <span class="blue">INFO</span>. <br />barlogger.<span class="green bold">info</span>("Located nearest gas station.");<br />// This request is disabled, because <span class="green bold">DEBUG</span> < <span class="blue">INFO</span>. <br />barlogger.<span class="green bold">debug</span>("Exiting gas station search");</pre> + </div> + <a name="RetrievingLoggers"></a> + <h3>Retrieving Loggers</h3> + <p>Calling the <code><a href="../apidocs/org/slf4j/LoggerFactory.html#getLogger%28java.lang.String%29">LoggerFactory.getLogger</a></code> method with the same name will always return a reference to + the exact same logger object. </p> + <p>For example, in</p> + <div class="source"> + <pre>Logger x = LoggerFactory.getLogger("wombat"); <br />Logger y = LoggerFactory.getLogger("wombat");</pre> + </div> + <p> <code>x</code> and <code>y</code> refer to <em>exactly</em> the same logger object. </p> + <p> Thus, it is possible to configure a logger and then to + retrieve the same instance somewhere else in the code + without passing around references. In fundamental + contradiction to biological parenthood, where parents always + preceed their children, logback loggers can be + created and configured in any order. In particular, a + "parent" logger will find and link to its descendants even + if it is instantiated after them. </p> + <p> Configuration of the logback environment is typically done + at application initialization. The preferred way is by + reading a configuration file. This approach will be + discussed shortly. </p> + <p> Logback makes it easy to name loggers by <em>software + component</em>. This can be accomplished by instantiating a + logger in each class, with the logger name equal to the fully + qualified name of the class. This is a useful and + straightforward method of defining loggers. As the log output + bears the name of the generating logger, this naming strategy + makes it easy to identify the origin of a log message. However, + this is only one possible, albeit common, strategy for naming + loggers. Logback does not restrict the possible set of + loggers. As a developer, you are free to name loggers as you + wish. </p> + <p>Nevertheless, naming loggers after the class where + they are + located seems to be the best general strategy known so far. </p> + <a name="AppendersAndLayouts"></a> + <h3>Appenders and Layouts</h3> + <p>The ability to selectively enable or disable logging requests + based on their logger is only part of the picture. Logback + allows logging requests to print to multiple destinations. In + logback speak, an output destination is called an + appender. Currently, appenders exist for the console, files, + remote socket servers, to MySQL, PostgreSQL, Oracle and other + databases, JMS, and remote UNIX Syslog daemons. </p> + <p>More than one appender can be attached to a logger.</p> + <p> The <code><a href="../apidocs/ch/qos/logback/classic/Logger.html#addAppender%28ch.qos.logback.core.Appender%29">addAppender</a></code> method adds an appender to a + given logger. Each enabled logging request for a given logger + will be forwarded to all the appenders in that logger as well as + the appenders higher in the hierarchy. In other words, appenders are + inherited additively from the logger hierarchy. For example, if a + console appender is added to the root logger, then all enabled + logging requests will at least print on the console. If in + addition a file appender is added to a logger, say <em>L</em>, + then enabled logging requests for <em>L</em> and <em>L</em>'s + children will print on a file <em>and</em> on the console. + It is + possible to override this default behavior so that appender + accumulation is no longer additive by setting the additivity flag + of a logger to false. </p> + <p> The rules governing appender additivity are summarized + below. </p> + <div class="definition"> + <div class="deftitle">Appender Additivity</div> + <p>The output of a log statement of logger <em>L</em> will go to all the appenders in <em>L</em> and its ancestors. This is the meaning of the term + "appender additivity". </p> + <p> However, if an ancestor of logger <em>L</em>, say <em>P</em>, has the additivity flag set to false, then <em>L</em>'s output will be directed to all the appenders + in <em>L</em> and it's ancestors upto and including <em>P</em> but not the appenders in any of the ancestors of <em>P</em>. </p> + <p> Loggers have their additivity flag set to true by + default. </p> + </div> + The table below shows an example: + <table class="bodyTable"> + <tbody> + <tr class="a"> + <th>Logger Name</th> + <th>Attached Appenders</th> + <th>Additivity Flag</th> + <th>Output Targets</th> + <th>Comment</th> + </tr> + <tr class="b"> + <td>root</td> + <td>A1</td> + <td>not applicable</td> + <td>A1</td> + <td>Since the root logger stands at the top of the logger + hiearchy, the additivity flag does not apply to it. </td> + </tr> + <tr class="a"> + <td>x</td> + <td>A-x1, A-x2</td> + <td>true</td> + <td>A1, A-x1, A-x2</td> + <td>Appenders of "x" and of root.</td> + </tr> + <tr class="b"> + <td>x.y</td> + <td>none</td> + <td>true</td> + <td>A1, A-x1, A-x2</td> + <td>Appenders of "x" and of root.</td> + </tr> + <tr class="a"> + <td>x.y.z</td> + <td>A-xyz1</td> + <td>true</td> + <td>A1, A-x1, A-x2, A-xyz1</td> + <td>Appenders of "x.y.z", "x" and of root.</td> + </tr> + <tr class="b"> + <td>security</td> + <td>A-sec</td> + <td><span class="blue">false</span></td> + <td>A-sec</td> + <td>No appender accumulation since the additivity flag is set to <code>false</code>. Only appender A-sec will be used. </td> + </tr> + <tr class="a"> + <td>security.access</td> + <td>none</td> + <td>true</td> + <td>A-sec</td> + <td>Only appenders of "security" because the additivity + flag in "security" is set to <code>false</code>. </td> + </tr> + </tbody> + </table> + <p> More often than not, users wish to customize not only the + output destination but also the output format. This is + accomplished by associating a <em>layout</em> with an appender. The layout is responsible for formatting + the logging request according to the user's wishes, whereas + an appender takes care of sending the formatted output to + its destination. The <code>PatternLayout</code>, part of + the standard + logback distribution, lets the user specify the output + format according to conversion patterns similar to the C + language <code>printf</code> function. </p> + <p> For example, the PatternLayout with the conversion pattern + "%-4relative [%thread] %-5level %logger{32} - %msg%n" will output + something akin to: </p> + <div class="source"> + <pre>176 [main] DEBUG chapter2.HelloWorld2 - Hello world.</pre> + </div> + <p>The first field is the number of milliseconds elapsed since + the start of the program. The second field is the thread + making the log request. The third field is the level of the + log request. The fourth field is the name of the logger + associated with the log request. The text after the '-' is + the message of the request. </p> + <a name="ParametrizedLogging"></a> + <h3>Parameterized logging</h3> + <p>Given that loggers in logback-classic implement the <a href="http://www.slf4j.org/api/org/slf4j/Logger.html">SLF4J's + Logger interface</a>, certain printing methods admit more than + one parameter. These printing method variants are mainly + intended to improve performance while minimizing the impact on + the readability of the code. </p> + <p> For some Logger <code>logger</code>, writing, </p> + <div class="source"> + <pre>logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));</pre> + </div> + <p>incurs the cost of constructing the message parameter, that + is converting both integer <code>i</code> and <code>entry[i]</code> to a String, and concatenating intermediate strings. This, + regardless of whether the message will be logged or not. </p> + <p> One possible way to avoid the cost of parameter construction + is by surrounding the log statement with a test. Here is an + example. </p> + <div class="source"> + <pre>if(logger.isDebugEnabled()) { <br /> logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));<br />}</pre> + </div> + <p>This way you will not incur the cost of parameter + construction if debugging is disabled for <code>logger</code>. + On the other hand, if the logger is enabled for the DEBUG + level, you will incur the cost of evaluating whether the + logger is enabled or not, twice: once in <code>debugEnabled</code> and once in <code>debug</code>. This is an insignificant + overhead because evaluating a + logger takes less than 1% of the time it takes to actually + log a request. </p> + <h4>Better alternative</h4> + <p>There exists a convenient alternative based on message + formats. Assuming <code>entry</code> is an object, you can + write: </p> + <div class="source"> + <pre>Object entry = new SomeObject(); <br />logger.debug("The entry is {}.", entry);</pre> + </div> + <p>After evaluting whether to log or not, and only if the + decision + is positive, will the logger implementation format the message + and replace the '{}' pair with the string value of <code>entry</code>. In other words, this form does not + incur + the cost of parameter construction in case the log statement is + disabled. </p> + <p> The following two lines will yield the exact same output. + However, in case of a <em>disabled</em> logging statement, the second variant will outperform the first variant + by a + factor of at least 30. </p> + <div class="source"> + <pre>logger.debug("The new entry is "+entry+".");<br />logger.debug("The new entry is {}.", entry);</pre> + </div> + <p>A two argument variant is also availalble. For example, you + can write: </p> + <div class="source"> + <pre>logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);</pre> + </div> + <p>If three or more arguments need to be passed, an <code>Object[]</code> variant is also availalble. For + example, you + can write: </p> + <div class="source"> + <pre>Object[] paramArray = {newVal, below, above};<br />logger.debug("Value {} was inserted between {} and {}.", paramArray);</pre> + </div> + <a name="Configuration"></a> + <h3>Configuration</h3> + <p>Inserting log requests into the application code requires a + fair amount of planning and effort. Observation shows that + approximately four percent of code is dedicated to + logging. Consequently, even moderately sized applications will + contain thousands of logging statements embedded within its + code. Given their number, it becomes imperative to manage these + log statements without the need to modify them manually. </p> + <div class="highlight"> + <p>In order to run the examples in this introduction, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> for further details. </p> + </div> + <p>The logback environment is fully configurable + programmatically. + However, it is far more flexible to configure logback using + configuration files. In logback, configuration files are written + in XML format. </p> + <p>Existing log4j users can convert their <em>log4j.properties</em> files to <em>logback.xml</em> using our <a href="http://logback.qos.ch/translator/">PropertiesTranslator</a> web-application. </p> + <p> Configuring logback from a XML file is an easy task. One just needs to + instanciate a <code>JoranConfigurator</code> and pass the + configuration + file, as the following example demonstrate. </p> + <em>Example 2.1: Logback configuration from file (<a href="../xref/chapter2/MyAppWithConfigFile.html">logback-examples/src/main/java/chapter2/MyAppWithConfigFile.java</a>)</em> + <div class="source"> + <pre>package chapter2;<br />//Import SLF4J classes.<br />import org.slf4j.Logger;<br />import org.slf4j.LoggerFactory;<br />import ch.qos.logback.classic.LoggerContext;<br />import ch.qos.logback.classic.joran.JoranConfigurator;<br />import ch.qos.logback.core.util.StatusPrinter;<br /><br />public class MyAppWithConfigFile {<br /><br /> public static void main(String[] args) {<br /> Logger logger = LoggerFactory.getLogger(MyAppWithConfigFile.class);<br /> LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();<br /> JoranConfigurator configurator = new JoranConfigurator();<br /> configurator.setContext(lc);<br /> configurator.doConfigure(args[0]);<br /> logger.info("Entering application.");<br /> Bar bar = new Bar();<br /> bar.doIt();<br /> logger.info("Exiting application.");<br /> StatusPrinter.print(lc.getStatusManager());<br /> }<br />}</pre> + </div> + <p>This class defines a logger instance variable. It then + instantiates a <code>Bar</code> object. The <code>Bar</code> class is listed below: </p> + <em>Example 2.2: Sample logging class (<a href="../xref/chapter2/Bar.html">logback-examples/src/main/java/chapter2/Bar.java</a>)</em> + <div class="source"> + <pre>package chapter2;<br />import org.slf4j.Logger;<br />import org.slf4j.LoggerFactory;<br /><br />class Bar {<br /> Logger logger = LoggerFactory.getLogger(Bar.class); public void doIt() {<br /> logger.debug("doing my job");<br /> }<br />}</pre> + </div> + <p><em>MyAppWithConfigFile</em> configures logback by using the <code>JoranConfigurator</code>. Joran is a XML interpreter, + similar to the + commons-digester API, but offering several advantages over + commons-digester. Here, it parses the xml file and runs actions + depending on the tags it finds. To setup the <code>JoranConfigurator</code> properly, we passed the <code>LoggerContext</code>. A <code>LoggerContext</code> is the class that creates and + manages + Loggers in logback. It is also the class that implements the <code>org.slf4j.ILoggerFactory</code> interface. </p> + <p> All + other classes only need to retrieve an instance of <code>org.slf4j.Logger</code> by calling <code>LoggerFactory.getLogger()</code>, and then log away. + For + example, the only dependence of the <code>Bar</code> class + is on <code>org.slf4j.Logger</code> and <code>org.slf4j.LoggerFactory</code>. Except code that + configures + logback (if such code exists) user code does not need to depend on + logback, but on SLF4J instead. </p> + <p>Let us configure logback with the + next XML configuration file:</p> + <em>Example 2.3: Basic configuration with a xml file + (logback-examples/src/main/java/chapter2/sample-config-1.xml)</em> + <div class="source"> + <pre><?xml version="1.0" encoding="UTF-8" ?><br /><configuration><br /><br /> <appender name="STDOUT"<br /> class="ch.qos.logback.core.ConsoleAppender"><br /> <layout class="ch.qos.logback.classic.PatternLayout"><br /> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><br /> </layout><br /> </appender><br /><br /> <root><br /> <level value="debug" /><br /> <appender-ref ref="STDOUT" /><br /> </root><br /><br /></configuration></pre> + </div> + <p>We first created an <code>Appender</code>, named <em>STDOUT</em> that is of <code>ConsoleAppender</code> type. Its layout + is managed + by a <code>PatternLayout</code>, that uses the value of + the <em>pattern</em> parameter + to generate the logging statement. We then configured the root + logger, set its level to DEBUG, and linked the newly configured <code>ConsoleAppender</code> to the root logger.</p> + <p>Note that we've set the root logger level explicitly. Since + root + logger have a DEBUG level by default we could have omitted this.</p> + <p>To run this example, use this command:</p> + <div class="source"> + <pre>java chapter2.MyAppWithConfigFile src/main/java/chapter2/sample-config-1.xml</pre> + </div> + <p> Here is what you should see in the console: </p> + <div class="source"> + <pre>18:15:26.718 [main] INFO chapter2.MyAppWithConfigFile - Entering application.<br />18:15:26.718 [main] DEBUG chapter2.Bar - doing my job<br />18:15:26.718 [main] INFO chapter2.MyAppWithConfigFile - Exiting application.</pre> + </div> + <p>Logging to the console is a rather simple example. Let's now + configure logback so that it logs on the console, but also to a + custom file.</p> + <em>Example 2.4: Configuring logback with multiple appenders + (logback-examples/src/main/java/chapter2/sample-config-2.xml)</em> + <div class="source"> + <pre><?xml version="1.0" encoding="UTF-8" ?><br /><configuration><br /><br /> <appender name="STDOUT"<br /> class="ch.qos.logback.core.ConsoleAppender"><br /> <layout class="ch.qos.logback.classic.PatternLayout"><br /> <pattern>%-4relative [%thread] %-5level %class - %msg%n</pattern><br /> </layout><br /> </appender><br /><br /> <appender name="FILE"<br /> class="ch.qos.logback.core.FileAppender"><br /> <layout class="ch.qos.logback.classic.PatternLayout"><br /> <pattern>%-4relative [%thread] %-5level %class - %msg%n</pattern><br /> </layout><br /> <File>sample-log.txt</File><br /> </appender><br /><br /> <root><br /> <level value="debug" /><br /> <appender-ref ref="STDOUT" /><br /> <appender-ref ref="FILE" /><br /> </root><br /><br /></configuration></pre> + </div> + <p>Now, + all the logging statements are directed to the console and + to a file named <em>sample-log.txt</em>. As you can see, + the + configuration needed to add an Appender is rather small. The options + are declared as xml element, in either Appender configuration. They are + read and their value are assigned to the corresponding attribute in + the specified java class. </p> + <p>Suppose that we do not want to see the DEBUG level + statements in + the chapter2 package anymore. This is done by adding the following + bold xml snippet to the configuration file, right before the <code><root></code> element.</p> + <em>Example 2.5: Configuring a specific logger + (logback-examples/src/main/java/chapter2/sample-config-3.xml)</em> + <div class="source"> + <pre><?xml version="1.0" encoding="UTF-8" ?><br /><configuration><br /><br /> <appender name="STDOUT"<br /> class="ch.qos.logback.core.ConsoleAppender"><br /> <layout class="ch.qos.logback.classic.PatternLayout"><br /> <pattern>%-4relative [%thread] %-5level %class - %msg%n</pattern><br /> </layout><br /> </appender><br /> <appender name="FILE"<br /> class="ch.qos.logback.core.FileAppender"><br /> <layout class="ch.qos.logback.classic.PatternLayout"><br /> <pattern>%-4relative [%thread] %-5level %class - %msg%n</pattern><br /> </layout><br /> <File>sample-log.txt</File><br /> </appender><br /><b><br /></b> <b><logger name="chapter2"><br /></b> <b><level value="info" /><br /></b> <b></logger><br /></b><br /> <root><br /> <level value="debug" /><br /> <appender-ref ref="STDOUT" /><br /> <app ender-ref ref="FILE" /><br /> </root><br /><br /></configuration><br /> +</pre> + </div> + <p>Once done, the output is modified to show only statements of + level INFO and higher:</p> + <div class="source"> + <pre>0 [main] INFO chapter2.MyAppWithConfigFile - Entering application.<br />0 [main] INFO chapter2.MyAppWithConfigFile - Exiting application.</pre> + </div> + <p>Note + that to obtain these different logging behaviors we did not + need to recompile code. We could just as easily have logged to a UNIX + Syslog daemon, redirected all chapter2 output to a log visualizer, or + forwarded logging events to a remote logback server, which would log + according to local server policy, for example by forwarding the log + event to a second logback server.</p> + <p>Until now, we always had to specifically load the + configuration file and pass it + to a logback component. However, this step is not necessary in most + cases. When logback + is not configured by instanciating <code>JoranConfigurator</code> objects, it follows a simple policy to configure itself. </p> + <ul> + <li>Logback first tries to find a file called <em>logback.xml</em> within the classpath.</li> + <li>If no such file is found, it checks for another file called <em>logback-test.xml</em>.</li> + <li>In case none of these files are found, logback configures + itself automatically using the <a href="../xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code> </a> class.</li> + </ul> + <p> The first two checks allow for two environments to cooperate nicely. + When the application + using logback is in development and test process, a special file can be + used to setup + a logging environment that is developer-friendly. Once in production + environment, the presence of a <em>logback.xml</em> file + overrides any <em>logback-test.xml</em> configuration. </p> + <p> The last step is meant to provide very basic logging functionnality in + case no configuration + file is provided. In that case, the logging requests are output to the + console. </p> + <p> Letting logback load its configuration file is the most often used way + of configuring. It allows the user to only import SLF4J classes in her + code. </p> + <p>The last step of logback's configuration policy + permits the use of a + minimal + logging configuration right out of the box. Remember the very first + example of the introduction. The output was generated due to this + feature. </p> + <a name="UnderTheHood"></a> + <h3>A peak under the hood</h3> + <p>After we have introduced the essential logback components, we + are + now ready to describe the steps that the logback framework takes when + the user invokes a logger's printing method. Let us now analyze the + steps logback takes when the user invokes the <code>info()</code> method of a logger named <em>com.wombat</em>. </p> + <h4>1. Get the filter chain decision</h4> + <p>Logback's <code>TurboFilter</code> chain is + called. These filters may + be used to prodvide a context-wide threshold, or to filter out certain + events based on basic logging informations such as <code>Marker</code>, <code>Level</code>, <code>Logger</code>, + message, or the <code>Throwable</code> that was provided in the logging request. + If the reply of the filter chain is <code>FilterReply.DENY</code>, + then the + logging request is dropped. If it is <code>FilterReply.NEUTRAL</code>, + then + the next step is processed. In case the reply is <code>FilterReply.ACCEPT</code>, + the next step is skipped and the logging request is directly processed + to step 3. </p> + <h4>2. Apply the Logger level filter</h4> + <p>Logback compares the effective level of the <em>com.wombat</em> logger + with the level of the request (in this example: <em>INFO</em>). + If the logging + request is disabled, then logback will drop the request without further + processing. </p> + <h4>3. Create a <code>LoggingEvent</code> object</h4> + <p>If the request passed the previous filter, or if the <code>TurboFilter</code> chain + gave a <code>FilterReply.ACCEPT</code> result, logback + will create a <code>ch.qos.logback.classic.LoggingEvent</code> object + containing all the relevant parameters of the request such as the + logger of the request, the request + level, the message, the exception that might have been passed along the + request, + the current time, the current thread, several information about the + class that issued the logging request and the <code>MDC</code> map. Note that some of these fields + are initialized lazily, that is only when they are actually needed. </p> + <h4>4. Invoking appenders</h4> + <p>After the creation of a <code>LoggingEvent</code> object, logback will proceed to invoke the <code>doAppend()</code> methods of all the applicable appenders, that is, the appenders + inherited from the logger context. </p> + <p> All appenders shipped with the logback distribution extend the <code>AppenderBase</code> abstract class that implements the <code>doAppend</code> method in a synchronized block ensuring thread-safety. The <code>doAppend()</code> method of <code>AppenderBase</code> also invokes custom filters attached to the appender, if any such + filters exist. Custom filters, which can be dynamically attached to any + appender, are presented Chapter 6. </p> + <h4>5. Formatting the <code>LoggingEvent</code></h4> + <p>It is responsibility of the invoked appender to format the + logging + event. However, most (but not all) appenders delegate the task of + formatting the logging event to their layout. Their layout formats the <code>LoggingEvent</code> instance and returns the result as a String. Note that some appenders, + such as the <code>SocketAppender</code>, + do not transform the logging event into a string but serialize it + instead. Consequently, they do not require nor have a layout. </p> + <h4>6. Sending out the <code>LoggingEvent</code></h4> + <p>After the logging event is fully formatted it is sent to its + destination by each appender. </p> + <p> Here is a sequence UML diagram to show how everything works. You might + want to click on the image to display its bigger version. </p> + <a href="underTheHood.html"><img src="images/chapter2/underTheHoodSequence2_small.gif" /></a> <a name="Performance"></a> + <h3>Performance</h3> + <p>One of the often-cited arguments against logging is its + computational + cost. This is a legitimate concern as even moderately sized + applications can generate thousands of log requests. Much effort is + spent measuring and tweaking logging performance. + Independently of these efforts, the user should still be aware of the + following performance issues. </p> + <h4>1. Logging performance when logging is turned off + entirely</h4> + <p>You can turn off logging entirely by setting the level of the + root logger + to <code>Level.OFF</code>, + the highest possible level. When logging is turned off entirely, the + cost of a log request consists of a method invocation plus an integer + comparison. On a 3.2Ghz Pentium D machine this cost is typically around + 20 nanoseconds. </p> + <p>However, any method invocation involves the "hidden" cost of + parameter construction. For example, for some logger <em>x</em> writing, </p> + <div class="source"> + <pre>x.debug("Entry number: " + i + "is " + entry[i]);</pre> + </div> + <p> incurs the cost of constructing the message parameter, i.e. converting + both integer <code>i</code> and <code>entry[i]</code> to a string, and concatenating intermediate strings, regardless of + whether the message will be logged or not. </p> + <p>The cost of parameter construction can be quite high and + depends on the + size of the parameters involved. To avoid the cost of parameter + construction you can use logback's parametrized logging: </p> + <div class="source"> + <pre>x.debug("Entry number: {} is {}", i, entry[i]);</pre> + </div> + <p> This will not incur the cost of parameter construction. Compared to the + previous call to the <code>debug()</code> method, this call will be faster by a very wide margin. The message + will be formatted only if the request is processed to the appenders. If + it is processed, the component that formats the message offers high + performance and does not impact negatively the overall process. It + respectively takes 2 and 4 microseconds to format a message with 1 and + 3 parameters. </p> + <p>Please notice that, despite the performance points + that we just + discussed, inserting logging statements in tight-loops or very + frequently invoked code is a lose-lose proposal + and will not result in high performance. They will slow down your + application even if logging is turned off or generate massive (and + hence useless) output if enabled. </p> + <h4>2. The performance of deciding whether to log or not to log + when logging is turned on.</h4> + <p>In logback, there is no need to walk the whole logger + hierarchy. A logger knows + its effective level (that is, its level, once level inheritance has + been + taken into consideration) when it is created. Should the level of a + parent logger + be changed, then all child loggers will be contacted and handle the + change. Thus, before + accepting or denying a request based on the effective level, the logger + does not need + to search its ancestors. </p> + <p> Given this situation, it takes the same time to decide whether to log + or not when logging + is turned on as it takes when logging is turned off. </p> + <h4>3. Actual logging (formatting and writing to the + output device)</h4> + <p>This is the cost of formatting the log output and sending it + to its + target destination. Here again, a serious effort was made to make + layouts (formatters) perform as quickly as possible. The same is true + for appenders. The typical cost of actually logging is about 9 to 12 + microseconds when logging to a file on the local machine. + It goes up to 1 millisecond when logging to a database on a remote + server. </p> + <p>Although feature-rich, one of the foremost design + goals of logback + was speed of execution, a requirement which is second only to + reliability. Some logback components have been rewritten many times to + improve performance. </p> + +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/contextSelector.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/contextSelector.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,282 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 8: Context Selector</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"><br /> + <h2>Chapter 8: Context Selector</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a>. + + </p> + </td> + </tr> + </table> + +<h3>Introduction</h3> + +<p> +When working with several Web applications, all running on one server, the +multiplications of <code>LoggerContext</code> objects might reveal itself +a tricky issue. +</p> + +<p> +Logback provides a simple yet powerful way of dealing with multiple +contexts, without corruption of data, nor collusion between context +instances. +</p> + +<p> +One thing we know is that JNDI environments are independant. Thus +setting environment variables in each application will allow a given component +to know which application it is dealing with at the moment. This is basically +the mechanism that uses logback to provide easy access to the right +<code>LoggerContext</code> instance. +</p> + +<p> +The component that manages the different contexts is a +<a href="../xref/ch/qos/logback/classic/selector/ContextSelector.html"> +ContextSelector</a> +implementation. The JNDI-specific implementation is called +<a href="../xref/ch/qos/logback/classic/selector/ContextJNDISelector.html"> +ContextJNDISelector</a>. +</p> + +<p> +Each Web application provides two environment variables. One that specifies +the application's <code>LoggerContext</code> name, and one that provides the +path to the xml file that will be used to configure the context. +</p> + + +<h3>The server side</h3> + +<h4>Configuring Tomcat</h4> + +<p> +First, place the logback jars (that is logback-classic-<em>VERSION</em>.jar, +logback-core-<em>VERSION</em>.jar and slf4j-api-<em>VERSION</em>.jar) in the +server's shared class directory. In Tomcat, this directory is +<em>TOMCAT_HOME/common/lib/</em>. +</p> + +<p> +The next step is to let logback know that it will have to use JNDI to manage +the context instances. This is done thanks to a System Property. When launching +Tomcat, make sure that the <em>logback.ContextSelector</em> property is +set with the <em>JNDI</em> value. This can be done by editing the +<em>TOMCAT_HOME/bin/catalina.sh</em> or <em>TOMCAT_HOME/bin/catalina.bat</em> +file, and adding the following line to the java options: +</p> + +<div class="source"><pre>-Dlogback.ContextSelector=JNDI</pre></div> + +<h4>Configuring Jetty</h4> + +<p> +Configuring Jetty requires first to enable the use of JNDI. This is not a big +deal, since the Jetty distribution provides the configuration files needed to +achieve this task. The only thing to do is launch Jetty with the following command: +</p> + +<div class="source"><pre>java -jar start.jar etc/jetty.xml etc/jetty-plus.xml</pre></div> + +<p> +Note that you will need to install your appplications in the +<em>JETTY_HOME/webapps-plus</em> directory. +</p> + +<p>In Jetty, the server shared class directory is <em>JETTY_HOME/lib/</em>. +This is where you will need to place the logback jars +(that is logback-classic-<em>VERSION</em>.jar, +logback-core-<em>VERSION</em>.jar and slf4j-api-<em>VERSION</em>.jar). +</p> + +<p> +The next step is to let logback know that it will have to use JNDI to manage +the context instances. This is done thanks to a System Property. +In Jetty, adding an environment variable is done by adding the following +xml element in the <em>JETTY_HOME/etc/jetty.xml</em> configuration file, +nested in a <em>Configuration</em> element: +</p> + +<div class="source"><pre><Call class="java.lang.System" name="setProperty"> + <Arg>logback.ContextSelector</Arg> + <Arg>JNDI</Arg> +</Call></pre></div> + +<p> +Be aware that adding a <em>-Dlogback.ContextSelector=JNDI</em> to the java +command when starting the server will not work. By doing this, the +<code>LoggerFactory</code> instanciated by the server for its internal logging +will try to use JNDI, when only the Web applications should attempt to retrieve +their <code>LoggerContext</code> this way. +</p> + +<h3>Configuring each Web application</h3> + +<p> +While each Web application will need the logback jars to compile, they need not +nor should be placed within the Web application's WAR file, except if you are +using Jetty. +</p> + +<p>This is due to <a href="http://docs.codehaus.org/display/JETTY/Classloading"> +Jetty's internal Classloading mechanism</a>. +Consequently, the <em>logback-classic-VERSION.jar</em> +and <em>slf4j-api-VERSION.jar</em> files should also be placed in the <em>WEB-INF/lib/</em> +directory of your webapps when running Jetty. +</p> + +<p> +In each Web application's <em>web.xml</em> file, two JNDI environment entries +are needed. The first one specifies the desired name of the application's +<code>LoggerContext</code>. It takes the following form: +</p> + +<div class="source"><pre><env-entry> + <description>JNDI logging context for this app</description> + <env-entry-name>logback/context-name</env-entry-name> + <env-entry-type>java.lang.String</env-entry-type> + <env-entry-value>ContextApp-A</env-entry-value> +</env-entry></pre></div> + +<p> +The second JNDI entry will lead logback to the application's own xml configuration +file. It can be declared as shown below: +</p> + +<div class="source"><pre><env-entry> + <description>URL for configuring logback context</description> + <env-entry-name>logback/configuration-resource</env-entry-name> + <env-entry-type>java.lang.String</env-entry-type> + <env-entry-value>logback-app-A.xml</env-entry-value> +</env-entry></pre></div> + +<p> +Specifying only the name of the file will lead logback to search for it in +the Web application's <em>WEB-INF/classes/</em> directory. +</p> + +<p> +When the Web application is recycled or shutdown, it is very often +useful to recycle the associated <code>LoggerContext</code>. This can +be done by installing a <code>ServletContextListener</code> which will +detach the context from the <code>ContextSelector</code> and shut it down. +</p> + +<p> +The <a href="../xref/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.html"> +<code>ContextDetachingSCL</code></a> class which +ships with logback does exactly that. To use it, add the following +lines to your Web application's <em>web.xml</em> file. +</p> + +<div class="source"><pre><listener> + <listener-class>ch.qos.logback.classic.selector.servlet.ContextDetachingSCL</listener-class> +</listener</pre></div> + + +<p> +Using the <code>ContextJNDISelector</code> might slow down your +application, because of the JNDI call that is issued each time +a <code>LoggerContext</code> is required. To prevent the cost +of this call, logback ships with a <code>LoggerContextFilter</code> +component. This filter is a <code>javax.servlet.Filter</code> implementation +that gets the environment-specific <code>LoggerContext</code> and sets it +in a <code>ThreadLocal</code> variable. Each time +the <code>ContextSelector</code> will be called to provide the +Web application's own <code>LoggerContext</code>, it will first check +if the <code>ThreadLocal</code> variable is set. If it is, then the call +to the JNDI environment will not be issued. The <code>LoggerContextFilter</code> +class increases the performances by a wide margin. +</p> + +<p> +Like all servlet filters, the +<a href="../xref/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.html"> +<code>LoggerContextFilter</code></a> can act +before and after the Web application's process. This allows the filter +to set the <code>ThreadLocal</code> variable at the beginning of the process +and to remove it once the Web application has finished processing the request. +This behaviour permits the thread to be recycled for use by another Web +application and still provide the correct <code>LoggerContext</code>. +</p> + +<p>The <code>LoggerContextFilter</code> can be used by adding the following +lines to your Web application's <em>web.xml</em> file. +</p> + +<div class="source"><pre><filter> + <filter-name>LoggerContextFilter</filter-name> + <filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class> +</filter> +<filter-mapping> + <filter-name>LoggerContextFilter</filter-name> + <url-pattern>/*</url-pattern> +</filter-mapping></pre></div> + +<h4>Some recommandations</h4> + +<p> +To avoid confusion, it is prudent to name each Web application +in the <em>web.xml</em> file, as in: +</p> + +<div class="source"><pre><display-name>Name_Of_My_WebApp</display-name></pre></div> + +<p> +We recommend that you name logback configuration resources uniquely. In +particualar, avoid naming the logback configuration resource as +<em>logback.xml</em> for a non-default logger context. +</p> + +<p> +While trying to configure the Web application logback would search for +the resource <em>logback.xml</em> using the thread context classloader. Thus, +it would first attempt to locate <em>logback.xml</em> file using the +classloader specific to the Web application. However, if the file +<em>logback.xml</em> did not exist there (if you forgot to put a custom one in +<em>WEB-INF/classes</em>), and if the file <em>logback.xml</em> existed higher up in the +classloader tree, we could end up in a situation where the logger +context for your Web application would be configured using the same +file as that used to configure the default context. Such +involuntary sharing of the same configuration by multiple repositories +will result in corrupt log output. +</p> +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/filters.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/filters.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,770 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 6: Filters</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> <h2>Chapter 6: Filter chains</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a>. + + </p> + </td> + </tr> + </table> + + <p> + As we have seen, logback has several built-in ways for filtering log requests, + including the context-wide filter, logger-level selection rule and appender filters. + These provide high performance filtering for the most commonly encountered + cases. These filters are largely inspired from Linux ipchains or + iptables as they are called in more recent Linux kernels. + Logback filters are based on ternary logic allowing them to be assembled or chained + together to compose an arbitrarily complex filtering policy. + </p> + + <div class="highlight"> + <p> + In order to run the examples in this chapter, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + + <p> + There are two main types of filters, namely <code>Filter</code> and + <code>TurboFilter</code>. + </p> + + <h2>Logback Classic</h2> + + <a name="Filter"></a> + <p><code>Filter</code> objects all implement the + <a href="../xref/ch/qos/logback/core/filter/Filter.html"><code>Filter</code></a> + abscract class. The <code>decide(Object event)</code> method is passed a + newly created <code>LoggingEvent</code> object. + </p> + + <h3>Filter chains</h3> + <p> + This abstract class assumes that filters be organized in a linear chain. + Its member field next points to the next filter in the chain, or + <code>null</code> if there are no further filters in the chain. + Figure 6.1 depicts a sample filter chain consisting of three filters. + </p> + + <img src="images/chapter6/filterChain.gif" alt="A sample filter chain"></img> + + <p> + Filters are based on ternary logic. The <code>decide(Object event)</code> + method of each filter is called in sequence. This method returns one of the + enumerations <code>FilterReply.DENY</code>, <code>FilterReply.NEUTRAL</code> or + <code>FilterReply.ACCEPT</code>. If the returned value is <code>FilterReply.DENY</code>, + then the log event is dropped immediately without consulting the + remaining filters. If the value returned is <code>FilterReply.NEUTRAL</code>, + then the next filter in the chain is consulted. If there are no further filters + to consult, then the logging event is processed normally. + If the returned value is <code>FilterReply.ACCEPT</code>, then the logging + event is processed immediately skipping the remaining filters. + </p> + + <p> + In logback-classic, <code>Filter</code> objects can only be added to <code>Appender</code> + instances. By adding filters to an appender you can filter events by various + criteria, such as the contents of the log message, the contents of the MDC, + the time of day or any other part of the logging event. + </p> + + <h3>Implementing your own Filter</h3> + + <p> + Creating your own filter is not difficult. All you have to do is extend the <code>Filter</code> + abstract class. The only method that you will have to implement is the <code>decide()</code> + method, allowing you to contentrate only on the behaviour of your filter. + </p> + + <p> + The next class is all it takes to implement one's own filter. All it does is accept + logging events who's message contains the String <em>sample</em>. The filter will give a + neutral response to any logging event who's message does not contain this String. + </p> + +<em>Example 6.1: Basic custom filter (<a href="../xref/chapter6/SampleFilter.html">logback-examples/src/main/java/chapter6/SampleFilter.java</a>)</em> +<div class="source"><pre>package chapter6; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; + +public class SampleFilter extends Filter { + + @Override + public FilterReply decide(Object eventObject) { + LoggingEvent event = (LoggingEvent)eventObject; + + if (event.getMessage().contains("sample")) { + return FilterReply.ACCEPT; + } else { + return FilterReply.NEUTRAL; + } + } +}</pre></div> + + <p> + What is shown above might be the simplest filter. Like any filter, it + can be attached to any appender using the <Filter> element, as + shown below: + </p> + +<em>Example 6.2: SampleFilter configuration (logback-examples/src/main/java/chapter6/SampleFilterConfig.xml)</em> +<div class="source"><pre><configuration> + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <b><Filter class="chapter6.SampleFilter" /></b> + + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern> + %-4relative [%thread] %-5level %logger - %msg%n + </pattern> + </layout> + </appender> + + <root> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + Thanks to Joran, logback's powerful configuration framework, adding + an option to such a filter is very easy. Just add the corresponding + getter and setter methods in the class, and you can specify the + option's value in an xml element, nested within the <em>filter</em> element. + </p> + + <p> + In case you want to implement a filter that provides different behaviour + depending on the result of its test (say, a filter that would accept or deny + an event depending on the content of its message), you can extend the + <a href="../xref/ch/qos/logback/core/filter/AbstractMatcherFilter.html"> + <code>AbstractMatcherFilter</code></a> class. It will provide your filter with + two attribute: <em>OnMatch</em> and <em>OnMismatch</em>, that can be configured + like any other option. + </p> + + <h3>Logback Filters</h3> + + <p> + As the moment, there are two filters that ship with logback. + <a href="../xref/ch/qos/logback/classic/LevelFilter.html"> + <code>LevelFilter</code></a> provides event filtering based on a <code>Level</code> value. + It the event's level is equal to the configured level, the filter accepts of denies + the event, depending on its configuration. It allows you to choose the + behaviour of logback for a precise given level. Here is a sample configuration that + uses <code>LevelFilter</code>. + </p> + +<em>Example 6.3: Sample LevelFilter configuration (logback-examples/src/main/java/chapter6/LevelFilterConfig.xml)</em> +<div class="source"><pre><configuration> + <appender name="CONSOLE" + class="ch.qos.logback.core.ConsoleAppender"> + <b><filter class="ch.qos.logback.classic.filter.LevelFilter"> + <level>INFO</level> + <onMatch>ACCEPT</onMatch> + <onMismatch>DENY</onMismatch> + </filter></b> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern> + %-4relative [%thread] %-5level %logger{30} - %msg%n + </pattern> + </layout> + </appender> + <root> + <level value="DEBUG" /> + <appender-ref ref="CONSOLE" /> + </root> +</configuration></pre></div> + + <p> + The second filter that ships with logback is + <a href="../xref/ch/qos/logback/classic/ThresholdFilter.html"> + <code>ThresholdFilter</code></a>. + It is also based on level value, but acts as a threshold to deny any request + whose level is not equal or greater to the configured level. A sample + use of the <code>ThresholdFilter</code> is shown below. + </p> + +<em>Example 6.4: Sample ThresholdFilter configuration (logback-examples/src/main/java/chapter6/ThresholdFilterConfig.xml)</em> +<div class="source"><pre><configuration> + <appender name="CONSOLE" + class="ch.qos.logback.core.ConsoleAppender"> + <b><filter class="ch.qos.logback.classic.filter.ThresholdFilter"> + <level>INFO</level> + </filter></b> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern> + %-4relative [%thread] %-5level %logger{30} - %msg%n + </pattern> + </layout> + </appender> + <root> + <level value="DEBUG" /> + <appender-ref ref="CONSOLE" /> + </root> +</configuration></pre></div> + + <h3>Evaluator Filters</h3> + + <p> + A special category of filters ships with logback. The + <a href="../xref/ch/qos/logback/core/filter/EvaluatorFilter.html"> + <code>EvaluatorFilter</code></a> objects use an + <a href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html"> + <code>EventEvaluator</code></a> + to decide wether to accept or deny the request. This allows unprecedented + flexibility in the way that you can affect the logging event's filtering. + </p> + + <p> + Creating a customized filter that makes use of <code>EventEvaluator</code> objects + works the same way as seen previously, except that one must extend the + <code>EvaluatorFilter</code> class, instead of the <code>Filter</code> + or <code>AbstractMatcherFilter</code> classes. + </p> + + <a name="EventEvaluator"></a> + <h3>Event Evaluators</h3> + + <p> + Events evaluators allow the user to enter java expressions, using + components of a logging event, and to check each logging event + against the compiled expression. + </p> + + <p> + Let's see a sample configuration. + </p> + +<em>Example 6.5: Basic event evaluator usage (logback-examples/src/main/java/chapter6/basicEventEvaluator.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <b><filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="myEval"> + <expression>message.contains("billing")</expression> + </evaluator> + <OnMismatch>NEUTRAL</OnMismatch> + <OnMatch>DENY</OnMatch> + </filter></b> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern> + %-4relative [%thread] %-5level %logger - %msg%n + </pattern> + </layout> + </appender> + + <root> + <level value="INFO" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + The bold part in the previous configuration adds an <code>EvaluatorFilter</code> + to a <code>ConsoleAppender</code>. An <code>EventEvaluator</code> is then given to + the filter. The <em>expression</em> element contains a recognizable java expression. + Notice that the <em>message</em> variable is defined implicitly. Logback provides + access to the internal components of a logging event and lets the user build her + expression at will. + </p> + + <p> + The implicit variables available to the <code>EventEvaluator</code> are described below: + </p> + + <table class="bodyTable"> + <tr class="b"> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + <tr class="a"> + <td>event + </td> + <td><code>LoggingEvent</code></td> + <td>The logging event associated with the logging request. + All of the following variables are also available from the event. For example, + <code>event.getMessage()</code> returns the same String value as the <em>message</em> + variable. + </td> + </tr> + <tr class="b"> + <td>message + </td> + <td><code>String</code></td> + <td>The message created with the logging request. + </td> + </tr> + <tr class="a"> + <td>logger + </td> + <td><code>LoggerRemoteView</code></td> + <td>This object can be treated like a usual logger. In case the logging event + is serialized and sent to a remote machine, the usual logger object is + dropped and replaced by a <code>LoggerRemoteView</code> object, which + performs much better when serialized. + </td> + </tr> + <tr class="b"> + <td>level + </td> + <td><code>int</code></td> + <td>The int value corresponding to the level. To help create easily + expressions involving levels, the default value <em>DEBUG</em>, + <em>INFO</em>, <em>WARN</em> and <em>ERROR</em> are also available. Thus, + using <em>level > INFO</em> is a correct expression. + </td> + </tr> + <tr class="a"> + <td>timeStamp + </td> + <td><code>long</code></td> + <td>The timestamp corresponding to the logging event's creation. + </td> + </tr> + <tr class="b"> + <td>marker + </td> + <td><code>Marker</code></td> + <td>The <code>Marker</code> object associated with the logging request. + </td> + </tr> + <tr class="a"> + <td>mdc + </td> + <td><code>Map</code></td> + <td>A map containing all the MDC values at the time of the + creation of the logging event. A value can be access by using the + following expression: <em>mdc.get("myKey")</em>. + </td> + </tr> + <tr class="b"> + <td>throwable + </td> + <td><code>Throwable</code></td> + <td>The exception that was passed to the logger when it + was requested. + </td> + </tr> + </table> + + <p> + The behaviour of the filter is also defined by its <span class="option">OnMatch</span> + and <span class="option">OnMismatch</span> options. The configuration specifies thanks + to these elements the replies that the <code>EvaluatorFilter</code> must give once its + expression has been evaluated. The example above returns the value <code>FilterReply.ACCEPT</code> + when the message of the logging event contains the String <em>important</em>. + If <em>important</em> is not contained in the message, then the filter lets the next filter + evaluate this logging event. + </p> + + <p> + Let us see an example of <code>EvaluatorFilter</code>. The <code>FilterEvents</code> + class issues ten logging requests, numbered from 0 to 9. + </p> + + <p> + First, let us run the <code>FilterEvents</code> class with a configuration that does + not contain any filters. This can be done by issuing the following command: + </p> + +<div class="source"><pre> +java chapter6.FilterEvents src/main/java/chapter6/basicConfiguration.xml +</pre></div> + + <p> + All requests will be displayed, as shown below: + </p> + +<div class="source"><pre>0 [main] INFO chapter6.FilterEvents - logging statement 0 +0 [main] INFO chapter6.FilterEvents - logging statement 1 +0 [main] INFO chapter6.FilterEvents - logging statement 2 +0 [main] DEBUG chapter6.FilterEvents - logging statement 3 +0 [main] INFO chapter6.FilterEvents - logging statement 4 +0 [main] INFO chapter6.FilterEvents - logging statement 5 +0 [main] ERROR chapter6.FilterEvents - <b>billing statement 6</b> +0 [main] INFO chapter6.FilterEvents - logging statement 7 +0 [main] INFO chapter6.FilterEvents - logging statement 8 +0 [main] INFO chapter6.FilterEvents - logging statement 9</pre></div> + + <p> + Suppose that we want to get rid of the billing information. We + can use an <code>EvaluatorFilter</code> configured as follows: + </p> + +<div class="source"><pre><configuration> + ... + <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="myEval"> + <expression>message.contains("billing")</expression> + </evaluator> + <OnMismatch>NEUTRAL</OnMismatch> + <OnMatch>DENY</OnMatch> + </filter> + ... +</configuration></pre></div> + + <p> + This filter will deny any logging event whose message + contains the String <em>billing</em>. If we run the <code>FilterEvents</code> + class again, we obtain the following output: + </p> + +<div class="source"><pre>0 [main] INFO chapter6.FilterEvents - logging statement 0 +0 [main] INFO chapter6.FilterEvents - logging statement 1 +0 [main] INFO chapter6.FilterEvents - logging statement 2 +0 [main] DEBUG chapter6.FilterEvents - logging statement 3 +0 [main] INFO chapter6.FilterEvents - logging statement 4 +0 [main] INFO chapter6.FilterEvents - logging statement 5 +0 [main] INFO chapter6.FilterEvents - logging statement 7 +0 [main] INFO chapter6.FilterEvents - logging statement 8 +0 [main] INFO chapter6.FilterEvents - logging statement 9</pre></div> + + + <a name="TurboFilter"></a> + <h3>TurboFilters</h3> + + <p> + <code>TurboFilter</code> objects all extend the + <a href="../xref/ch/qos/logback/classic/turbo/TurboFilter.html"> + <code>TurboFilter</code></a> abstract class. Like the usual filters, they + use ternary logic to return their evaluation of the logging event. + </p> + + <p> + Overall, they work much like the previously mentionned filters. However, + there are two main differences between <code>Filter</code> and + <code>TurboFilter</code> objects. + </p> + + <p> + <code>TurboFilter</code> objects are tied to the logging context. Hence, they + are called not only when a given appender is used, but each and every time a logging + request is issued. Their scope is wider than appender-attached filters. + </p> + + <p> + More importantly, they are called before the <code>LoggingEvent</code> object creation. + Their decision is made based on some of the logging event's components. They require + no logging event instanciation, nor any other treatement to provide their + filtering functionnalities. They are much more performant than the usual + <code>Filter</code> objects. + </p> + + <h3>Implementing your own TurboFilter</h3> + + <p> + To create your own <code>TurboFilter</code> component, just extend the + <code>TurboFilter</code> abstract class. As previously, when implementing + a custumized filter object, developing a custom <code>TurboFilter</code> only + ask that one implement the <code>decide()</code> method. In the next example, we + create a slightly more complex filter: + </p> + +<em>Example 6.6: Basic custom <code>TurboFilter</code> (<a href="../xref/chapter6/SampleTurboFilter.html">logback-examples/src/main/java/chapter6/SampleTurboFilter.java</a>)</em> +<div class="source"><pre>package chapter6; + +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.turbo.TurboFilter; +import ch.qos.logback.core.spi.FilterReply; + +public class SampleTurboFilter extends TurboFilter { + + String marker; + Marker markerToAccept; + + @Override + public FilterReply decide(Marker marker, Logger logger, Level level, + String format, Object[] params, Throwable t) { + + if (!isStarted()) { + return FilterReply.NEUTRAL; + } + + if ((markerToAccept.equals(marker))) { + return FilterReply.ACCEPT; + } else { + return FilterReply.NEUTRAL; + } + } + + public String getMarker() { + return marker; + } + + public void setMarker(String markerStr) { + this.marker = markerStr; + } + + @Override + public void start() { + if (marker != null && marker.trim().length() > 0) { + markerToAccept = MarkerFactory.getMarker(marker); + super.start(); + } + } +} +</pre></div> + + <p> + The <code>TurboFilter</code> above accepts events that contain a specific marker. + If said marker is not found, then the filter passes the responsability to + the next filter in the chain. + </p> + + <p> + To allow more flexibility, the marker that will be tested can be specified + in the configuration file. Hence the getter and setter methods. We also implemented + the <code>start()</code> method, to check that the option has been specified during the + configuration process. + </p> + + <p> + Here is a sample configuration that makes use of the newly created <code>TurboFilter</code>. + </p> + +<em>Example 6.7: Basic custom <code>TurboFilter</code> configuration (logback-examples/src/main/java/chapter6/sampleTurboFilterConfig.xml)</em> +<div class="source"><pre><configuration> + <b><turboFilter class="chapter6.SampleTurboFilter"> + <Marker>sample</Marker> + </turboFilter></b> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern> + %-4relative [%thread] %-5level %logger - %msg%n + </pattern> + </layout> + </appender> + + <root> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + Logback classic ships with several <code>TurboFilter</code> classes ready for use. + The + <a href="../xref/ch/qos/logback/classic/turbo/MDCFilter.html"><code>MDCFilter</code></a> + check the presence of a given value in the MDC. On the other hand, + <a href="../xref/ch/qos/logback/classic/turbo/MarkerFilter.html"><code>MarkerFilter</code></a> + checks for the presence of a specific marker associated with the logging request. + </p> + + <p> + Here is a sample configuration, using both <code>MDCFilter</code> and + <code>MarkerFilter</code>. + </p> + +<em>Example 6.8: <code>MDCFilter</code> and <code>MarkerFilter</code> +configuration (logback-examples/src/main/java/chapter6/turboFilters.xml)</em> +<div class="source"><pre><configuration> + + <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"> + <MDCKey>username</MDCKey> + <Value>sebastien</Value> + <OnMatch>ACCEPT</OnMatch> + </turboFilter> + + <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> + <Marker>billing</Marker> + <OnMatch>DENY</OnMatch> + </turboFilter> + + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%date [%thread] %-5level %logger - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="info"/> + <appender-ref ref="console" /> + </root> +</configuration></pre></div> + + <p> + You can see this configuration in action by issuing the following command: + </p> + +<div class="source"><pre> +java chapter6.FilterEvents src/main/java/chapter6/turboFilters.xml +</pre></div> + + <p> + As we've seen previously, the <code>FilterEvents</code> class creates 10 logging requests, + each with its number from 0 to 9. All of the requests are of level <em>INFO</em>, + just like the configured overall level, except for two requests. + The 3rd request, is a <em>DEBUG</em> level corresponding to the key <em>username</em>. + This obviously satisfies the first <code>TurboFilter</code> declared in the previous + configuration file. The 6th request, a <em>ERROR</em> level request, + which is issued along with the <em>billing</em> marker, matches + the second <code>TurboFilter</code>. + </p> + + <p> + Here is the output of the previous command: + </p> + +<div class="source"><pre> +2006-12-04 15:17:22,859 [main] INFO chapter6.FilterEvents - logging statement 0 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 1 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 2 +2006-12-04 15:17:22,875 [main] DEBUG chapter6.FilterEvents - logging statement 3 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 4 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 5 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 7 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 8 +2006-12-04 15:17:22,875 [main] INFO chapter6.FilterEvents - logging statement 9 +</pre></div> + + + <p> + One can see that the 3rd request, who should not be displayed if we + only followed the overall <em>INFO</em> level, appears anyway, because + it matched the first <code>TurboFilter</code> requirements and was accepted. + </p> + + <p> + On the other hand, the 6th request, that is a <em>ERROR</em> level request + should have been displayed. But it satisfied the second <code>TurboFilter</code> + whose <span class="option">OnMatch</span> option is set to <em>DENY</em>. + Thus, the 6th request was not displayed. + </p> + + + <h2>Logback Access</h2> + + <p> + Logback access benefits from most of the possibilities available + to the classic module. <code>Filter</code> objects are available and work + in the same way as their classic counterpart. They handle access' implementation + of logging events: <code>AccessEvent</code>. + Thus, a customized filter + for logback access is follows strictly the same rules than one for the + classic module, except for the event implemenation recieved as a parameter. + On the other hand, + <code>TurboFilter</code> objects are not available to the access module. + </p> + + <h3>Filters</h3> + + <p> + <code>EvaluatorFilter</code> objects, with their expressions, are available to + the access module. However, the variables that one can use to build an expression + are different. Only the <code>AccessEvent</code> object can be used, by inserting the + <em>event</em> variable in the expression. Although less wide than its classic + counterpart, the access evaluation filter is just as powerfull. All the + request and response components are reachable from the <em>event</em> variable. + </p> + + <p> + Here is a sample configuration that will ensure that any 404 error will be displayed: + </p> + +<em>Example 6.9: Access Evaluator (logback-examples/src/main/java/chapter6/accessEventEvaluator.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <b><filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="myEval"> + <expression>event.getStatusCode() == 404</expression> + </evaluator> + <OnMismatch>NEUTRAL</OnMismatch> + <OnMatch>ACCEPT</OnMatch> + </filter></b> + <layout class="ch.qos.logback.access.PatternLayout"> + <pattern> + %h %l %u %t %r %s %b + </pattern> + </layout> + </appender> + + <appender-ref ref="STDOUT" /> +</configuration></pre></div> + + <p> + We might imagine a slightly more complex use of filters to ensure the display of 404 errors, but + to prevent polluting the output with endless accesses to CSS files. Here is what such a configuration + would look like: + </p> + +<em>Example 6.10: Access Evaluator (logback-examples/src/main/java/chapter6/accessEventEvaluator2.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <b><filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="Eval404"> + <expression>event.getStatusCode() == 404</expression> + </evaluator> + <OnMismatch>NEUTRAL</OnMismatch> + <OnMatch>ACCEPT</OnMatch> + </filter> + <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> + <evaluator name="EvalCSS"> + <expression>event.getRequestURI().contains("css")</expression> + </evaluator> + <OnMismatch>NEUTRAL</OnMismatch> + <OnMatch>DENY</OnMatch> + </filter></b> + <layout class="ch.qos.logback.access.PatternLayout"> + <pattern> + %h %l %u %t %r %s %b + </pattern> + </layout> + </appender> + + <appender-ref ref="STDOUT" /> +</configuration></pre></div> +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/index.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/index.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,101 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback Manual</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> <h2>The logback manual</h2> + + <p>The complete logback manual documents the latest version of + logback framework. In over 100 pages and dozens of concrete + examples, it covers both basic and advanced logback features: + </p> + + <div> + <ul> + <li>the overall logback architecture</li> + <li>discussion of best logback practices and anti-patterns</li> + <li>logback configuration scripts in XML format</li> + <li>appenders</li> + <li>layouts</li> + <li>filter chains</li> + <li>logback diagnostic contexts</li> + <li>logback default initialization</li> + <li>logback in Servlet Containers</li> + </ul> + </div> + + + <div class="highlight"> + <p> + If you wish to print chapters in this document, we recommend + that you do so using <a href="http://www.getfirefox.com">Firefox 2</a>, with <em>Adapt to + page size</em> enabled, or <a href="http://www.opera.com">Opera</a>. + </p> + <p> + To run the examples provided in this book, you might have + to run the provided script to setup your classpath. The scripts + can be found in the logback distributions, inside the <em>logback-examples</em> + directory. + </p> + </div> + + <p>The logback manual describes the logback API in considerable + detail, including its features and design rationale. Authored by + Ceki G�lc� and S�bastien Pennec, the main + contributors to the logback project, the logback manual is + intended for developers already familiar with the Java language + but new to logback, as much as for experienced logback users. With + the aid of introductory material and many examples, new users + should quickly come up to speed. + </p> + + <div> + <p>Without further ado, here are the contents of the manual:</p> + + <ul> + <li> + <a href="introduction.html"><b>Chapter 1: Introduction to logback</b></a> + </li> + <li> + <a href="architecture.html"><b>Chapter 2: Architecture</b></a> + </li> + <li> + <a href="joran.html"><b>Chapter 3: Logback configuration with Joran</b></a> + </li> + + <li> + <a href="appenders.html"><b>Chapter 4: Appenders</b></a> + </li> + + <li> + <a href="layouts.html"><b>Chapter 5: Layouts</b></a> + </li> + + <li> + <a href="filters.html"><b>Chapter 6: Filter chains</b></a> + </li> + + <li> + <a href="mdc.html"><b>Chapter 7: Diagnostic Context</b></a> + </li> + + <li> + <a href="contextSelector.html"><b>Chapter 8: Context Selector</b></a> + </li> + + </ul> + </div> +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/introduction.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/introduction.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,262 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 1: Introduction</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> + <h2>Introduction</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a> + . + + </p> + </td> + </tr> + </table> + + + <h2>Introduction</h2> + + <p> + Logback is intended as a successor to the popular log4j project. + It was designed by Ceki G�lc�, the log4j founder. + It builds upon a decade long experience gained in + designing industrial-strength logging systems. The resulting + product, logback is faster with a smaller footprint than all + existing logging systems, sometimes by a wide margin. Logback + also offers unique and rather useful features such as Markers, + parameterized logging statements, conditional stack tracing and + powerful event filtering. These are only few examples of useful + features logback has to offer. For its own error reporting, + logback relies on <code>Status</code> objects, which greatly + facilitate troubleshooting. You may wish to rely on Status + objects in contexts other than logging. Logback-core bundles + Joran, a powerful and generic configuration system, which can be + put to use in your own projects to great effect. + </p> + + <h2>First Baby Step</h2> + + <div class="highlight"> + <p> + In order to run the examples in this introduction, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + + <a name="Requirements"></a> + <h3>Requirements</h3> + + <p>Logback-classic module requires the presence + <em>slf4j-api.jar</em>, <em>logback-core.jar</em> in addition to + <em>logback-classic.jar</em> on the classpath. + </p> + + + <p>Let us now begin experimenting with logback.</p> + +<em>Example 1.1: Basic template for logging (<a href="../xref/chapter1/HelloWorld1.html">logback-examples/src/main/java/chapter1/HelloWorld1.java</a>)</em> +<div class="source"><pre>package chapter1; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HelloWorld1 { + + public static void main(String[] args) { + + Logger logger = LoggerFactory.getLogger("chapter1.HelloWorld1"); + logger.debug("Hello world."); + + } +}</pre></div> + + <p> + The <code>HelloWorld</code> class is defined in the + <code>chapter1</code> package. It starts by importing the <code>Logger</code> + and <code>LoggerFactory</code> + classes defined in the SLF4J API, more specifically within the <code>org.slf4j</code> + package. + </p> + + + <p> + On the first line of the main() method, the variable named <code>logger</code> + is assigned a <code>Logger</code> + instance retreived by invoking the static method <code>getLogger</code> + in the <code>LoggerFactory</code> class. + This logger is named "chapter1.HelloWorld1". The main method proceeds to call the + <code>debug</code> method of this logger passing "Hello World" as an argument. + We say that the main + method contains a logging statement of level debug with the message "Hello world". + </p> + + <p> + You will note that the above example does not reference any + logback classes. In most cases, as far as logging is + concerned, your classes will need to import only SLF4J + classes. In principle, you will have to import logback + classes only for configuring logback. Thus, the vast + majority of your classes will only be cognizant of SLF4J API + and oblivious to the existence of logback. + </p> + + + <p>You can launch the first + sample application, <em>chapter1.HelloWord1</em> with the command: + </p> + <div class="source"><pre>java chapter1.HelloWorld1</pre></div> + + <p> + Launching the <code>HelloWorld1</code> + application will output a single line on the console. By virtue of + to logback's default configuration policy, when no default file + is found to configure logback explicitely, logback will add a + <code>ConsoleAppender</code> to the root logger. + </p> + +<div class="source"><pre>20:49:07.962 [main] DEBUG chapter1.HelloWorld1 - Hello world.</pre></div> + + <p> + Logback can report information about its internal state + using a built-in status system. Important events occuring + during logback's lifetime can be accessed through a + <code>StatusManager</code>. For the time being, let us instruct logback to print its + internal state. This is accomplished by a static method in + the <code>LoggerStatusPrinter</code> + class. + </p> + +<em>Example 1.2: Printing Logger Status (<a href="../xref/chapter1/HelloWorld2.html">logback-examples/src/main/java/chapter1/HelloWorld2.java</a>)</em> +<div class="source"><pre>package chapter1; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +<b>import ch.qos.logback.classic.util.LoggerStatusPrinter;</b> + +public class HelloWorld2 { + + public static void main(String[] args) { + Logger logger = LoggerFactory.getLogger("chapter1.HelloWorld2"); + logger.debug("Hello world."); + <b>LoggerStatusPrinter.printStatusInDefaultContext();</b> + } +}</pre></div> + + + <p>Running the <code>HelloWorld2</code> application will produce + the following output:</p> + +<div class="source"><pre>20:49:07.962 [main] DEBUG chapter1.HelloWorld2 - Hello world. +|-INFO in ch.qos.logback.classic.BasicConfigurator@1c1ea29 - Setting up default configuration.</pre></div> + + + <p> + Logback explains that it configured itself using its default + policy, which is a basic <code>ConsoleAppender</code>. + An <code>Appender</code> is a class that can be + seen as an output destination. Appenders exist for many different + destinations including the console, files, Syslog, TCP Socket, JMS and + many more. Users can also easily create their own Appenders as + appropriate for their specific situation. + </p> + + <p> + The previous examples are rather simple. However, actual logging + in a larger application would not be any different. The general + pattern logging statements will not change. Only the configuration + process will be different since you will certainly need a more + specific configuration than what logback provides by default. + As you will see later on in this document, + configuring logback can be done in different flexible and + powerfull ways. Note that, normally, you won't need to invoke + <code>LoggerStatusPrinter</code> + after your log statements. + </p> + + <p> + Here is a list of the three required steps in order to enable + logging in your application. + </p> + + <ol type="1"> + + <p>Configure the logback environment. You can do so in several + more or less sophisticated ways. More on this later.</p> + + <p>In every class where you wish to perform logging, retrieve a + <code>Logger</code> instance by invoking the + <code>org.slf4j.LoggerFactory</code> class' + <code>getLogger()</code> method, passing the current class name + or the class itself as parameter.</p> + + <p>Use this logger instance by invoking its printing methods, + namely the debug(), info(), warn() and error(). This will + produce logging output on the configured appenders.</p> + </ol> + + <a name="BuildingLogback"></a> + <h3>Building logback</h3> + +<p> +Like many java applications today, logback relies on <a href="http://maven.apache.org"> +Maven 2</a> as its build tool. Maven 2 is a free open source build tool that requires +one or more build files names <em>pom.xml</em> which already ship with logback +distributions. +</p> + +<p> +Building all logback components is mostly done by issuing the <em>mvn compile</em> +line in a terminal or command window. Maven 2 will automatically download the required +external libraries and use them. However, a library cannot be downloaded from +the Maven 2 repository. Libraries such as <code>JMS</code> +from sun require a separate download and to issue a command to install their +jars into your local repository. The required command will be presented +by Maven 2 in your console when trying to compile logback. +</p> + +<p> +Logback distributions contain complete source code such that you can modify parts +of logback library and build your own version of it. You may even +redistribute the modified version, as long as you adhere to the conditions +of the LGPL License. In particular you may not call the modified version <em>logback</em> +or claim that it is endorsed by the QOS.ch. +</p> +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/joran.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/joran.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,1407 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter3: Logback configuration</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> + + <h2>Chapter 3: Logback configuration with Joran</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a> + . + + </p> + </td> + </tr> + </table> + + <div class="highlight"> + <p> + In order to run the examples in this chapter, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + +<p>Joran stands for a cold north-west wind which, every now and then, +blows force-fully on Lake Leman, a.k.a lake Geneva. Located right in +the middle of Europe, the Leman happens to be the continent's largest +sweet water reserve. +</p> + +<p> + This document begins with an explanation of how to use it within + logback to configure precisely a logging strategy. Then, + <a href="#Joran">a second part</a> gives a generic explanation of + how the configuration framework in logback works, and how to use + it in your own applications. +</p> + +<h2>Configuration in logback</h2> + +<p> +Logback can be configured both programmatically and with an xml configuration +file. Here are the steps that logback follows to try to configure itself: +</p> + +<ul> + <p>Logback tries to find a file called <em>logback.xml</em> within the classpath.</p> + <p>If no such file is found, it checks for another file called <em>logback-test.xml</em>.</p> + <p>In case none of these files are found, logback configures itself automatically using the + <a href="../xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code> + </a> class.</p> +</ul> +<p> +The first two checks allow for two environments to cooperate nicely. When the application +using logback is in development and test process, a special file can be used to setup +a logging environment that is developer-friendly. Once in production environment, the +presence of a <em>logback.xml</em> file overrides any <em>logback-test.xml</em> +configuration. +</p> + +<p> +The last step is meant to provide very basic logging functionnality in case no configuration +file is provided. In that case, the logging requests are output to the console. +</p> + +<h3>Automatically configuring logback</h3> + +<p> +The simplest way to configure logback is by letting logback use its +<code>BasicConfigurator.configureDefaultContext()</code> method. Let us give a taste of how +this is done with the help of an imaginary application called <code>MyApp1</code>. +</p> + +<em>Example 3.1: Simple example of <code>BasicConfigurator</code> usage +<a href="../xref/chapter3/MyApp1.html">(logback-examples/src/main/java/chapter3/MyApp1.java)</a></em> +<div class="source"><pre>package chapter3; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MyApp1 { + final static Logger logger = LoggerFactory.getLogger(MyApp1.class); + + public static void main(String[] args) { + logger.info("Entering application."); + + Foo foo = new Foo(); + foo.doIt(); + logger.info("Exiting application."); + } +}</pre></div> + +<p> +There is no invokation of the <code>BasicConfigurator</code> here, since logback +automatically calls it when no configuration files are found. It creates a rather +simple logback setup. This call is hardwired to add a <code>ConsoleAppender</code> to +the root logger. The output is formatting using a <code>PatternLayout</code> set to the +pattern <em>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</em>. Note that +by default the root logger is assigned to the <code>DEBUG</code> level. +</p> + +<p> +The output of the command <em>java chapter3.MyApp1</em> should be similar to: +</p> + +<div class="source"><pre>16:06:09.031 [main] INFO chapter3.MyApp1 - Entering application. +16:06:09.046 [main] DEBUG chapter3.Foo - Did it again! +16:06:09.046 [main] INFO chapter3.MyApp1 - Exiting application.</pre></div> + +<p> +If you are unable to run this command, then make sure that you have set +your classpath correctly. The scripts provided in the +<em>logback-examples/</em> directory will help you setting it up. +</p> + +<p> +As a side note, let us mention that in logback child loggers link only +to their existing ancestors. In particular, the logger named <em>chapter3.Foo</em> +is linked directly with the root logger, thereby circumventing the unused +<em>chapter3</em> logger. This noticeably improves the performance +of hierarchy walks and also slightly reduces logback's memory footprint +</p> + +<p> +The <code>MyApp1</code> class uses logback by calling the org.slf4j.LoggerFactory and +org.slf4j.Logger classes, retrieve the loggers it wishes to use, and log away. +For example, the only dependence of the <code>Foo</code> class on logback is the +org.slf4j.LoggerFactory and org.slf4j.Logger import. +Except code that configures logback (if such code exists) user code does not need to +depend on logback. Given that SLF4J permits the use of any implementation under its +abstraction layer, it is rather easy to migrate large bodies of code from an implementation +to another. Logback also ships with a module called <em>log4j-bridge</em> that intercepts +log4j calls and redirects them to the corresponding logback components. Thank to that module, +one can migrate an entire application using log4j to logback just by replacing jars. More +information about the <em>log4j-bridge</em> module in its +<a href="../bridge.html">specific documentation page</a>. +</p> + +<h3>The same using <code>JoranConfigurator</code></h3> + +<p> +The previous example outputs logging information always in the same fixed manner. +Fortunately, it is easy to modify <code>MyApp1</code> so that the log output can +be controlled at runtime. Here is a slightly modified version called <code>MyApp2</code>. +</p> + +<em>Example 3.2: Simple example of <code>BasicConfigurator</code> usage <a href="../xref/chapter3/MyApp2.html">(logback-examples/src/main/java/chapter3/MyApp2.java)</a></em> +<div class="source"><pre>package chapter3; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + +public class MyApp2 { + final static Logger logger = LoggerFactory.getLogger(MyApp2.class); + + public static void main(String[] args) { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + + try { + <b>JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + lc.shutdownAndReset(); + configurator.doConfigure(args[0]);</b> + } catch (JoranException je) { + je.printStackTrace(); + } + + logger.info("Entering application."); + + Foo foo = new Foo(); + foo.doIt(); + logger.info("Exiting application."); + } +}</pre></div> + +<p> +<code>MyApp2</code> fetches the <code>LoggerContext</code>, creates a new +<code>JoranConfigurator</code>, gives it the context and finally asks that +the configurator parses a configuration file. A basic configuration file, that +creates the same components as the default configuration would create, is +listed below: +</p> + +<em>Example 3.3: Basic configuration file (logback-examples/src/main/java/chapter3/sample0.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + +<p> +Assuming the current directory is <em>logback-examples</em>, try running the +<code>MyApp2</code> class by issuing the following command: +</p> + +<div class="source"><pre>java chapter3.MyApp2 src/main/java/chapter3/sample0.xml</pre></div> + +<p> +The ouput of this command is very similar to the output of the previous example, except +that <code>MyApp2</code> retrieves a logger called <em>chapter3.MyApp2</em> instead of +<code>chapter3.MyApp1</code>. The output will reflect the difference. +</p> + +<div class="source"><pre>16:09:00.593 [main] INFO chapter3.MyApp2 - Entering application. +16:09:00.593 [main] DEBUG chapter3.Foo - Did it again! +16:09:00.593 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +It is often very useful to define the logback debug configuration property in order +to instruct logback to output internal configuration messages on the console. To achieve +this, one only needs to add an attribute to the main <em>configuration</em> element in the +configuration file, as shown above: +</p> + +<em>Example 3.4: Basic configuration file using debug mode (logback-examples/src/main/java/chapter3/sample1.xml)</em> +<div class="source"><pre><configuration debug="true"> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + +<p> +This should cause logback to print internal configuration messages in +addition to the actual logs. Relaunching the <code>MyApp2</code> application with this +new configuration file will ouput the following lines: +</p> + +<div class="source"><pre>|-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch. \ +qos.logback.core.ConsoleAppender] +|-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT] +|-INFO in ch.qos.logback.core.joran.action.AppenderAction - Popping appender named [STDOUT] from the \ +object stack +|-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG +|-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to \ +Logger[root] +|-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration. +16:18:23.687 [main] INFO chapter3.MyApp2 - Entering application. +16:18:23.687 [main] DEBUG chapter3.Foo - Did it again! +16:18:23.687 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +At the end of this output, one will immediately recognize the lines that were printed +before. But right above stand the printing of logback's <code>Status</code> objects. +<code>Status</code> objects are logback's powerful error reporting mechanism. They provide +easy and precise access to logback's internal state. +</p> + +<h3>XML Syntax</h3> + +<h4>Configuring Loggers</h4> + +<p> +Loggers are configured using <em>logger</em> elements. A <em>logger</em> element takes exactly +one mandatory name atttribute and an optional additivity attribute, which takes values +<em>true</em> or <em>false</em>. The <em>logger</em> element admits at most one <em>level</em> +element which is discussed next. It may also contain zero or more <em>appender-ref</em> elements; +each appender thus referenced is added to the named logger. It is important to keep mind that +each named logger that is declared with a <em>logger</em> element first has all its +appenders removed and only then are the referenced appenders attached to it. +In particular, if there are no appender references, then the named logger will +lose all its appenders. +</p> + +<p> +The <em>level</em> element is used to set logger levels. It admits two attributes +<em>value</em> and <em>class</em>. The value attribute can be one of the strings <em>DEBUG</em>, +<em>INFO</em>, <em>WARN</em> <em>ERROR</em>, <em>ALL</em> or <em>OFF</em>. +The special case-insensitive value <em>INHERITED</em>, or its synonym <em>NULL</em>, +will force the level of the logger to be inherited from higher up in the hierarchy. +Note that the level of the root logger cannot be inherited. +If you set the level of a logger and later decide that it should inherit +its level, then you need to specify <em>INHERITED</em> or its synonym <em>NULL</em> +as the level value. The class attribute allows you to specify a custom +level where the value of the attribute is the fully qualified name of a +custom level class. You may alternatively use the <em>level#classname</em> syntax within +the value attribute. The <em>level</em> element has no children. +</p> + +<p> +The <em>root</em> element configures the root logger. It does not admit +any attributes because the additivity flag does not apply to the root logger. +Moreover, since the root logger cannot be named, it does not admit a name +attribute either. The <em>root</em> element admits at most one <em>level</em> +element and zero or more <em>appender-ref</em> elements. +Similar to the <em>logger</em> element, declaring a <em>root</em> element +will have the effect of first closing and then detaching all its current +appenders and only subsequently will referenced appenders, if any, will be added. +In particular, if it has no appender references, then the root logger +will lose all its appenders. +</p> + +<p> +Setting the level of a logger is as simple as declaring it and setting +its level, as the next example illustrates. Suppose we are no longer interested +in seeing any <code>DEBUG</code> level logs from any component +belonging to the chapter3 package. The following configuration file shows how to achieve that. +</p> + +<em>Example 3.5: Setting the level of a logger (logback-examples/src/main/java/chapter3/sample2.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> + </layout> + </appender> + + <b><logger name="chapter3"> + <level value="INFO" /> + </logger></b> + + <root> + <!-- The following level element is not necessary since the --> + <!-- level of the root level is set to DEBUG by default. --> + <level value="DEBUG" /> + <appender-ref ref="STDOUT" /> + </root> + +</configuration></pre></div> + +<p> +This new configuration will yield the following output, when used with the +same <code>chapter3.MyApp2</code> class. +</p> + +<div class="source"><pre>17:34:07.578 [main] INFO chapter3.MyApp2 - Entering application. +17:34:07.578 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +Obviously, you can configure the levels of as many loggers as you wish. +In the next configuration file we set the level of the <em>chapter3</em> logger to +<code>INFO</code> but at the same time set the level of the <em>chapter3.Foo</em> logger +to <code>DEBUG</code>. +</p> + +<em>Example 3.6: Setting the level of multiple loggers (logback-examples/src/main/java/chapter3/sample3.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + </Pattern> + </layout> + </appender> + + <b><logger name="chapter3"> + <level value="INFO" /> + </logger> + + <logger name="chapter3.Foo"> + <level value="DEBUG" /> + </logger></b> + + <root> + <!-- The following level element is not necessary since the --> + <!-- level of the root level is set to DEBUG by default. --> + <level value="DEBUG" /> + <appender-ref ref="STDOUT" /> + </root> + +</configuration></pre></div> + +<p> +Running <code>MyApp2</code> with this configuration file will result in the +following output on the console: +</p> + +<div class="source"><pre>17:39:27.593 [main] INFO chapter3.MyApp2 - Entering application. +17:39:27.593 [main] DEBUG chapter3.Foo - Did it again! +17:39:27.593 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +After <code>JoranConfigurator</code> configures logback using the <em>sample3.xml</em> +file, the logger settings, more specifically their levels, are summarized in the following table. +</p> + +<table class="bodyTable"> + <tr class="b"> + <th>Logger name</th> + <th>Assigned Level</th> + <th>Effective Level</th> + </tr> + <tr class="a"> + <td>root</td> + <td><code>DEBUG</code></td> + <td><code>DEBUG</code></td> + </tr> + <tr class="b"> + <td>chapter3</td> + <td><code>INFO</code></td> + <td><code>INFO</code></td> + </tr> + <tr class="a"> + <td>chapter3.MyApp2</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> + <tr class="b"> + <td>chapter3.Foo</td> + <td><code>DEBUG</code></td> + <td><code>DEBUG</code></td> + </tr> +</table> + +<p> +It follows that the two logging statements of level <code>INFO</code> in the <code>MyApp2</code> +class are enabled while the <code>debug</code> statement in <code>Foo.doIt()</code> method +will also print without hindrance. Note that the level of the root logger is always +set to a non-null value, which is <code>DEBUG</code> by default. +One rather important point to remember is that the logger-level filter depends +on the effective level of the logger being invoked, not the level of the logger +where the appenders are attached. The configuration file <em>sample4.xml</em> is a case in point: +</p> +<em>Example 3.7: Logger level sample (logback-examples/src/main/java/chapter3/sample4.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + </Pattern> + </layout> + </appender> + + <b><logger name="chapter3"> + <level value="INFO" /> + </logger></b> + + <root> + <b><level value="OFF" /></b> + <appender-ref ref="STDOUT" /> + </root> + +</configuration></pre></div> + +<p> +The following table lists the loggers and their level setting after applying the +<em>sample4.xml</em> configuration file. +</p> + +<table class="bodyTable"> + <tr class="a"> + <th>Logger name</th> + <th>Assigned Level</th> + <th>Effective Level</th> + </tr> + <tr class="b"> + <td>root</td> + <td><code>OFF</code></td> + <td><code>OFF</code></td> + </tr> + <tr class="a"> + <td>chapter3</td> + <td><code>INFO</code></td> + <td><code>INFO</code></td> + </tr> + <tr class="b"> + <td>chapter3.MyApp2</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> + <tr class="a"> + <td>chapter3.Foo</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> +</table> + +<p> +The ConsoleAppender named <em>STDOUT</em>, the only configured appender in +<em>sample4.xml</em>, is attached to the root logger whose level is set to +<code>OFF</code>. However, running MyApp2 with configuration script +<em>sample4.xml</em> will output: +</p> + +<div class="source"><pre>17:52:23.609 [main] INFO chapter3.MyApp2 - Entering application. +17:52:23.609 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +Thus, the level of the root logger has no apparent effect because the loggers in +<code>chapter3.MyApp2</code> and <code>chapter3.Foo</code> classes, namely +<em>chapter3.MyApp2</em> and <em>chapter3.Foo</em>, inherit their level from the +<em>chapter3</em> logger which has its level set to <code>INFO</code>. +As noted previously, the <em>chapter3</em> logger exists by virtue of its +declaration in the configuration file - even if the Java source code does not +directly refer to it. +</p> + +<h4>Configuring Appenders</h4> + +<p> +Appenders are configured using <em>appender</em> elements. These elements admit +two attributes <em>name</em> and <em>class</em> both of which are mandatory. +The <em>name</em> attribute specifies the name of the appender whereas +the <em>class</em> attribute specifies the fully qualified name of the class +of which the named appender will be an instance. +The <em>appender</em> may contain zero or one <em>layout</em> elements and +zero or more <em>filter</em> elements. Appart from these two basic elements, +<em>appender</em> elements may contain any element that corresponds to a setter +method of the appender class, to configure the appender's options. +</p> + +<p> +The <em>layout</em> element takes a mandatory class attribute specifying +the fully qualified name of the class of which the associated layout +should be an instance. Like the <em>appender</em> element, it may contain +other elements, referring to setter methods, to configure its options. +</p> + +<p> +Logging to multiple appenders is as easy as defining the various appenders +and referencing them in a logger, as the next configuration file illustrates: +</p> + +<em>Example 3.8: Multiple loggers (logback-examples/src/main/java/chapter3/multiple.xml)</em> +<div class="source"><pre><configuration> + + <appender name="<b>FILE</b>" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>myApp.log</file> + + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH-mm-ss}.log + </FileNamePattern> + </rollingPolicy> + + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <appender name="<b>STDOUT</b>" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %msg%n + </Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <b><appender-ref ref="FILE" /> + <appender-ref ref="STDOUT" /></b> + </root> +</configuration></pre></div> + +<p> +This configuration scripts defines two appenders called <em>FILE</em> and <em>STDOUT</em>. +The <em>FILE</em> appender logs to a file called <em>myApp.log</em>. The layout for this appender +is a <code>PatternLayout</code> that outputs the date, level, thread name, logger name, +file name and line number where the log request is located, +the message and line separator character(s). +The second appender called <code>STDOUT</code> outputs to the console. +The layout for this appender outputs only the message string followed by a line separator. +</p> + +<p> +The appenders are attached to the root logger by referencing +them by name within an <em>appender-ref</em> element. Note that each appender +has its own layout. Layouts are usually not designed to be shared by multiple +appenders. XML configuration files do not provide any syntactical +means for sharing layouts. +</p> + +<p> +By default, <b>appenders are cumulative</b>: a logger will log to the appenders +attached to itself (if any) as well as all the appenders attached to its ancestors. +Thus, attaching the same appender to multiple loggers will cause +logging output to be duplicated. +</p> + +<em>Example 3.9: Duplicate appender (logback-examples/src/main/java/chapter3/duplicate.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + </Pattern> + </layout> + </appender> + + <logger name="chapter3"> + <appender-ref ref="STDOUT" /> + </logger> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + +<p> +Running <code>MyApp2</code> with <em>duplicate.xml</em> will yield the following output: +</p> + +<div class="source"><pre>14:25:36.343 [main] INFO chapter3.MyApp2 - Entering application. +14:25:36.343 [main] INFO chapter3.MyApp2 - Entering application. +14:25:36.359 [main] DEBUG chapter3.Foo - Did it again! +14:25:36.359 [main] DEBUG chapter3.Foo - Did it again! +14:25:36.359 [main] INFO chapter3.MyApp2 - Exiting application. +14:25:36.359 [main] INFO chapter3.MyApp2 - Exiting application.</pre></div> + +<p> +Notice the duplicated output. The appender named <em>STDOUT</em> is attached to +two loggers, to root and to <em>chapter3</em>. Since the root logger is the +ancestor of all loggers and <em>chapter3</em> is the parent of <em>chapter3.MyApp2</em> +and <em>chapter3.Foo</em>, logging request made with these two loggers +will be output twice, once because <em>STDOUT</em> is attached to <em>chapter3</em> +and once because it is attached to <em>root</em>. +</p> + +<p> +Appender additivity is not intended as a trap for new users. +It is a quite convenient logback feature. For instance, you can configure +logging such that log messages appear on the console (for all loggers in the system) +while messages only from some specific set of loggers flow into a specific appender. +</p> + +<em>Example 3.10: Multiple appender (logback-examples/src/main/java/chapter3/restricted.xml)</em> +<div class="source"><pre><configuration> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>myApp.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH-mm-ss}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%msg%n</Pattern> + </layout> + </appender> + + <logger name="chapter3"> + <appender-ref ref="FILE" /> + </logger> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + +<p> +In this example, the console appender will log all the messages (for all loggers in the system) +whereas only logs under the <em>chapter3</em> tree go into the <em>myApp.log</em> file. +</p> + +<h4>Overriding the default cumulative behaviour</h4> + +<p> +In case the default cumulative behavior turns out to be unsuitable for +one's needs, one can override it by setting the additivity flag to false. +Thus, a branch in your logger tree may direct output to a set of appenders +different than those of the rest of the tree. +</p> + +<em>Example 3.11: Additivity flag (logback-examples/src/main/java/chapter3/additivityFlag.xml)</em> +<div class="source"><pre><configuration> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>foo.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + foo-%d{yyyy-MM-dd-HH-mm-ss}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%msg%n</Pattern> + </layout> + </appender> + + <logger name="chapter3.Foo" <b>additivity="false"</b>> + <appender-ref ref="FILE" /> + </logger> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + +<p> +This example, the appender named <em>FILE</em> is attached to the <em>chapter3.Foo</em> +logger. Moreover, the <em>chapter3.Foo</em> logger has its additivity flag set to false +such that its logging output will be sent to the appender named <em>FILE</em> but +not to any appender attached higher in the hierarchy. Other loggers remain +oblivious to the additivity setting of the <em>chapter3.Foo</em> logger. +Running the <code>MyApp2</code> application with the <em>additivityFlag.xml</em> +configuration file will output results on the console from the <em>chapter3.MyApp2</em> +logger. +However, output from the <em>chapter3.Foo</em> logger will appear in the <em>foo.log</em> file +and only in that file. +</p> + +<h4>Variable substitution</h4> + +<p> +All option <em>values</em> admit variable substitution. The syntax of variable +substitution is similar to that of Unix shells. The string between an +opening <em>${</em> and closing <em>}</em> is interpreted as a key. +The value of the substituted variable can be defined as a system property +in the configuration file itself or in a separate file linked to the +configuration file. The value of the key +is first searched in configuration file or linked properties file, +and if not found there, it is then searched in system properties. +The corresponding value replaces <em>${aKey}</em> sequence. For example, +if <em>java.home</em> system property is set to <em>/home/xyz</em>, +then every occurrence of the sequence <em>c:\Program Files\Java\jdk1.5.0_10\jre</em> will be +interpreted as <em>/home/xyz</em>. +</p> + +<p> +The first example shows a declared property at the beginning of the +configuration file. It is then used further down the file to specify +the place to create the output file. +</p> + +<em>Example 3.12: Simple Variable substitution (logback-examples/src/main/java/chapter3/variableSubstitution1.xml)</em> +<div class="source"><pre><configuration> + + <b><substitutionProperty name="user.home.dir" value="/Users/seb" /></b> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <b><file>${user.home.dir}/myApp.log</file></b> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + +<p> +The next example shows the use of a System property to achieve the same result. The +property is not declared anywhere, thus logback will look for it in the System properties. +</p> + +<em>Example 3.13: System Variable substitution (logback-examples/src/main/java/chapter3/variableSubstitution2.xml)</em> +<div class="source"><pre><configuration> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <b><file>${user.home.dir}/myApp.log</file></b> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + +<p> +When many variables are used, it is often more convenient to create +a separate file that will contain all the variables. Here is how one can +do such a setup. +</p> + +<em>Example 3.14: Variable substitution using a separate file (logback-examples/src/main/java/chapter3/variableSubstitution3.xml)</em> +<div class="source"><pre><configuration> + + <substitutionProperty file="variables1.properties" /> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <b><file>${user.home.dir}/myApp.log</file></b> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + +<p> +This configuration file contains a link to another file called <em>variables.properties</em>. +The variables contained in that other file will be read and will thus be available to the +logback configuration file. Here is what the <em>variable.properties</em> file looks like. +</p> + +<em>Example 3.15: Variable file (logback-examples/src/main/java/chapter3/variables1.properties)</em> +<div class="source"><pre>user.home.dir=/Users/seb</pre></div> + +<p> +Nothing more is needed to declare the variable. +</p> + +<p> +Recursive subsitution is also available. If the user wants to use variables to +specify not only the destination directory but also the file name, here is what she +would write in her <em>variables.properties</em> file. +</p> + + +<em>Example 3.16: Recursive use of variables (logback-examples/src/main/java/chapter3/variables2.properties)</em> +<div class="source"><pre>user.home.dir=/Users/seb +fileName=myApp.log +destination=${user.home.dir}/${fileName}</pre></div> + +<p> +In the configuration file, only the last variable, <em>${destination}</em> will +be used, as shown below: +</p> + +<em>Example 3.17: Variable substitution using a separate file (logback-examples/src/main/java/chapter3/variableSubstitution4.xml)</em> +<div class="source"><pre><configuration> + + <substitutionProperty file="variables1.properties" /> + + <appender name="FILE" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <b><file>${destination}</file></b> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH}.log + </FileNamePattern> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + +<a name="Joran"></a> +<h2>Using Joran in your own application</h2> + +<p>As we've seen, logback relies on Joran, a +mature, flexible and powerful configuration framework. Many of the +capabilities offered by logback modules are possible with the help of Joran. +</p> + +<p>Joran is actually a generic configuration system which can be used +independently of logging. To emphaises this point, we should mention +that the logback-core module does not have a notion of loggers. In +that sprit, many of the examples related to this tutorial, have +nothing to do with loggers, appenders or layouts. +</p> + +<p>The examples for this chapter can be found under +<em>LOGBACK_HOME/logback-examples/src/main/java/chapter3</em>. +</p> + +<p>To install joran, simply <a href="download.html">download</a> +logback and add <em>logback-core-VERSION.jar</em> to your classpath.</p> + +<h2>Historical perspective</h2> + +<p>One of the most powerful features of the Java language is +reflection. Reflection makes it possible to configure software systems +declaratively. For example, many important properties of an EJB are +configured with the <em>ejb.xml</em> file. While EJBs are written in Java, many +of their properties are specified within the <em>ejb.xml</em> file. Similarly, +logback settings can be specified in a configuration file, expressed +in XML format. +</p> + +<p>In log4j, logback's predecessor, <code>DOMConfigurator</code> that +shipped with log4j version 1.2.x can parse configuration files written +in XML. The <code>DOMConfigurator</code> was written in a way that +forced to tweak it each time the structure of the configuration file +changed. The modified code had to be recompiled and redeployed. Just +as importantly, the code of the DOMConfigurator consists of loops +dealing with children elements containing many interspersed if/else +statements. One could not help but notice that that particular code +reeked of redundancy. The <a href="http://jakarta.apache.org/commons/digester/">digester +project</a> has shown that it is possible to parse XML files using +pattern matching rules. At parse time, digester will apply the rules +that match previously stated patterns. Rule classes are usually quite +small and specialized. Consequently, they are relatively easy to +understand and to maintain. +</p> + +<p>Joran is heavily inspired by the commons-digester project but uses +a slightly different terminology. In commons-digester, a rule can be +seen as consisting of a pattern and a rule, as shown by the +<code>Digester.addRule(String pattern, Rule rule)</code> method. We +find it unnecessarily confusing to have a rule to consist of itself, +not recursively but with a different meaning. In Joran, a rule +consists of a pattern and an action. An action is invoked when a match +occurs for the corresponding pattern. This relation between patterns +and actions lies at the core of Joran. Quite remarkably, one can deal +with quite complex requirements by using simple patterns, or more +precisely with exact matches and wildcard matches. For example, the +pattern <em>a/b</em> will match a <code><b></code> element nested within +an <code><a></code> element but not a <code><c></code> element, +even if nested within a <code><b></code> element. It is also +possible to match a particular XML element, regardless of its nesting +level, by using the <em>*</em> wildcard character. For example, the pattern +<em>*/a</em> will match an <code><a></code> element at any nesting +position within the document. Other types of patterns, for example +<em>a/*</em>, are not currently supported by Joran. +</p> + +<h2>SAX or DOM?</h2> + +<p>Due to the event-based architecture of the SAX API, a tool based on +SAX cannot easily deal with forward references, that is, references to +elements which are defined later than the current element being +processed. Elements with cyclical references are equally +problematic. More generally, the DOM API allows the user to perform +searches on all the elements and make forward jumps. +</p> + +<p>This extra flexibility initially led us to choose the DOM API as +the underlying parsing API for Joran. After some experimentation, it +quickly became clear that dealing with jumps to distant elements while +parsing the DOM tree did not make sense when the interpretation rules +were expressed in the form of patterns and actions. <em>Joran only +needs to be given the elements in the XML document in a sequential, +depth-first order.</em> +</p> + +<p>Joran was first implemented in DOM. However, the author migrated to +SAX in order to benefit form the location information provided to the +user, that is, to an <code>org.w3.sax.ContentHandler</code>. With the +help of location information, it becomes possible to display essential +error reports to the user which include exact line and column. This +extra information turns out to be handy in hunting down problems. +</p> + + +<h2>Actions</h2> + +<p>Actions extend the +<code>ch.qos.logback.core.joran.action.Action</code> class which +consists of the following abstract methods. +</p> + + +<div class="source"><pre>package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; +import ch.qos.logback.core.joran.spi.ExecutionContext; + +public abstract class Action { + + + /** + * Called when the parser first encounters an element. + */ + public abstract void begin(ExecutionContext ec, + String name, + Attributes attributes); + + /** + * Called when the parser encounters the element end. At + * this stage, we can assume that child elements, if any, + * have been processed. + */ + public abstract void end(ExecutionContext ec, String name); +}</pre></div> + +<p>Thus, every action must implement the begin and end methods.</p> + + +<h2>Execution context</h2> + +<p>To allow various actions to collaborate, the invocation of begin +and end methods include an execution context as the first +parameter. The execution context includes an object stack, an object +map, an error list and a reference to the Joran interpreter invoking +the action. Please see the +<code>ch.qos.logback.core.joran.spi.ExecutionContext</code> class for +the exact list of fields contained in the execution context. +</p> + +<p>Actions can collaborate together by fetching, pushing or popping +objects from the common object stack, or by putting and fetching keyed +objects on the common object map. Actions can report any error +conditions by adding error items on the execution context's +<code>StatusManager</code>. +</p> + +<a name="helloWorld"></a> +<h3>A hello world example</h3> + +<p>The <em>logback-examples/src/main/java/chapter3/helloWorld/</em> directory includes a +trivial action and Joran interpreter setup which just displays <em>Hello +World</em> when a <hello-world> element is encountered in an XML file. +It also includes the basic steps which are +necessary to set up and invoke a Joran interpreter. +</p> +<p> +The <em>hello.xml</em> file contains only one element, without any +other nested elements. The <a href="../xref/chapter3/helloWorld/HelloWorldAction.html"> +<code>HelloWorldAction</code></a> class is +a trivial implementation: it only prints "Hello World" in the console when +it's <code>begin()</code> method is called. +</p> +<p> +<a href="../xref/chapter3/helloWorld/HelloWorld.html"><code>HelloWorld</code></a> +is a class that sets up the Joran interpreter, +with the minimal steps necessary: +</p> + +<ul> + <p>It creates a <code>RuleStore</code> and a <code>Context</code></p> + <p>It adds the <em>hello-world</em> pattern, with it's corresponding action</p> + <p>It creates a Joran interpreter, and passes the <code>RuleStore</code></p> + <p>It creates a SAX parser and parses the given file, specifying the newly created + Joran interpreter as the <code>ContentHandler</code></p> +</ul> + +<p> +It's last step is to print the content of the <code>Context</code>. +Since Joran uses logback's powerfull <code>Status</code> objects for +error reporting, one can have a good feedback on what happened during +the parsing. +</p> + +<p> +In this example, the parsing is rather simple. The <em>hello-world</em> element +will activate <code>HelloWorldAction</code>'s <code>begin()</code> and +<code>end()</code> methods. +In the first method, a simple call to <code>System.out.println()</code> +will be issued, displaying <em>Hello World</em> in the console. +</p> + +<a name="calculator"></a> +<h3>Collaborating actions</h3> +<p> +The <em>logback-examples/src/main/java/joran/calculator/</em> directory includes several actions +which collaborate together through the common object stack in order +to accomplish simple computations. +</p> +<p> +The <em>calculator1.xml</em> file contains a <code>computation</code> element, +with a nested <code>literal</code> element. +</p> +<p> +In the <a href="../xref/chapter3/calculator/Calculator1.html"> +<code>Calculator1</code></a> class, we declare various patterns and actions, +that will collaborate and calculate a result based on the xml file. The simple +<em>calculator1.xml</em> file only creates a computation and declares a literal +value. The resulting parsing is pretty simple: +</p> +<ul> + <p>The <a href="../xref/chapter3/calculator/ComputationAction1.html"> + <code>ComputationAction1</code></a> class' <code>begin()</code> method + is called</p> + <p>The <a href="../xref/chapter3/calculator/LiteralAction.html"> + <code>LiteralAction</code></a> class' <code>begin()</code> and <code>end()</code> + methods are called</p> + <p>The <a href="../xref/chapter3/calculator/ComputationAction1.html"> + <code>ComputationAction1</code></a> class' <code>end()</code> method + is called</p> +</ul> +<p> +What is interesting here is the way that the Actions collaborate. +The <code>LiteralAction</code> reads a literal value and pushes it in the +object stack maintained by the <code>ExecutionContext</code>. Once done, +any other action can pop the value to read or modify it. Here, the +<code>end()</code> method of the <code>ComputationAction1</code> class pops +the value from the stack and prints it. +</p> +<p>The <em>calculator2.xml</em> file is a bit more complex, but much more interesting.</p> +<p>It contains the following elements:</p> + +<em>Example 3.18: Calculator configuration file (logback-examples/src/main/java/chapter3/calculator/calculator2.xml)</em> +<div class="source"><pre><computation name="toto"> + <literal value="7"/> + <literal value="3"/> + <add/> + <literal value="3"/> + <multiply/> +</computation></pre></div> +<p> +Here, there are obviously more actions that will be part of the computation. +</p> +<p>When called, the <a href="../xref/chapter3/calculator/AddAction.html"> +<code>AddAction</code></a> class will remove the two integers at +the bottom of the stack, add them and push the resulting integer at the +top of the stack, for further use.</p> +<p>Later in the computation, the <a href="../xref/chapter3/calculator/MultiplyAction.html"> +<code>MultiplyAction</code></a> class will be called. +It will take the last two integers from the stack, multiply them and +push the result in the stack.</p> +<p>We have here two examples of action whose <code>begin()</code> method behaves in +a certain, predictable way, but whose <code>end()</code> methods are empty.</p> + +<p>Finally, a <em>calculator3.xml</em> is also provided, to demonstrate the possibility +elements that contain instances of the same element. Here's the content of +<em>calculator3.xml</em>:</p> + +<em>Example 3.19: Calculator configuration file (logback-examples/src/main/java/chapter3/calculator/calculator3.xml)</em> +<div class="source"><pre><computation name="toto"> + <computation> + <literal value="7"/> + <literal value="3"/> + <add/> + </computation> + + <literal value="3"/> + <multiply/> +</computation></pre></div> + +<p>Much like the use of parentheses in an algebrical equation, the presence of +a <code>computation</code> element nested in another is managed by the +<a href="../xref/chapter3/calculator/ComputationAction2.html"> +<code>ComputationAction2</code></a> class using an internal stack. The well-formedness +of XML will guarantee that a value saved by one <code>begin()</code> will be consumed +only by the matching <code>end()</code> method.</p> + +<a name="newRule"></a> +<h3>New-rule action</h3> +<p>Joran includes an action which allows the Joran interpreter to lean +new rules on the fly while interpreting the XML file containing the +new rules. See the <em>logback-examples/src/main/java/joran/newRule/</em> +directory for sample code. +</p> +<p>In this package, the <a href="../xref/chapter3/newRule/NewRuleCalculator.html"> +<code>NewRuleCalculator</code></a> class contains +the same setup as we have seen so far, but for one line:</p> + +<div class="source"><pre>ruleStore.addRule(new Pattern("/computation/new-rule"), new NewRuleAction());</pre></div> + +<p>By adding this line, we ask Joran to allow new rules to be learnt +at parsing time. It works pretty much like the other rules: it has a +<code>begin()</code> and <code>end()</code> method, and is called each time +the parser finds a <em>new-rule</em> element.</p> + +<p>When called, the <code>begin()</code> method looks for a <em>pattern</em> +and a <em>actionClass</em> attribute. The action class is then instanciated +and added to the <code>RuleStore</code>, along with its corresponding pattern.</p> + +<p>Here is how new rules can be declared in an xml file:</p> + +<div class="source"><pre><new-rule pattern="*/computation/literal" actionClass="chapter3.calculator.LiteralAction"/></pre></div> + +<p>Using new rule declarations, the preceding example, involving the calculation, could be +expressed this way:</p> + +<em>Example 3.20: Configuration file using new rules on the fly (logback-examples/src/main/java/chapter3/newrule/new-rule.xml)</em> +<div class="source"><pre><computation name="toto"> + <new-rule pattern="*/computation/literal" + actionClass="chapter3.calculator.LiteralAction"/> + <new-rule pattern="*/computation/add" + actionClass="chapter3.calculator.AddAction"/> + <new-rule pattern="*/computation/multiply" + actionClass="chapter3.calculator.MultiplyAction"/> + + <computation> + <literal value="7"/> + <literal value="3"/> + <add/> + </computation> + + <literal value="3"/> + <multiply/> +</computation></pre></div> + +<a name="implicit"></a> +<h3>Implicit actions </h3> +<p>The rules defined thus far are called explicit rules because they +require an explicit pattern, hence fixing the tag name of the elements +for which they apply. +</p> + +<p>In highly extensible systems, the number and type of components to +handle are innumerable so that it would become very tedious or even +impossible to list all the applicable patterns by name. +</p> + +<p>At the same time, even in highly extensible systems one can observe +well-defined patterns linking the various parts together. Implicit +rules come in very handy when processing components composed of +sub-components unknown ahead of time. For example, Apache Ant is +capable of handling tasks which contain tags unknown at compile time +by looking at methods whose names start with <em>add</em>, as in +<code>addFile</code>, or <code>addClassPath</code>. +When Ant encounters an embedded tag within a task, it +simply instantiates an object that matches the signature of the task +class' add method and attaches the resulting object to the parent. +</p> + +<p>Joran includes similar capability in the form of implicit +actions. Joran keeps a list of implicit actions which can be applied +if no explicit pattern matches the current XML element. However, +applying an implicit action may not be always appropriate. Before +executing the implicit action, Joran asks an implicit action whether +it is appropriate in the current context. Only if the action replies +affirmatively does Joran interpreter invoke the (implicit) +action. This extra step makes it possible to support multiple implicit +actions or obviously none, if no implicit action is appropriate for a +given situation. +</p> + +<p>For example, the <a href="../xref/ch/qos/logback/core/joran/action/NestedComponentIA.html"> +<code>NestedComponentIA</code></a> extending +<a href="../xref/ch/qos/logback/core/joran/action/ImplicitAction.html"> +<code>ImplicitAction</code></a> , will +instantiate the class specified in a nested component and attach it +to the parent component by using setter method of the parent +component and the nested element's name. Under certain circumstances, +a nested action needs to be applied to an element say <a> and also +to another element <b> nested within <a>. The current +implementation of <code>NestedComponentIA</code> is capable of +handling multiply nested elements requiring intervention by the same +implicit action. +</p> + +<p>Both <code>ImplicitAction</code> and <code>NestedComponentIA</code> are located in the +<code>ch.qos.logback.core.joran.action</code> package. +</p> + +<p>Refer to the <em>logback-examples/src/main/java/joran/implicit</em> +directory for an example of an implicit action. +</p> + +<p>In that directory, you will find two actions classes, one xml file and one +class containing the setup of Joran.</p> + +<p>The <a href="../xref/chapter3/implicit/NOPAction.html"> +<code>NOPAction</code></a> class does nothing. It is used to set +the context of the <em>foo</em> element, using this line:</p> + +<div class="source"><pre>ruleStore.addRule(new Pattern("*/foo"), new NOPAction());</pre></div> + +<p>After that, the implicit action, namely +<a href="../xref/chapter3/implicit/PrintMeImplicitAction.html"> +<code>PrintMeImplicitAction</code></a>, +is added to the <code>RuleStore</code>. This is done by simply adding a new +instance of the action to the <code>Joran interpreter</code></p> + +<div class="source"><pre>ji.addImplicitAction(new PrintMeImplicitAction());</pre></div> + +<p>When called, the <code>isApplicable()</code> method of <code>PrintMeImplicitAction</code> +checks the value of the <em>printme</em> attribute. If the value is <code>true</code>, +the implicit action is applicable: its <code>begin()</code> method will be called.</p> + +<p>The <em>implicit1.xml</em> file contains the following lines:</p> + +<em>Example 3.21: Usage of implicit rules (logback-examples/src/main/java/chapter3/implicit/implicit1.xml)</em> +<div class="source"><pre><foo> + + <xyz printme="true"> + <abc printme="true"/> + </xyz> + + <xyz/> + + <foo printme="true"/> + +</foo></pre></div> + +<p>As one can see, the first element will be printed, since it has a <em>printme</em> +attribute, which bears the value <code>true</code>.</p> + +<p>The second element will not be printed, because no <em>printme</em> attibute is present.</p> + +<p>The last element will not be printed, although the required attribute is present. +This is because implicit rules are called only if no explicit rules are defined. Since +we added a <code>NOPAction</code> with the <em>*/foo</em> pattern, it will be used instead +of the <code>PrintMeImplicitAction</code>.</p> + +<p>Running the example yields the following output:</p> + +<div class="source"><pre>Element <xyz> asked to be printed. +Element <abc> asked to be printed. +ERROR in ch.qos.logback.core.joran.spi.ExecutionContext@1c5c1 - no applicable action \ +for <xyz>, current pattern is [/foo/xyz]</pre></div> + +<p>The last line was printed because of a call to <code>StatusPrinter</code> at the end +of the main class.</p> + +<h3>Non goals</h3> + +<p>The Joran API is not intended to be used to parse documents with +thousands of elements. +</p> + +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/layouts.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/layouts.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,1753 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 5: Layouts</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> <h2>Chapter 5: Layouts</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a> + . + + </p> + </td> + </tr> + </table> + + <div class="highlight"> + <p> + In order to run the examples in this chapter, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + + <h2>What is a layout</h2> + <p> + While appenders are responsible for writing logging output + to an appender dependent device, layouts are responsible for + the format of the output. In case you were wondering, + layouts have nothing to do with large estates in Florida. + The + <code>format()</code> + method in the + <a href="../xref/ch/qos/logback/core/Layout.html"><code>Layout</code></a> + interface takes an object that represents + an event (of any type) and returns a String. A synopsis of the + <code>Layout</code> interface is shown below. + </p> + <div class="source"><pre>public interface Layout<E> extends ContextAware, LifeCycle { + + String doLayout(E event); + String getHeader(); + String getFooter(); + String getContentType(); +}</pre></div> + <p> + This interface is rather simple and yet is sufficent for + many formatting needs. The Texan developer from Texas, + who you might know from Joseph Heller's <em>Catch-22</em>, + might exclaim: + it just takes two methods to implement a layout!!? + </p> + + <h2>Logback classic</h2> + + <p> + Logback classic only processes events of type + <a href="../xref/ch/qos/logback/classic/spi/LoggingEvent.html"> + <code>ch.qos.logback.classic.spi.LoggingEvent</code></a>. + </p> + + <h3>Writing your own Layout</h3> + <p> + Let us implement a simple and functional layout for the + classic module which prints the time elapsed since the start + of the application, the level of the logging event, the + caller thread between brackets, its logger name, a dash followed + by the event message and a new line. + </p> + <p>Sample output might look like:</p> + <div class="source">10489 DEBUG [main] com.marsupial.Pouch - Hello world.</div> + + <p>Here is a possible implementation, authored by the Texan developer:</p> + <em>Example 5.0: Sample implementation of a Layout + <a href="../xref/chapter5/MySampleLayout.html"> + (logback-examples/src/main/java/chapter5/MySampleLayout.java)</a></em> + <div class="source"><pre>package chapter5; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.LayoutBase; + +public class MySampleLayout extends LayoutBase<LoggingEvent> { + + public String doLayout(LoggingEvent event) { + StringBuffer sbuf = new StringBuffer(128); + sbuf.append(event.getTimeStamp() - LoggingEvent.getStartTime()); + sbuf.append(" "); + sbuf.append(event.getLevel()); + sbuf.append(" ["); + sbuf.append(event.getThreadName()); + sbuf.append("] "); + sbuf.append(event.getLoggerRemoteView().getName()); + sbuf.append(" - "); + sbuf.append(event.getFormattedMessage()); + sbuf.append(LINE_SEP); + return sbuf.toString(); + } +}</pre></div> + + <p> + Note that + <code>MySampleLayout</code> + extends <a href="../xref/ch/qos/logback/core/LayoutBase.html"> + <code>LayoutBase</code></a>. + This class manages trivial components of a <code>Layout</code> + such as started or stopped status, header, footer and + content type access or logging context awareness. It allows + the developer to concentrate on the formatting she expects + from her <code>Layout</code>. Note that the <code>LayoutBase</code> + class is generic. By extending it, we precise the type that it will + have to handle, by adding <em><LoggingEvent></em> after its declaration. + </p> + + <p>The marginally more interesting <code>doLayout(LoggingEvent event)</code> + method begins by instantiating a StringBuffer. It proceeds by adding various + fields of the event parameter. The Texan from Texas was careful to print + the formatted form of the message and not its object form. + This allows for logging requests which are passed object arrays to + build the message in its proper form. + </p> + <p> + In the above listing of the <code>Layout</code> class, + we had omitted the class static <code>LINE_SEP</code> + field which is simply assigned the value returned by + <code>System.getProperty("line.separator")</code> + method. After adding system dependent line separator + character(s), the format method returns the string buffer as + a String. + </p> + <p> + The <code>doLayout</code> method ignores any eventual exceptions contained + in the event. In a real world layout implementation, you would probably not want + to silently ignore exceptions. + </p> + + <p>Custom layouts are configured as any other layout, as shown below:</p> + + <em>Example 5.0: Configuration of MySampleLayout + (logback-examples/src/main/java/chapter5/sampleLayoutConfig.xml)</em> +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <b><layout class="chapter5.MySampleLayout" /></b> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p>The sample application <a href="../xref/chapter5/SampleLogging.html"> + <code>chapter5.SampleLogging</code></a> configures logback with the + configuration script supplied as parameter and then logs a debug message, + followed by an error message. </p> + + <p> + To run this example execute the command + <em>java chapter5.SampleLogging src/main/java/chapter5/sampleLayoutConfig.xml</em> + once in the <em>logback-examples</em> directory. This will produce the following + output: + </p> + +<div class="source"><pre>0 DEBUG [main] chapter5.SampleLogging - Everything's going well +0 ERROR [main] chapter5.SampleLogging - ... not quite</pre></div> + + <p>That was simple enough. + The skeptic Pyrrho of Elea, who insists that nothing is certain except + perhaps uncertainty itself, which is by no means certain either, + might ask: how about a layout with options? + The reader shall find a slightly modified version of our + custom layout in <code>MySampleLayout2.java</code>. She will discover that adding an option + to a layout is as simple as declaring a setter method for the option. + </p> + <p> + The + <a href="../xref/chapter5/MySampleLayout2.html"><code>MySampleLayout2</code> + </a> + class contains two attributes. The first one is a prefix that + can be added to the output. The second attribute is used to + choose wether to display the name of the thread from which + the logging request was sent. + </p> + <p>Here is the implementation of this class:</p> +<div class="source"><pre>package chapter5; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.LayoutBase; + +public class MySampleLayout2 extends LayoutBase<LoggingEvent> { + + String prefix = null; + boolean printThreadName = true; + + <b>public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public void setPrintThreadName(boolean printThreadName) { + this.printThreadName = printThreadName; + }</b> + + public String doLayout(LoggingEvent event) { + StringBuffer sbuf = new StringBuffer(128); + <b>if (prefix != null) { + sbuf.append(prefix + ": "); + }</b> + sbuf.append(event.getTimeStamp() - LoggingEvent.getStartTime()); + sbuf.append(" "); + sbuf.append(event.getLevel()); + <b>if (printThreadName) { + sbuf.append(" ["); + sbuf.append(event.getThreadName()); + sbuf.append("] "); + } else { + sbuf.append(" "); + }</b> + sbuf.append(event.getLoggerRemoteView().getName()); + sbuf.append(" - "); + sbuf.append(event.getFormattedMessage()); + sbuf.append(LINE_SEP); + return sbuf.toString(); + } +}</pre></div> + + <p>Appart from the actual use of the two attributes, in the <code>doLayout</code> method, + the two setter methods are the only addition to the original class. Yet, it is sufficient + to allow the user to configure these attributes, as shown in the configuration file below:</p> + +<div class="source"><pre><configuration> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="chapter5.MySampleLayout2"> + <b><prefix>MyPrefix</prefix> + <printThreadName>false</printThreadName></b> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + Note that the <code>PrintThreadName</code> attribute is a boolean + and not a <code>String</code>. It can be configured anyway by writing <em>true</em> + of <em>false</em> in the configuration file. + </p> + + + <a name="PatternLayout"></a> + <h3>PatternLayout</h3> + + <p> + Logback classic ships with a flexible layout called + <a href="../xref/ch/qos/logback/classic/PatternLayout.html"> + <code>PatternLayout</code></a>. + As all classic layouts, <code>PatternLayout</code> + takes a logging event and returns a String. However, the + returned String can be customized at will by tweaking its + conversion pattern. + </p> + <p> + The conversion pattern of + <code>PatternLayout</code> + is closely related to the conversion pattern of the + <code>printf()</code> + function in the C programming language. A conversion pattern + is composed of literal text and format control expressions + called conversion specifiers. You are free to insert any + literal text within the conversion pattern. Each conversion + specifier starts with a percent sign (%) and is followed by + optional format modifiers, a conversion word and optional + parameters between braces. The + conversion word controls the type of data to use, e.g. + logger name, level, date, thread name. The format modifiers + control such things as field width, padding, and left or + right justification. The following is a simple example. + </p> + <em> + Example 5.1: Sample usage of a PatternLayout + <a href="../xref/chapter5/PatternSample.html"> + (logback-examples/src/main/java/chapter5/PatternSample.java)</a> + </em> + <div class="source"><pre>package chapter5; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.core.ConsoleAppender; + +public class PatternSample { + + static public void main(String[] args) throws Exception { + Logger rootLogger = (Logger)LoggerFactory.getLogger("root"); + + <b>PatternLayout layout = new PatternLayout(); + layout.setPattern("%-5level [%thread]: %message%n"); + layout.start();</b> + + ConsoleAppender<LoggingEvent> appender = new ConsoleAppender<LoggingEvent>(); + appender.setContext(rootLogger.getLoggerContext()); + appender.setLayout(layout); appender.start(); + + rootLogger.addAppender(appender); + + rootLogger.debug("Message 1"); + rootLogger.warn("Message 2"); + } +}</pre></div> + + <p> + The conversion pattern is set to be <b>"%-5level [%thread]: + %message%n"</b>. Running PatternSample will yield the following + output on the console. + </p> + <div class="source"><pre>DEBUG [main]: Message 1 +WARN [main]: Message 2</pre></div> + <p> + Note that in the conversion pattern <b>"%-5level [%thread]: + %message%n"</b> there is no explicit separator between literal + text and conversion specifiers. When parsing a conversion + pattern, + <code>PatternLayout</code> + is capable of differentiating between literal text (space + characters, the brackets, colon character) and conversion + specifiers. In the example above, the conversion specifier + %-5level means the level of the logging event should be left + justified to a width of five characters. Format specifiers + will be explained in a short moment. + </p> + <p> + Note that usual brackets chars <em>(</em> + and <em>)</em> need to be escaped to be parsed correctly. These + brackets can be used by adding two backslashes before the bracket + like in <em>\\)</em> and <em>\\)</em>. + </p> + <p> + As mentionned previously, certain conversion specifiers can include + optional parameters which are declared + between braces following the conversion word. A sample conversion + specifier with options could be <em>%logger{10}</em>. + </p> + + <p>The recognized conversions words along with their options are + described below. When multiple conversion words are listed on the left + column, they should be considered as aliases. + </p> + + <table class="bodyTable"> + <th>Conversion Word</th> + <th>Effect</th> + + <tr class="b"> + <td> + <b>c</b>{<em>length</em>} <br></br> + <b>l</b>{<em>length</em>} <br></br> + <b>lo</b>{<em>length</em>} <br></br> + <b>logger</b>{<em>length</em>} <br></br> + </td> + + <td> + <p> + Used to output the name of the logger at the + source of the logging event. + </p> + <p> + The logger name conversion word can take an + integer as a first option. The + converter's abbreviation algorithm will shorten the logger name + without significant loss of meaning. + </p> + + <p>The next table should clarify the matter.</p> + + <table class="bodyTable"> + <tr class="a"> + <th>Conversion Pattern</th> + <th>Logger name</th> + <th>Result</th> + </tr> + <tr class="b"> + <td>%logger</td> + <td>mainPackage.sub.sample.Bar</td> + <td>mainPackage.sub.sample.Bar</td> + </tr> + <tr class="a"> + <td>%logger{10}</td> + <td>mainPackage.sub.sample.Bar</td> + <td>m.s.s.Bar</td> + </tr> + + <tr class="b"> + <td>%logger{15}</td> + <td>mainPackage.sub.sample.Bar</td> + <td>m.s.sample.Bar</td> + </tr> + + <tr class="a"> + <td>%logger{16}</td> + <td>mainPackage.sub.sample.Bar</td> + <td>m.sub.sample.Bar</td> + </tr> + + <tr class="b"> + <td>%logger{26}</td> + <td>mainPackage.sub.sample.Bar</td> + <td>mainPackage.sub.sample.Bar</td> + </tr> + </table> + </td> + </tr> + <tr class="a"> + <td> + <b>C</b>{<em>length</em>} <br></br> + <b>class</b>{<em>length</em>} <br></br> + </td> + + <td> + <p> + Used to output the fully qualified class name of + the caller issuing the logging request. + </p> + <p> + Just like the <em>%logger</em> conversion word above, this + word can take an interger as it's first option + and use its abbreviation algorithm to + shorten the class name. + </p> + <p> + By default the class name is output in full. + </p> + <p> + Generating the caller class information is not particularly fast. + Thus, it's use should be avoided unless + execution speed is not an issue. + </p> + </td> + </tr> + + <tr class="b"> + <td> + <b>d</b>{<em>pattern</em>} <br></br> + <b>date</b>{<em>pattern</em>} <br></br> + </td> + <td> + <p>Used to output the date of the logging event. + The date conversion word may be followed by an option + enclosed between braces.</p> + <p>The option admits the same syntax as the time pattern + string of the <code>java.text.SimpleDateFormat</code>.</p> + <p>A shortcut to the ISO8601 format is available by + specifying the String <em>"ISO8601"</em> in the braces. If no option is set, + the converter uses <em>"ISO8601"</em> as the default value.</p> + <p>Here are some sample option values. They assume + that the actual date is Friday 20th of October, 2006 and that + the author finished his meal a short while ago.</p> + + <table class="bodyTable"> + <tr class="a"> + <th>Conversion Pattern</th> + <th>Result</th> + </tr> + <tr class="b"> + <td>%date</td> + <td>2006-10-20 14:46:49,812</td> + </tr> + <tr class="a"> + <td>%date{ISO8601}</td> + <td>2006-10-20 14:46:49,812</td> + </tr> + <tr class="b"> + <td>%date{HH:mm:ss.SSS}</td> + <td>14:46:49.812</td> + </tr> + <tr class="a"> + <td>%date{dd�MMM�yyyy�;HH:mm:ss.SSS}</td> + <td>20 oct. 2006;14:46:49.812 </td> + </tr> + </table> + </td> + </tr> + + <tr class="b"> + <td> + <b>F / file</b> + </td> + + <td> + <p> + Used to output the file name where the logging + request was issued. + </p> + <p> + Generating the file information is not particularly fast. + Thus, it's use should be avoided unless + execution speed is not an issue. + </p> + </td> + </tr> + + <tr class="a"> + <td> + <b>caller{depth}</b> + <b>caller{depth, evaluator-1, ... evaluator-n}</b> + </td> + + <td> + <p> + Used to output location information of the + caller which generated the logging event. + </p> + <p> + The location information depends on the JVM + implementation but usually consists of the fully + qualified name of the calling method followed by + the caller's source the file name and line + number between parentheses. + </p> + <p> + A integer can be added to the + <em>caller</em> + conversion specifier's options to configure the depth of + the information to be displayed. + </p> + <p>For example, <b>%caller{2}</b> would display the following excerpt:</p> + +<div class="source"><pre>0 [main] DEBUG - logging statement +Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) +Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre></div> + <p>And <b>%caller{3}</b> would display this other excerpt:</p> +<div class="source"><pre>16 [main] DEBUG - logging statement +Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) +Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) +Caller+2 at mainPackage.ConfigTester.main(ConfigTester.java:38)</pre></div> + + <p> + This conversion word can also use evaluators to test logging events + against a given criteria before creating the output. For example, + using <b>%caller{3, CALLER_DISPLAY_EVAL}</b> will display three lines + of stacktrace, only if the evaluator called <em>CALLER_DISPLAY_EVAL</em> + returns a <b>positive</b> answer. + </p> + <p>Evaluators are described + further down this document. + </p> + </td> + </tr> + + <tr class="b"> + <td> + <b>L / line</b> + </td> + + <td> + <p> + Used to output the line number from where the + logging request was issued. + </p> + <p> + Generating the line number information is not particularly fast. + Thus, it's use should be avoided unless + execution speed is not an issue. + </p> + </td> + </tr> + + + <tr class="a"> + <td> + <b>m / msg / message</b> + </td> + <td> + Used to output the application supplied message + associated with the logging event. + </td> + </tr> + + <tr class="b"> + <td> + <b>M / method</b> + </td> + + <td> + <p> + Used to output the method name where the logging + request was issued. + </p> + <p> + Generating the method name is not particularly fast. + Thus, it's use should be avoided unless + execution speed is not an issue. + </p> + </td> + </tr> + + <tr class="a"> + <td> + <b>n</b> + </td> + + <td> + <p> + Outputs the platform dependent line separator + character or characters. + </p> + <p> + This conversion word offers practically the + same performance as using non-portable line + separator strings such as "\n", or "\r\n". Thus, + it is the preferred way of specifying a line + separator. + </p> + </td> + + </tr> + + <tr class="b"> + <td> + <b>p / le / level</b> + </td> + <td>Used to output the level of the logging event.</td> + </tr> + + <tr class="a"> + + <td> + <b>r / relative</b> + </td> + + <td> + Used to output the number of milliseconds elapsed + since the start of the application until the + creation of the logging event. + </td> + </tr> + + + <tr class="b"> + <td> + <b>t / thread</b> + </td> + + <td> + Used to output the name of the thread that generated + the logging event. + </td> + + </tr> + + <tr class="a"> + <td> + <b>X</b>{<em>key</em>} <br></br> + <b>mdc</b>{<em>key</em>} <br></br> + </td> + + <td> + + <p> + Used to output the MDC (mapped diagnostic + context) associated with the thread that + generated the logging event. + </p> + <p> + If + <b>mdc</b> + conversion word is followed by a key + between braces, as in <b>%mdc{clientNumber}</b>, + then the value in the MDC corresponding + to the key will be output. + </p> + <p> + If no option is given, then + the entire content of the MDC will be output in the format + "key1=val1, key2=val2". + </p> + + <p> + See + <a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/MDC.html"> + MDC + </a> + class for more details. + </p> + + </td> + </tr> + <tr class="b"> + <td> + <b>ex</b>{<em>length</em>} <br></br> + <b>throwable</b>{<em>length</em>} <br></br> + <b>ex{length, evaluator-1, ..., evaluator-n}</b> + <b>throwable{length, evaluator-1, ..., evaluator-n}</b> + </td> + + <td> + <p> + Used to output the stack trace of the exception associated + with the logging event, if any. By default the full stack trace + will be output. + </p> + <p>The <em>throwable</em> conversion word can followed by one of + the following options: + </p> + <ul> + <p><em>short</em>: prints the first line of the stack trace</p> + <p><em>full</em>: prints the full stack trace</p> + <p>Any integer: prints the given number of lines of the stack trace</p> + </ul> + + <p>Here are some examples:</p> + + <table class="bodyTable"> + <tr class="a"> + <th>Conversion Pattern</th> + <th>Result</th> + </tr> + <tr class="b"> + <td>%ex</td> + <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem + at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22) + at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17) + at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td> + </tr> + <tr class="a"> + <td>%ex{short}</td> + <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem + at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)</pre></td> + </tr> + <tr class="b"> + <td>%ex{full}</td> + <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem + at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22) + at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17) + at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td> + </tr> + <tr class="a"> + <td>%ex{2}</td> + <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem + at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22) + at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)</pre></td> + </tr> + </table> + + <p> + This conversion word can also use evaluators to test logging events + against a given criteria before creating the output. For example, + using <b>%ex{full, EX_DISPLAY_EVAL}</b> will display the full + stacktrace of the exception, only if the evaluator called <em>EX_DISPLAY_EVAL</em> + returns a <b>negative</b> answer. Evaluators are described + further down this document. + </p> + </td> + </tr> + + <tr class="b"> + <td> + <b>marker</b> + </td> + + <td> + <p> + Used to output the marker associated with the logger request. + </p> + <p> + If there is a single marker available, its name is displayed. + In case the marker has children markers, the converter displays + the parent's and children's names as shown below. + </p> + <p> + <em>parentName [ child1, child2 ]</em> + </p> + </td> + </tr> + <tr class="a"> + + <td> + <b>%</b> + </td> + + <td>The sequence %% outputs a single percent sign.</td> + </tr> + + </table> + <p> + By default the relevant information is output as is. + However, with the aid of format modifiers it is possible to + change the minimum field width, the maximum field width and + justification. + </p> + <p> + The optional format modifier is placed between the percent + sign and the conversion character or word. + </p> + <p> + The first optional format modifier is the + <em>left justification flag</em> + which is just the minus (-) character. Then comes the + optional + <em>minimum field width</em> + modifier. This is a decimal constant that represents the + minimum number of characters to output. If the data item + contains fewer characters, it is padded on either the left + or the right until the minimum width is reached. The default + is to pad on the left (right justify) but you can specify + right padding with the left justification flag. The padding + character is space. If the data item is larger than the + minimum field width, the field is expanded to accommodate + the data. The value is never truncated. + </p> + <p> + This behavior can be changed using the + <em>maximum field width</em> + modifier which is designated by a period followed by a + decimal constant. If the data item is longer than the + maximum field, then the extra characters are removed from + the <em>beginning</em> + of the data item. For example, if the + maximum field width is eight and the data item is ten + characters long, then the first two characters of the data + item are dropped. This behavior deviates from the printf + function in C where truncation is done from the end. + </p> + <p> + Truncation from the end is possible by appending a minus + character right after the period. In that case, if the + maximum field width is eight and the data item is ten + characters long, then the last two characters of the data + item are dropped. + </p> + <p> + Below are various format modifier examples for the logger + conversion specifier. + </p> + + <table class="bodyTable"> + <th>Format modifier</th> + <th>Left justify</th> + <th>Minimum width</th> + <th>Maximum width</th> + <th>Comment</th> + + <tr class="b"> + <td>%20logger</td> + <td>false</td> + <td>20</td> + <td>none</td> + <td> + Left pad with spaces if the category name is less + than 20 characters long. + </td> + </tr> + <tr class="a"> + <td>%-20logger</td> + <td>true</td> + <td>20</td> + <td>none</td> + <td> + Right pad with spaces if the logger name is less + than 20 characters long. + </td> + </tr> + <tr class="b"> + <td>%.30logger</td> + <td>NA</td> + <td>none</td> + <td>30</td> + <td> + Truncate from the beginning if the logger name is + longer than 30 characters. + </td> + </tr> + <tr class="a"> + <td>%20.30logger</td> + <td>false</td> + <td>20</td> + <td>30</td> + <td> + Left pad with spaces if the logger name is shorter + than 20 characters. However, if logger name is + longer than 30 characters, then truncate from the + beginning. + </td> + </tr> + <tr class="b"> + <td>%-20.30logger</td> + <td>true</td> + <td>20</td> + <td>30</td> + <td> + Right pad with spaces if the logger name is shorter + than 20 characters. However, if logger name is + longer than 30 characters, then truncate from the + <em>beginning</em>. + </td> + </tr> + <tr class="a"> + <td>%.-30logger</td> + <td>NA</td> + <td>none</td> + <td>30</td> + <td> + Truncate from the <em>end</em> if the logger name is + longer than 30 characters. + </td> + </tr> + </table> + + <p>Here are some examples of the format modifier truncation:</p> + + + <table class="bodyTable"> + <th>Format modifier</th> + <th>Logger name</th> + <th>Result</th> + <tr class="b"> + <td>[%-20.20logger]</td> + <td>main.Name</td> + <td><pre>[main.Name ]</pre></td> + </tr> + <tr class="a"> + <td>[%20.-20logger]</td> + <td>main.Name</td> + <td><pre>[ main.Name]</pre></td> + </tr> + <tr class="b"> + <td>[%-10.10logger]</td> + <td>main.foo.foo.bar.Name</td> + <td><pre>[o.bar.Name]</pre></td> + </tr> + <tr class="a"> + <td>[%10.-10logger]</td> + <td>main.foo.foo.bar.Name</td> + <td><pre>[main.foo.f]</pre></td> + </tr> + </table> + + <h3>Option handling</h3> + + <p> + A conversion specifier can be followed by options between + braces. We have already seen some of the + possibilities offered by logback's option handling with, for + example, the MDC conversion specifier: + <em>%mdc{someKey}</em>. + </p> + <p>A conversion specifier might have more than one options. For example, + a conversion specifier that uses evaluators, which we will cover very soon, + simply adds the evaluator names to the option list, as shown below:</p> + + <div class="source"><pre> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <param name="Pattern" value="%-4relative [%thread] %-5level - %msg%n \ + <b>%caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</b>" /> + </layout> + </appender></pre></div> + + + + + <h4>Evaluators</h4> + <p> + Another use case for adding options to a conversion + specifier is when + <code>PatternLayout</code> + is used with + <a href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html"> + <code>EventEvaluator</code></a> objects. + </p> + <p> + <code>EventEvaluator</code> objects + have the responsability to check wether a given event + matches a given criteria. + </p> + <p> + Let's look at an example using + <code>EventEvaluator</code> objects. + The following configuration file outputs the logging + events to the console, displaying date, thread, level, + message and caller data. + </p> + <p> + Since displaying the caller data of a logging event is rather + expensive, this information will be displayed only when the + logging request comes from a specific logger, and whose + message contains a certain string. By doing that, we make + sure that only the specific logging requests will have + their caller information generated and displayed, without + penalizing application performance. + </p> + + <p> + Here is how to configure logback to behave like we + described: + </p> + <em> + Example 5.2: Sample usage of EventEvaluators + (logback-examples/src/main/java/chapter5/callerEvaluatorConfig.xml) + </em> + <div class="source"><pre><configuration> + <b><evaluator name="DISP_CALLER_EVAL"> + <Expression>logger.getName().contains("chapter5") && \ + message.contains("who calls thee")</Expression> + </evaluator></b> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <param name="Pattern" value="%-4relative [%thread] %-5level - %msg%n \ + <b>%caller{2, DISP_CALLER_EVAL}</b>" /> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + <p>Please note that the & value cannot be written like one would do in a java + class, because of XML encoding rules.</p> + <p>Let us test this configuration with the following code.</p> + <em> + Example 5.2: Sample usage of EventEvaluators + <a href="../xref/chapter5/CallerEvaluatorExample.html"> + (logback-examples/src/main/java/chapter5/CallerEvaluatorExample.java)</a> + </em> + <div class="source"><pre>package chapter5; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.util.StatusPrinter; + +public class CallerEvaluatorExample { + + public static void main(String[] args) { + Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class); + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + + try { + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + configurator.doConfigure(args[0]); + } catch (JoranException je) { + StatusPrinter.print(lc); + } + + for (int i = 0; i < 5; i++) { + if (i == 3) { + logger.debug("who calls thee?"); + } else { + logger.debug("I know me " + i); + } + } + } +}</pre></div> + <p> + This excerpt does nothing too fancy. Five logging requests + are issued, the third one being different from the others. + </p> + <p> + When a logging request is sent, the corresponding logging + event will pass through the evaluation process. Here, + the third request will match the evaluation, + causing its caller data to be displayed. + </p> + <p> + Here is the output of the + <code>CallerEvaluatorExample</code> + class. + </p> + <div class="source"><pre>0 [main] DEBUG - I know me 0 +0 [main] DEBUG - I know me 1 +0 [main] DEBUG - I know me 2 +0 [main] DEBUG - who calls thee? +Caller+0 at chapter5.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28) + +0 [main] DEBUG - I know me 4</pre></div> + + <p> + Of course, one can change the expression to match a real + world situation. An expression testing logger name and + request level could also be meaningful: all logging requests of + level <em>WARN</em> and up, coming from a sensible part of an application + like a financial transaction module, would have their caller data displayed. + </p> + <p><b>Important:</b> With the <em>caller</em> conversion specifier, the data is + displayed when <em>the expression evaluates to <b>true</b>.</em></p> + <p> + Let us look at a different situation. When exceptions are included in + a logging request, their stack trace is usually displayed. However, in some cases, + one might want to supress the stack trace of specific exception. + </p> + <p>The java code shown below creates five log requests, each one + with an exception. However, we do not want to have the stack trace of the + third request to be output.</p> + +<div class="source"><pre>package chapter5; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.util.StatusPrinter; + +public class ExceptionEvaluatorExample { + + public static void main(String[] args) { + Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class); + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + + try { + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + configurator.doConfigure(args[0]); + } catch (JoranException je) { + StatusPrinter.print(lc); + } + for (int i = 0; i < 5; i++) { + if (i == 3) { + logger.debug("logging statement " + i, new TestException( + "do not display this")); + } else { + logger.debug("logging statement " + i, new Exception("display")); + } + } + } +}</pre></div> + + <p>The following configuration will allow that.</p> + <em> + Example 5.3: Sample usage of EventEvaluators + (logback-examples/src/main/java/chapter5/exceptionEvaluatorConfig.xml) + </em> + <div class="source"><pre><configuration> + + <b><evaluator name="DISPLAY_EX_EVAL"> + <Expression>throwable != null && throwable instanceof \ + chapter5.TestException</Expression> + </evaluator></b> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <param name="Pattern" + value="%-4relative [%thread] %-5level - %msg \ + <b>%ex{full, DISPLAY_EX_EVAL}</b>%n" /> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p> + With this configuration, each time an instance of the + <em>chapter5.TestException</em> + is included within a logging request, no stack trace will be displayed. + </p> + <p><b>Important:</b> With the <b><em>%ex</em></b> conversion specifier, the data is + displayed when <em>the expression evaluates to <b>false</b>.</em></p> + + <h3>Creating a custom conversion specifier</h3> + <p>We've seen up to here quite a lot of possibilities with conversion specifier and + <code>PatternLayout</code> objects. But what if somebody wants to make her own conversion + specifier?</p> + + <p>In that case, two steps are needed.</p> + + <p>First, one must implement her own <code>Converter</code> + class. <a href="../xref/ch/qos/logback/core/pattern/Converter.html"> + <code>Converter</code></a> objects are responsible to extract a specific information out of + a <code>LoggingEvent</code>. When <em>%logger</em> is used, a + <a href="../xref/ch/qos/logback/classic/pattern/LoggerConverter.html"> + <code>LoggerConverter</code></a> + is called to extract the name of the logger from the <code>LoggingEvent</code>.</p> + + <p>Let us say that our customized <code>Converter</code> will output the level of the logging + event, colored following ANSI rules. Here is the necessary implementation:</p> + +<em> Example 5.4: Sample Converter Example +<a href="../xref/chapter5/MySampleConverter.html"> +(src/main/java/chapter5/MySampleConverter.java)</a></em> +<div class="source"><pre>package chapter5; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.pattern.ClassicConverter; +import ch.qos.logback.classic.spi.LoggingEvent; + +public class MySampleConverter extends ClassicConverter { + + private static final String END_COLOR = "\u001b[m"; + + private static final String ERROR_COLOR = "\u001b[0;31m"; + private static final String WARN_COLOR = "\u001b[0;33m"; + + @Override + <b>public String convert(LoggingEvent event) { + StringBuffer sbuf = new StringBuffer(); + sbuf.append(getColor(event.getLevel())); + sbuf.append(event.getLevel()); + sbuf.append(END_COLOR); + return sbuf.toString(); + }</b> + + /** + * Returns the appropriate characters to change the color for the specified + * logging level. + */ + private String getColor(Level level) { + switch (level.toInt()) { + case Level.ERROR_INT: + return ERROR_COLOR; + case Level.WARN_INT: + return WARN_COLOR; + default: + return ""; + } + } +} +</pre></div> + + <p>This implementation is quite straightforward. The <code>MySampleConverter</code> class + extends <code>ClassicConverter</code>, and implements the <code>convert</code> method. + In that method, all it has to do is return the appropriate information. + </p> + + <p>The second step, once the <code>Converter</code> class done, is to let logback know about + the new <code>Converter</code>. For this task, we just need to declare the new + conversion word in the configuration file, as shown below:</p> + +<em> Example 5.4: Sample Converter Example (src/main/java/chapter5/mySampleConverterConfig.xml)</em> +<div class="source"><pre><configuration> + + <b><conversionRule conversionWord="sample" converterClass="chapter5.MySampleConverter" /></b> + + <appender name="STDOUT" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <b><Pattern>%-4relative [%thread] %sample - %msg%n</Pattern></b> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> + + <p>In this configuration file, once the new conversion word has been declared, we + just use it within a <code>PatternLayout</code> pattern element, as if + our custom conversion word had always been here.</p> + + <p>The intersted reader might want to take a look at other <code>Converter</code> implementations + like + <a href="../xref/ch/qos/logback/classic/pattern/MDCConverter.html"> + <code>MDCConverter</code></a> to learn how to implement more complex behaviours, involving + the use of options, in her custom <code>Converter</code> objects. + </p> + + <a name="ClassicHTMLLayout"></a> + <h3>HTMLLayout</h3> + <p><a href="../xref/ch/qos/logback/classic/html/HTMLLayout.html"> + <code>HTMLLayout</code></a> outputs events in an HTML table. Each row of the table corresponds to a + logging event.</p> + + <p>Here is a sample of the output that can be obtained using <code>HTMLLayout</code> + along with the default CSS stylesheet:</p> + <img src="images/chapter5/htmlLayout1.gif" alt="HTML Layout Sample Image"></img> + + <p> + The content of the table columns are specified using a + conversion pattern. See <code>PatternLayout</code> for documentation on + the available patterns. This ensures that the user has full control over the creation + of the html table. One can choose to display any (or all) data that <code>PatternLayout</code> + can provide. + </p> + <p>One notable point about the use of <code>PatternLayout</code> with <code>HTMLLayout</code> + is that conversion specifiers should not be separated by a space or in general + any literals. Each specifier found in the + pattern will result in a separate column, meaning that each literal will create + an extra column.</p> + <p> + The pattern <em>%ex</em> + used to display an Exception is not the only way to display + an Exception with this layout. If you use this pattern, a + table column will be created to display the potential + Exception's stacktrace. That means that, in most cases, the column + will be empty, and will take quite a lot of space when displaying + an exception's stack trace. + </p> + <p> + Since printing a stack trace on a separate column is not very readable, + a better solution is available in the form of + implementations of the <code>IThrowableRenderer</code> interface. + These implementations can be called and assigned to + <code>HTMLLayout</code> to manage the display of anything related to + Exceptions. + </p> + <p> + By default, a + <a href="../xref/ch/qos/logback/classic/html/DefaultThrowableRenderer.html"> + <code>DefaultThrowableRenderer</code></a> is + assigned to the <code>HTMLLayout</code>. It writes the Exception on a <em>new + table row</em>, along with its stacktrace, in a easily readable + manner, like presented in the picture above. + </p> + <p> + If one wants to use the + <em>%ex</em> + pattern anyway, then a + <a href="../xref/ch/qos/logback/core/html/NOPThrowableRenderer.html"> + <code>NOPThrowableRenderer</code></a> can be specified + in the configuration file. + </p> + <p> + A user-specified external CSS file can be linked to the html + page. In that case, the following + xml element can be nested into the <code><layout></code> element. + </p> +<div class="source"><pre><layout> + ... + <cssBuilder class="ch.qos.logback.core.html.UrlCssBuilder"> + <param name="url" value="path_to_StyleFile.css" /> + </cssBuilder> + ... +</layout></pre></div> + + <p>In case one does not want to customize the html + output, an internal CSS style is used.</p> + + <p> + The <code>HTMLLayout</code> is often, although not necessarily used in conjunction with + <code>SMTPAppender</code>, to send a nicely formatted html email. + </p> + <p> + When one wants to use the <code>HTMLLayout</code> with a + <code>SMTPAppender</code>, + the following configuration would be typical. + </p> + <div class="source"><pre><configuration> + <appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender"> + <layout class="ch.qos.logback.classic.html.HTMLLayout"> + <param name="pattern" value="%relative%thread%mdc%level%class%msg" /> + </layout> + <param name="From" value="sender.email@domain.net" /> + <param name="SMTPHost" value="mail.domain.net" /> + <param name="Subject" value="LastEvent: %class - %msg" /> + <param name="To" value="destination.email@domain.net" /> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="SMTP" /> + </root> +</configuration></pre></div> + + <p><code>HTMLLayout</code> can also be used with any <code>FileAppender</code>. In that + case, one can specify a rolling policy to archive log messages automatically. + One real world example could use the configuration below.</p> + +<div class="source"><pre><configuration> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <ActiveFileName>lastLogEntries.html</ActiveFileName> + <FileNamePattern>logEntries.%d{yyyy-MM-dd}.log</FileNamePattern> + </rollingPolicy> + + <layout class="ch.qos.logback.classic.html.HTMLLayout"> + <cssBuilder class="ch.qos.logback.core.html.UrlCssBuilder"> + <url>address_of_a_custom_stylesheet.css</url> + </cssBuilder> + <Pattern>%relative%thread%mdc%level%logger%msg</Pattern> + <b><Title>Logging Events</Title></b> + </layout> + </appender> + + <root> + <level value="debug" /> + <appender-ref ref="FILE" /> + </root> +</configuration></pre></div> + + <h2>Logback access</h2> + + <p>Many access layouts are mere adaptations of classic layouts. Logback + classic and access modules address different needs, but offer comparable power + and flexibility.</p> + + <h3>Writing your own Layout</h3> + <p>Writing a custom <code>Layout</code> for logback access is nearly identical + as to writing a <code>Layout</code> for the classic module.</p> + + <a name="AccessPatternLayout"></a> + <h3>PatternLayout</h3> + <p>Access' <a href="../xref/ch/qos/logback/access/PatternLayout.html"> + <code>PatternLayout</code></a> work the exact same way as it's classic counterpart. + </p> + <p>However, the conversion specifier are different, giving specific access to request + and response objects' attributes.</p> + + <p>Here are the conversion specifier one can use with logback access + <code>PatternLayout</code>.</p> + + <table class="bodyTable"> + <th>Conversion Word</th> + <th>Effect</th> + + <tr class="b"> + <td><b>a / remoteIP</b></td> + <td> + <p> + Remote IP address. + </p> + </td> + </tr> + <tr class="a"> + <td><b>A / localIP</b></td> + <td> + <p> + Local IP address. + </p> + </td> + </tr> + <tr class="b"> + <td><b>b / B / byteSent</b></td> + <td> + <p> + Response's content length. + </p> + </td> + </tr> + <tr class="a"> + <td><b>h / clientHost</b></td> + <td> + <p> + Remote host. + </p> + </td> + </tr> + <tr class="b"> + <td><b>H / protocol</b></td> + <td> + <p> + Request protocol. + </p> + </td> + </tr> + <tr class="a"> + <td><b>l</b></td> + <td> + <p> + Remote log name. In logback-access, this converter always + returns the value "-". + </p> + </td> + </tr> + + <tr class="b"> + <td><b>reqParameter{paramName}</b></td> + <td> + <p> + Parameter of the response. + </p> + <p>This conversion word takes the first option in braces and looks + for the corresponding parameter in the request.</p> + <p><b>%reqParameter{input_data}</b> + displays the corresponding parameter.</p> + </td> + </tr> + <tr class="a"> + <td><b>i{header} / header{header}</b></td> + <td> + <p> + Request header. + </p> + <p>This conversion word takes the first option in braces and looks + for the corresponding header in the request.</p> + <p><b>%header{Referer}</b> displays the referer of the request.</p> + <p> + If no option is specified, it displays every available header. + </p> + </td> + </tr> + <tr class="b"> + <td><b>m / requestMethod</b></td> + <td> + <p> + Request method. + </p> + </td> + </tr> + <tr class="a"> + <td><b>r / requestURL</b></td> + <td> + <p> + URL requested. + </p> + </td> + </tr> + <tr class="b"> + <td><b>s / statusCode</b></td> + <td> + <p> + Status code of the request. + </p> + </td> + </tr> + <tr class="a"> + <td><b>t / date</b></td> + <td> + <p> + Used to output the date of the logging event. + The date conversion specifier may be followed by + a set of braces containing a date and time + pattern strings used by + <code>java.text.SimpleDateFormat</code> + . + <em>ABSOLUTE</em> + , + <em>DATE</em> + or + <em>ISO8601</em> + can also be used. + </p> + <p> + For example, + <b>%d{HH:mm:ss,SSS}</b> + , + <b> + %d{dd�MMM�yyyy�;HH:mm:ss,SSS} + </b> + or + <b>%d{DATE}</b> + . If no date format specifier is given then + ISO8601 format is assumed. + </p> + </td> + </tr> + <tr class="b"> + <td><b>u / user</b></td> + <td> + <p> + Remote user. + </p> + </td> + </tr> + <tr class="a"> + <td><b>U / requestURI</b></td> + <td> + <p> + Requested URI. + </p> + </td> + </tr> + <tr class="b"> + <td><b>v / server</b></td> + <td> + <p> + Server name. + </p> + </td> + </tr> + <tr class="a"> + <td><b>localPort</b></td> + <td> + <p> + Local port. + </p> + </td> + </tr> + <tr class="b"> + <td><b>reqAttribute{attributeName}</b></td> + <td> + <p> + Attribute of the request. + </p> + <p>This conversion word takes the first option in braces and looks + for the corresponding attribute in the request.</p> + <p><b>%reqAttribute{SOME_ATTRIBUTE}</b> + displays the corresponding attribute.</p> + </td> + </tr> + <tr class="a"> + <td><b>reqCookie{cookie}</b></td> + <td> + <p> + Request cookie. + </p> + <p>This conversion word takes the first option in braces and looks + for the corresponding cookie in the request.</p> + <p><b>%cookie{COOKIE_NAME}</b> displays corresponding cookie.</p> + </td> + </tr> + <tr class="b"> + <td><b>responseHeader{header}</b></td> + <td> + <p> + Header of the response. + </p> + <p>This conversion word takes the first option in braces and looks + for the corresponding header in the response.</p> + <p><b>%header{Referer}</b> displays the referer of the response.</p> + </td> + </tr> + <tr class="a"> + <td><b>requestContent</b></td> + <td> + <p> + This conversion word displays the content of the request, that is the + request's <code>InputStream</code>. It is used in conjunction with a + <a href="../xref/ch/qos/logback/access/servlet/TeeFilter.html"> + <code>TeeFilter</code></a>, a <code>javax.servlet.Filter</code> that + replaces the original <code>HttpServletRequest</code> + by a <a href="../xref/ch/qos/logback/access/servlet/TeeHttpServletRequest.html"> + <code>TeeHttpServletRequest</code></a>. The latter object allows + access to the requet's <code>InputStream</code> multiple times without + any loss of data. + </p> + </td> + </tr> + <tr class="b"> + <td><b>fullRequest</b></td> + <td> + <p> + This conversion word takes all the available headers of the + request and displays their values. + </p> + </td> + </tr> + <tr class="a"> + <td><b>responseContent</b></td> + <td> + <p> + This conversion word displays the content of the response, that is the + response's <code>InputStream</code>. It is used in conjunction with a + <a href="../xref/ch/qos/logback/access/servlet/TeeFilter.html"> + <code>TeeFilter</code></a>, a <code>javax.servlet.Filter</code> that + replaces the original <code>HttpServletResponse</code> + by a <a href="../xref/ch/qos/logback/access/servlet/TeeHttpServletResponse.html"> + <code>TeeHttpServletResponse</code></a>. The latter object allows + access to the requet's <code>InputStream</code> multiple times without + any loss of data. + </p> + </td> + </tr> + <tr class="b"> + <td><b>fullResponse</b></td> + <td> + <p> + This conversion word takes all the available headers of the + response and displays their values. + </p> + </td> + </tr> + </table> + + <p>Logback access' <code>PatternLayout</code> also recognize three keywords, which + act like shortcuts to a certain pattern.</p> + + <ul> + <p><em>common</em> or <em>CLF</em></p> + <p><em>combined</em></p> + </ul> + + <p>The <em>common</em> keyword corresponds to the pattern <em>%h %l %u %t \"%r\" %s %b</em> + which displays client host, remote log name, user, date, requested URL, status code + and response's content length</p> + + <p>The <em>combined</em> keyword is a shortcut to + <em>%h %l %u %t \"%r\" %s %b \"%i{Referer}\" \"%i{User-Agent}\"</em>. This pattern begins + much like the <em>common</em> pattern but also displays two request headers, namely + referer, and user-agent.</p> + + <a name="AccessHTMLLayout"></a> + <h3>HTMLLayout</h3> + + <p>The access version of + <a href="../xref/ch/qos/logback/access/html/HTMLLayout.html"> + <code>HTMLLayout</code></a> works like logback classic's + version.</p> + + <p>By default, it will create a table containing the following data:</p> + + <ul> + <p>Remote IP</p> + <p>Date</p> + <p>Request URL</p> + <p>Status code</p> + <p>Content Length</p> + </ul> + + <p>Here is what you can expect from a configured access <code>HTMLLayout</code>:</p> + <img src="images/chapter5/htmlLayoutAccess.gif" alt="Access HTML Layout Sample Image"></img> + + <p>What's better than a real world example? Our own log4j properties to logback configuration + <a href="http://logback.qos.ch/translator/">translator</a> + is using logback access to showcase a live ouput, using a <code>RollingFileAppender</code> and + access' <code>HTMLLayout</code>.</p> + + <p>You can see the file by <a href="http://logback.qos.ch/translator/logs/access.html">following this link</a>.</p> + + <p>Just like any access log, it can be altered simply by visiting the + <a href="http://logback.qos.ch/translator/">translator</a> application.</p> + + +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/manual/mdc.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/manual/mdc.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,623 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Chapter 7: Diagnostic Context</title> +<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" /> +</head> +<body> +<script src="../templates/header.js"></script> +<div id="left"> + <script src="../templates/left.js"></script> +</div> +<div id="right"> + <script src="../templates/right.js"></script> +</div> +<div id="content"> + <h2>Chapter 7: Diagnostic Context</h2> + <div class="author"> + Authors: Ceki G�lc�, S�bastien Pennec + </div> + + <table class="bodyTable"> + <tr class="a"> + <td> + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" style="border-width: 0" src="http://creativecommons.org/images/public/somerights20.png"></img> + </a> + </td> + <td> + <p>Copyright � 2000-2006, QOS.ch</p> + + <p> + + This work is licensed under a + <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a>. + + </p> + </td> + </tr> + </table> + + <p> + One of the design goals of logback is to audit and debug complex distributed applications. + Most real-world distributed systems need to deal with multiple clients simultaneously. + In a typical multithreaded implementation of such a system, different threads will handle + different clients. A possible but discouraged approach to differentiate the logging output of + one client from another consists of instantiating a new and separate logger for each client. + This technique promotes the proliferation of loggers and considerably increases + their management overhead. + </p> + <div class="highlight"> + <p> + In order to run the examples in this chapter, you need + to make sure that certain jar files are present on the + classpath. + Please refer to the <a href="../setup.html">setup page</a> + for further details. + </p> + </div> + + <p> + A lighter technique consists of uniquely stamping each + log request servicing a given client. Neil Harrison described this method in the book + <em>Patterns for Logging Diagnostic Messages</em> in + Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, + and F. Buschmann (Addison-Wesley, 1997). Logback offers a variant of this technique: + Mapped Diagnostic Contexts (MDC). + </p> + + <p> + To uniquely stamp each request, the user puts contextual information into the + <code><a href="../xref/ch/qos/logback/classic/MDC.html">MDC</a></code>, + the abbreviation of Mapped Diagnostic Context. + The public interface of the MDC class is shown below. + </p> + +<div class="source"><pre>package ch.qos.logback.classic; + +public class MDC { + //Put a context value as identified by <em>key</em> + //into the current thread's context map. + <b>public static void put(String key, String val);</b> + + //Get the context identified by the <code>key</code> parameter. + <b>public static String get(String key);</b> + + //Remove the the context identified by the <code>key</code> parameter. + <b>public static void remove(String key);</b> + + //Clear all entries in the MDC. + <b>public static void clear();</b> + + //Returns the keys in the MDC as a Set. The returned value can be null. + <b>public static Set<String> getKeys();</b> +}</pre></div> + + <p> + The <code>MDC</code> class contains only static methods. + It lets the developer place information in a <em>diagnostic context</em> that can be + subsequently retrieved by certain logback components. The + <code>MDC</code> manages contextual information on a <em>per thread basis</em>. + Typically, while starting to service a new client request, the developer will + insert pertinent contextual information, such as the client id, client's IP + address, request parameters etc. into the <code>MDC</code>. Logback components, + if appropriately configured, will automatically include this information + in each log entry. + </p> + + <p> + The next application named + <code><a href="../xref/chapter7/SimpleMDC.html">SimpleMDC</a></code> + demonstrates this basic principle. + </p> +<em>Example 7.1: Basic MDC usage (<a href="../xref/chapter7/SimpleMDC.html"> +logback-examples/src/main/java/chapter7/SimpleMDC.java)</a></em> +<div class="source"><pre>package chapter7; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.core.ConsoleAppender; + +public class SimpleMDC { + static public void main(String[] args) throws Exception { + // You can put values in the MDC at any time. We first put the + // first name + <b>MDC.put("first", "Dorothy");</b> + + // Configure logback + PatternLayout layout = new PatternLayout(); + layout.setPattern("%X{first} %X{last} - %m%n"); + layout.start(); + ConsoleAppender<LoggingEvent> appender = new ConsoleAppender<LoggingEvent>(); + appender.setLayout(layout); + appender.start(); + Logger root = (Logger)LoggerFactory.getLogger("root"); + root.addAppender(appender); + + // get a logger + Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class); + + // We now put the last name + <b>MDC.put("last", "Parker");</b> + + // The most beautiful two words in the English language according + // to Dorothy Parker: + logger.info("Check enclosed."); + logger.debug("The most beautiful two words in English."); + + MDC.put("first", "Richard"); + MDC.put("last", "Nixon"); + logger.info("I am not a crook."); + logger.info("Attributed to the former US president. 17 Nov 1973."); + } +}</pre></div> + + <p> + The main method starts by associating the value <em>Dorothy</em> with + the key <em>first</em> in the <code>MDC</code>. You can place as many + value/key associations in the <code>MDC</code> as you wish. + Multiple insertions with the same key will overwrite older values. + The code then proceeds to configure logback. + Note the usage of the <em>%X</em> specifier within the + <code>PatternLayout</code> conversion pattern. The <em>%X</em> + conversion specifier is employed twice, once for the key <em>first</em> + and once for the key <em>last</em>. After configuring the root logger, + the code associates the value <em>Parker</em> with the key <em>last</em>. + It then invokes the logger twice with different messages. + The code finishes by setting the <code>MDC</code> to different values + and issuing several logging requests. Running SimpleMDC yields: + </p> + +<div class="source"><pre>Dorothy Parker - Check enclosed. +Dorothy Parker - The most beautiful two words in English. +Richard Nixon - I am not a crook. +Richard Nixon - Attributed to the former US president. 17 Nov 1973.</pre></div> + + + <p> + The <code>SimpleMDC</code> application illustrates how logback layouts, + if configured appropriately, automatically output <code>MDC</code> information. + Moreover, the information placed into the <code>MDC</code> can be used by + multiple logger invocations. + </p> + + <h3>Advanced Use</h3> + + <p> + Mapped Diagnostic Contexts shine brightest within client server architectures. + Typically, multiple clients will be served by multiple threads on the server. + Although the methods in the <code>MDC</code> class are static, + the diagnostic context is managed on a per thread basis, allowing each server + thread to bear a distinct <code>MDC</code> stamp. <code>MDC</code> operations + such as <code>put()</code> and <code>get()</code> affect the <code>MDC</code> + of the <em>current</em> thread only. The <code>MDC</code> in other threads remain + unaffected. Given that <code>MDC</code> information is managed on a + per thread basis, each thread will have its own copy of the <code>MDC</code>. + Thus, there is no need for the developer to worry about thread-safety or + synchronization when programming with the <code>MDC</code> because + it safely and transparently handles these issues. + </p> + + <p> + The next example is somewhat more advanced. + It shows how the <code>MDC</code> can be used in a client-server setting. + The server-side implements the <code>NumberCruncher</code> interface shown in + Example 7.2 below. <code>The NumberCruncher</code> interface contains a single + method named <code>factor()</code>. Using RMI technology, client invokes the + <code>factor()</code> method of the server application to retrieve the distinct + factors of an integer. + </p> + +<em>Example 7.2: The service interface (<a href="../xref/chapter7/NumberCruncher.html"> +logback-examples/src/main/java/chapter7/NumberCruncher.java)</a></em> +<div class="source"><pre>package chapter7; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * NumberCruncher factors positive integers. + */ +public interface NumberCruncher extends Remote { + /** + * Factor a positive integer <code>number</code> and return its + * <em>distinct</em> factor's as an integer array. + * */ + int[] factor(int number) throws RemoteException; +}</pre></div> + + <p> + The <code>NumberCruncherServer</code> application, listed in Example 7.3 below, + implements the <code>NumberCruncher</code> interface. Its main method exports + an RMI Registry on the local host that accepts requests on a well-known port. + </p> + +<em>Example 7.3: The server side (<a href="../xref/chapter7/NumberCruncherServer.html"> +logback-examples/src/main/java/chapter7/NumberCruncherServer.java)</a></em> +<div class="source"><pre>package chapter7; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.util.Vector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + + +/** + * A simple NumberCruncher implementation that logs its progress when + * factoring numbers. The purpose of the whole exercise is to show the + * use of mapped diagnostic contexts in order to distinguish the log + * output from different client requests. + * */ +public class NumberCruncherServer extends UnicastRemoteObject + implements NumberCruncher { + + private static final long serialVersionUID = 1L; + + static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class); + + public NumberCruncherServer() throws RemoteException { + } + + public int[] factor(int number) throws RemoteException { + // The client's host is an important source of information. + try { + <b>MDC.put("client", NumberCruncherServer.getClientHost());</b> + } catch (java.rmi.server.ServerNotActiveException e) { + logger.warn("Caught unexpected ServerNotActiveException.", e); + } + + // The information contained within the request is another source + // of distinctive information. It might reveal the users name, + // date of request, request ID etc. In servlet type environments, + // useful information is contained in the HttpRequest or in the + // HttpSession. + <b>MDC.put("number", String.valueOf(number));</b> + + logger.info("Beginning to factor."); + + if (number <= 0) { + throw new IllegalArgumentException(number + + " is not a positive integer."); + } else if (number == 1) { + return new int[] { 1 }; + } + + Vector<Integer> factors = new Vector<Integer>(); + int n = number; + + for (int i = 2; (i <= n) && ((i * i) <= number); i++) { + // It is bad practice to place log requests within tight loops. + // It is done here to show interleaved log output from + // different requests. + logger.debug("Trying " + i + " as a factor."); + + if ((n % i) == 0) { + logger.info("Found factor " + i); + factors.addElement(new Integer(i)); + + do { + n /= i; + } while ((n % i) == 0); + } + + // Placing artificial delays in tight-loops will also lead to + // sub-optimal resuts. :-) + delay(100); + } + + if (n != 1) { + logger.info("Found factor " + n); + factors.addElement(new Integer(n)); + } + + int len = factors.size(); + + int[] result = new int[len]; + + for (int i = 0; i < len; i++) { + result[i] = ((Integer) factors.elementAt(i)).intValue(); + } + + <b>// clean up + MDC.remove("client"); + MDC.remove("number");</b> + + return result; + } + + static void usage(String msg) { + System.err.println(msg); + System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" + + " where configFile is a logback configuration file."); + System.exit(1); + } + + public static void delay(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + } + } + + public static void main(String[] args) { + if (args.length != 1) { + usage("Wrong number of arguments."); + } + + String configFile = args[0]; + + if (configFile.endsWith(".xml")) { + try { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + lc.shutdownAndReset(); + configurator.doConfigure(args[0]); + } catch (JoranException je) { + je.printStackTrace(); + } + } + + NumberCruncherServer ncs; + + try { + ncs = new NumberCruncherServer(); + logger.info("Creating registry."); + + Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); + registry.rebind("Factor", ncs); + logger.info("NumberCruncherServer bound and ready."); + } catch (Exception e) { + logger.error("Could not bind NumberCruncherServer.", e); + + return; + } + } +}</pre></div> + + <p> + The implementation of the <code>factor(int number)</code> method is + of particular relevance. It starts by putting the client's hostname into the + <code>MDC</code> under the key <em>client</em>. The number to factor, + as requested by the client, is put into the <code>MDC</code> under the key + <em>number</em>. After computing the distinct factors of the integer + parameter, the result is returned to the client. Before returning the + result however, the values for the <em>client</em> and <em>number</em> are + cleared by calling the <code>MDC.remove()</code> method. Normally, + a <code>put()</code> operation should be balanced by the corresponding + <code>remove()</code> operation. Otherwise, the <code>MDC</code> will + contain stale values for certain keys. We would recommend that whenever + possible <code>remove()</code> operations be performed within finally blocks, + ensuring their invocation regardless of the execution path of the code. + </p> + + <p> + After these theoretical explanations, we are ready to run the number + cruncher example. Start the server with the following command: + </p> + +<div class="source"><pre>java chapter7.NumberCruncherServer src/main/java/chapter7/mdc1.xml</pre></div> + + <p> + The <em>mdc1.xml</em> configuration file is listed below: + </p> +<em>Example 7.4: Configuration file (logback-examples/src/main/java/chapter7/mdc1.xml)</em> +<div class="source"><pre><configuration> + + <appender name="CONSOLE" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="CONSOLE"/> + </root> +</configuration></pre></div> + + <p> + Note the use of the <em>%X</em> conversion specifier within the + <span class="option">Pattern</span> option. + </p> + + <p> + The following command starts an instance of <code>NumberCruncherClient</code> + application: + </p> + +<div class="source"><pre>java chapter7.NumberCruncherClient <em>hostname</em></pre></div> + + <p> + where <em>hostname</em> is the host where the + <code>NumberCruncherServer</code> is running + </p> + + <p> + Executing multiple instances of the client and requesting the server to factor + the numbers 129 from the first client and shortly thereafter + the number 71 from the second client, the server outputs the following: + </p> + +<div class="source"><pre> +<b>70984 [RMI TCP Connection(4)-192.168.1.6] INFO C:orion N:129 - Beginning to factor.</b> +70984 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 2 as a factor. +71093 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 3 as a factor. +71093 [RMI TCP Connection(4)-192.168.1.6] INFO C:orion N:129 - Found factor 3 +71187 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 4 as a factor. +71297 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 5 as a factor. +71390 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 6 as a factor. +<b>71453 [RMI TCP Connection(5)-192.168.1.6] INFO C:orion N:71 - Beginning to factor.</b> +71453 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 2 as a factor. +71484 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 7 as a factor. +71547 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 3 as a factor. +71593 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 8 as a factor. +71656 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 4 as a factor. +71687 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 9 as a factor. +71750 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 5 as a factor. +71797 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 10 as a factor. +71859 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 6 as a factor. +71890 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 11 as a factor. +71953 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 7 as a factor. +72000 [RMI TCP Connection(4)-192.168.1.6] INFO C:orion N:129 - Found factor 43 +72062 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 8 as a factor. +72156 [RMI TCP Connection(5)-192.168.1.6] INFO C:orion N:71 - Found factor 71</pre></div> + + <p> + The clients were run from a machine called <em>orion</em> as can be seen in + the above output. Even if the server processes the requests of clients + near-simultaneously in separate threads, the logging output pertaining + to each client request can be distinguished by studying the output of the + <code>MDC</code>. Note for example the stamp associated with <em>number</em>, + i.e. the number to factor. + </p> + + <p> + The attentive reader might have observed that the thread name could + also have been used to distinguish each request. The thread name can cause + confusion if the server side technology recycles threads. In that case, + it may be hard to determine the boundaries of each request, that is, + when a given thread finishes servicing a request and when it begins servicing the next. + Because the <code>MDC</code> is under the control of the application developer, + <code>MDC</code> stamps do not suffer from this problem. + </p> + + + + <h3>Automating access to the <code>MDC</code></h3> + + <p> + As we've seen, the <code>MDC</code> is very useful when dealing with multiple + clients. In the case of a web application that manages user authentication, one + simple solution could be to set the user's name in the <code>MDC</code> and remove + it once the user logs out. Unfortunately, it is not always possible to achieve + reliable results using this technique. Since <code>MDC</code> is managing + information on a <em>per thread</em> basis, a server that recycles + threads might lead to false information contained in the <code>MDC</code>. + </p> + + <p> + To allow the information contained in the <code>MDC</code> to be correct + at all times when a request is processed, a solution might be to store the + username at the beginning of the process, and remove it at the end of + said process. A servlet + <a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html"> + <code>Filter</code></a> is a good tool to have at + hand in such case. + </p> + + <p> + By using a servlet filter, one can access the request, try to access + to relevant user information and store it in the <code>MDC</code>. + Then, after the process of creating the response, one just needs + to remove the user information from the <code>MDC</code>. + </p> + + <p> + Here is an implementation of such a filter: + </p> + +<em>Example 7.5: User servlet filter (<a href="../xref/chapter7/UserServletFilter.html"> +logback-examples/src/main/java/chapter7/UserServletFilter.java)</a></em> +<div class="source"><pre>package chapter7; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import ch.qos.logback.classic.MDC; + +public class UserServletFilter implements Filter { + + boolean userRegistered = false; + + private final String userKey = "username"; + + public void destroy() { + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest req = (HttpServletRequest) request; + Principal principal = req.getUserPrincipal(); + + if (principal != null) { + String username = principal.getName(); + registerUsername(username); + } else { + HttpSession session = req.getSession(); + String username = (String)session.getAttribute(userKey); + registerUsername(username); + } + + try { + chain.doFilter(request, response); + } finally { + if (userRegistered) { + MDC.remove(userKey); + } + } + } + + public void init(FilterConfig arg0) throws ServletException { + } + + private void registerUsername(String username) { + if (username != null && username.trim().length() > 0) { + MDC.put(userKey, username); + userRegistered = true; + } + } +}</pre></div> + + + <p> + When the filter's <code>doFilter()</code> method is called, is first looks for a + <code>java.security.Principal</code> object in the request. This object contains + the name of the currently authenticated user. In case the user principal is not set, + the filter looks for a session attribute matching a given key (here <em>username</em>). + If a user information is found, it is registered in the <code>MDC</code>. + </p> + + <p> + Once the filter chain has completed, the filter removes the user information + from the <code>MDC</code>. + </p> + + <p> + With this filter, the user information is present in the <code>MDC</code> only + the time it takes to process the request. The thread may be reused to process a + request for another user without risking to display false information in the + logs. + </p> +<script src="../templates/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/news.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/news.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,283 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>News</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + <h2>Logback News</h2> + + <p>Here are the latest news about logback.</p> + + <h3>January 31st, 2007 - Release of version 0.9</h3> + + <p> + This version contains a new component, namely the <code>ContextSelector</code>, + that provides context separation and management when logback is used + by several web-apps running under the same server. A + <a href="manual/contextSelector.html">new chapter</a> was added to the logback manual to detail + the use of the <code>ContextSelector</code>, along with its associated components. + </p> + + <p> + The <code>JMXConfigurator</code> has been improved. It now shows the context's + Status objects, which lets users check the internal state of logback. + </p> + + <p> + The logback manual's chapter 2, about <a href="manual/architecture.html">logback's architecture</a>, + has been updated with two sections: Under the hood and Performance. + </p> + + <h3>January 23th, 2007 - Release of version 0.8.1</h3> + + <p> + This version contains new components in the Access module, allowing users + to display the full HttpServletRequest or HttpServletResponse of an access event. + </p> + + <p> + The documentation section has been updated. The short introduction was split + into the chapter 1 and chapter 2 of the logback manual. The chapters about + Appenders and Layouts have been updated to document new components of logback. + </p> + + <p> + A demonstration webApp presenting logback's major components is available. + A document explains how to run it, and provides a step-by-step visit of the + demo. + </p> + + <p> + A first translation of logback jars to JDK1.4 is present in this release. + </p> + + + <h3>January 12th, 2007 - Release of version 0.8</h3> + + <p> + This version contains a whole new chapter, namely Chapter 3, about logback + configuration. Several other documentation pages have been improved. + </p> + + <p> + Logback now uses Generics in many components. + </p> + + <p> + Several new components have been added to logback. A JMX Configurator now allows + users to see and modify loggers or reload configuration among other possibilities. + A <a href="jmxConfig.html">document</a> + about this configurator is available in the <a href="documentation.html">corresponding section</a> + of the site. We'd like to thank Sebastian Davids for his ideas and contributions to this + component. + </p> + + <p> + A JMSTopicAppender and JMSQueueAppender are now available, as well as two new filters: LevelFilter + and ThresholdFilter. A refactoring was done in the filters objects to ease the implementation + of custom filters. + </p> + + + <h3>December 19th, 2006 - Release of version 0.7.1</h3> + + <p> + Version 0.7.1 of logback has been released. + </p> + + <p> + This version contains more detailled information about logback access module, and + its JMX components. A <a href="access.html">dedicated page</a> explains how to configure and use logback + access in Tomcat and Jetty, and access some of its components via JMX. + </p> + + <h3>December 18th, 2006 - Release of version 0.7</h3> + + <p> + Version 0.7 of logback has been released. + </p> + + <p> + Logback now ships with a new module: <em>log4j-bridge</em>. This new module + can be used to intercept log4j calls and redirects them to logback components. + More information about this module can be found in the corresponding + <a href="bridge.html">documentation page</a>. + </p> + + <p> + The documentation has been vastly updated. Two new chapters, + namely Filters and MDC, are available in the manual section. + </p> + + <h3>November 30th, 2006 - Release of version 0.6</h3> + + <p> + Version 0.6 of logback has been released. + </p> + + <p> + Logback classic now supports automatic configuration, allowing test and production + environment configuration. <code>TurboFilters</code> make their first appearance + in a logback release. They provide ultra-fast filtering possibilities. + The logging context now supports listeners which will be contacted each + time the context is reset or started. <code>SMTPAppender</code> + allows for much more flexible configuration than before. + </p> + + <p> + In logback access, new Appenders are available, namely <code>SocketAppender</code> + and <code>DBAppender</code>. + Logback access now supports filtering and event evaluations. A <code>CountingFilter</code> + has been added. It provides statistical views of server access, reachable + via JMX. + </p> + + <p> + The documentation has also been improved. A complete new chapter + has been added about Appenders, the short introduction to logback + classic has been updated and a new module, containing many configuration + examples has been added. + </p> + + <p> + Logback now uses continous integration in its development. + </p> + + <p> + Tests have been improved, many new have been added. + This release also provides some bug fixes. + </p> + + <hr width="80%" align="center"></hr> + + <h3>October 26th, 2006 - Release of version 0.5</h3> + + <p> + Version 0.5 of logback has been released. + </p> + + <p> + This release offers a important improvements in Joran. In + particular, Joran can now replay configuration elements. + </p> + + <p> + As in the previous release, a major area of work is the + documentation which is being continously improved. + </p> + + <hr width="80%" align="center"></hr> + + <h3>October 9th, 2006 - Release of version 0.4</h3> + <p> + Version 0.3 of logback has been released. + </p> + + <p> + This release includes an improved access module, with specific + implementations for the Jetty and Tomcat servers. Documentation + was also added to show how to integrate logback-access with + Jetty. + </p> + + <p> + As for the classic module, several appenders and layouts have + been added or improved. The error reporting of logback has also + been enhanced, presenting the user with a link to an online page + explaining possible reasons for the error. + </p> + + <p> + A joran documentation was added, with examples in the core + module. + </p> + + <hr width="80%" align="center"></hr> + + <h3>September 8th, 2006 - Release of version 0.3</h3> + <p> + Version 0.3 of logback has been released. + </p> + + <p> + This release offers several new Appenders, support for Mapped + Diagnostic Context, improved tests and documentation<br></br> + </p> + + <p> + In response to a bug report by Rickard Nilsson on the logback + mailing list, a bug affecting parametrized logging was fixed. + </p> + + <p> + We also released a <a href="http://logback.qos.ch/translator/">PropertiesTranslator</a> + webapp that converts <em>log4j.properties</em> files to joran + configuration files (in XML format).<br></br> + </p> + + <hr width="80%" align="center"></hr> + + <h3>August 23th, 2006 - Release of version 0.2.5</h3> + + <p> + Version 0.2.5 of logback has been released. + </p> + + <p> + This release offers better documentation. With a number of + correction mande in the short introduction to logback-classic. + </p> + + <hr width="80%" align="center"></hr> + + + <h3>August 15th, 2006 - Release of version 0.2</h3> + <p> + Version 0.2 of logback has been released. + </p> + + <p> + It offers better tests, a few more functionalities, and enhanced + documentation. We also improved the site design to make it + simpler and more efficient. + </p> + + <hr width="80%" align="center"></hr> + + <h3>July 26th, 2006 - Release of version 0.1</h3> + + <p> + Version 0.1 of logback has been released. + </p> + + <hr width="80%" align="center"></hr> + + <h3>February 9th, 2006 - Logback web-site goes live</h3> + <p> + The logback web-site goes live on the 9th of February. At + its present state, it is pretty primitive but updates will + follow. + </p> + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/repos.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/repos.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,75 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Repository</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + <h2>Source Repository</h2> + + <p> + We keep the source code in revision control systems called + Subversion. Developers have write access to the Subversion + repository, enabling them to make changes to the source + code. Everyone has read access to the repositories, so you may + download the most up-to-date development version of the + software. Note that the latest version in the Subversion + repository many not work as expected, it may not even compile + properly. If you are looking for a stable release of the source + code, you should download an official distribution instead of + the latest version in the Subversion repositories. + + There are several ways to access the Subversion + repositories: + </p> + + <div class="section"> + <h2>Web Access</h2> + </div> + <p> + If you just wish to browse around or download a few individual + files, you can do so with web-based ViewVC interface: + </p> + + <p> + <a href="http://svn.qos.ch/viewvc/logback/trunk/">http://svn.qos.ch/viewvc/logback/trunk/</a> + </p> + + <div class="section"> + <h2>Checking out a read-only copy</h2> + </div> + <p> + To access the Subversion repositories anonymously, you will need + a Subversion client. You can check out the entire logback + project with the following command:</p> + + <div class="source">svn co http://svn.qos.ch/repos/logback/trunk/ target_directory</div> + + <div class="section"> + <p> + Note that anonymous access allows read-only access only. For + read-write access please contact the logback <a href="mailinglist.html">developer list</a> . + </p> + </div> + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/setup.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/setup.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,77 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Setup</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + <h2>Classpath Setup</h2> + +<p> +In order to run the examples provided in the documentation, +you need to add the following jars to your class path: +</p> + +<ul> + <p>logback-core-1.0-SNAPSHOT.jar</p> + <p>logback-classic-1.0-SNAPSHOT.jar</p> + <p>logback-examples-1.0-SNAPSHOT.jar</p> + <p>slf4j-api-1.2.jar</p> +</ul> + +<h3>Example</h3> + +<p> +Assuming your current directory is +<em>$LOGBACK_HOME/logback-examples</em>, where <em>$LOGBACK_HOME</em> stands +for the directory where you installed logback, you can launch the first +sample application, <em>chapter1.HelloWord1</em> with the following command: +</p> + +<div class="source"><pre>java -cp + lib/slf4j-api-1.2.jar;../logback-core-1.0-SNAPSHOT.jar;\ + ../logback-classic-1.0-SNAPSHOT.jar;logback-examples-1.0-SNAPSHOT.jar\ + chapter1.HelloWorld1</pre></div> + +<p> +It is more convenient to set the CLASSPATH environment variable +once and for all before running the examples. +</p> +<p>The <em>setClasspath.cmd</em> script located in the $LOGBACK_HOME/logback-examples +folder will configure the class path for the MS Windows platform. For Unix, you can +use <em>setClasspath.sh</em>. +</p> + +<p>Please edit the script in order to adapt the <em>LB_HOME</em> variable +to match your local environment.</p> + +<p> +Please be aware that many examples will launch java classes along +with configuration files. To access these files by using +the same commands as written in the documentation, you will need to +issue the commands from within the <em>$LOGBACK_HOME/logback-examples</em> +directory. +</p> + + + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/team.html ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/team.html Thu Feb 1 15:44:05 2007 @@ -0,0 +1,85 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> +<title>Logback Team</title> +<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" /> +</head> +<body> +<script src="templates/base/header.js"></script> +<div id="left"> + <script src="templates/base/left.js"></script> +</div> +<div id="right"> + <script src="templates/base/right.js"></script> +</div> +<div id="content"> + + + + <h2>Logback Team</h2> + + <p>Let us introduce you to the logback team members:</p> + + <table class="bodyTable"> + <tr class="a"> + <td><img src="images/jeannoel.gif" alt="Jean-Noel"></img></td> + <td> + Jean-Noel Charpin has been designing and developing object oriented software + and decision making systems since 1998. He is particularly interested in + translating lean manufacturing principles and practices to the software development + domain in order to improve quality and reliability. + </td> + </tr> + <tr class="b"> + <td><img src="images/ceki.gif" alt="Ceki"></img></td> + <td> + <p>Ceki G�lc� has been working on logging systems + since 1996. He is the founder of the log4j, slf4j and + logback projects. Ceki enjoys writing software, a task far + more difficult and time-consuming than what it seems at + first sight. His interests range from cryptography, systems + monitoring and testing to application interoperability. + </p> + + <p>One of the core lessons he has learned over the years is + that unit tests lie at the heart of maintainable + software. Adding tests towards the end of development cycle + yields poor results. Tests must be an inherent part of the + development process right from the start. + </p> + + <p>When not busy programming, Ceki enjoys watching the + Simpsons on DVD or alternatively preparing sushis for his + friends. + </p> + + </td> + </tr> + <tr class="a"> + <td><img src="images/seb.gif" alt="Sebastien"></img></td> + <td> + <p>S�bastien Pennec has been using computers for the most + part of his life, be it as a hobby, or during his studies + or, more recently, as Software Engineer. He loves to write + software in dynamic and enthusiastic environments, while + learning new technologies and practices. + </p> + <p> + S�bastien's hobbys include writing articles about + Macintosh products on <a href="http://www.cuk.ch">cuk.ch</a>, + digital photography and playing poker with his friends. + </p> + </td> + </tr> + </table> + + + + + + +<script src="templates/base/footer.js"></script> +</div> +</body> +</html> Added: logback/trunk/logback-site/src/site/resources/templates/base/footer.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/base/footer.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,4 @@ + +document.write('<p class="footer">') +document.write('<a href="http://www.qos.ch/">QOS.ch</a>') +document.write('</p>') \ No newline at end of file Added: logback/trunk/logback-site/src/site/resources/templates/base/header.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/base/header.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,7 @@ + +document.write('<p align="left">'); +document.write('<a href="http://logback.qos.ch.ch/">'); +document.write('<img src="images/logos/lblogo.jpg" alt="" border="0"/>'); +document.write('</a>') +document.write('</p>'); +document.write('<div id="breadcrumbs"></div>'); Added: logback/trunk/logback-site/src/site/resources/templates/base/left.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/base/left.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,18 @@ +writeMenu(''); +function writeMenu(prefix) { + document.write('<p class="menu_header">Logback</p>'); + document.write('<p><a href="index.html">Introduction</a>'); + document.write('<a href="' + prefix + 'news.html">News</a>'); + document.write('<a href="' + prefix + 'download.html">Download</a>'); + document.write('<a href="' + prefix + 'documentation.html">Documentation</a>'); + document.write('<a href="' + prefix + 'mailinglist.html">Mailing Lists</a>'); + document.write('<a href="' + prefix + 'repos.html">Source Repository</a>'); + document.write('<a href="' + prefix + 'bugreport.html">Bug Report</a>'); + document.write('<a href="' + prefix + 'license.html">License</a>'); + document.write('<a href="http://logback.qos.ch/translator/">Log4j Properties Translator</a>'); + document.write('<a href="' + prefix + 'team.html">Logback Team</a>'); + document.write('<a href="http://www.qos.ch/"><img src="' + prefix + 'images/logos/qosLogo.png" /></a>'); + document.write('</p>'); +} + + Added: logback/trunk/logback-site/src/site/resources/templates/base/right.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/base/right.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,8 @@ + +/*document.write('<p class="menu_header">Menu</p>') +document.write('<p><a href=".">anc</a>') +document.write('<a href=".">abc</a>') +document.write('<a href=".">log4j configration ttnaslator</a>') +document.write('<a href=".">xxxxxxxxxxx</a>') +document.write('</p>') +*/ \ No newline at end of file Added: logback/trunk/logback-site/src/site/resources/templates/footer.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/footer.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,4 @@ + +document.write('<p class="footer">') +document.write('<a href="http://www.qos.ch/">QOS.ch</a>') +document.write('</p>') \ No newline at end of file Added: logback/trunk/logback-site/src/site/resources/templates/header.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/header.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,7 @@ + +document.write('<p align="left">'); +document.write('<a href="http://logback.qos.ch.ch/">'); +document.write('<img src="../images/logos/lblogo.jpg" alt="" border="0"/>'); +document.write('</a>') +document.write('</p>'); +document.write('<div id="breadcrumbs"></div>'); Added: logback/trunk/logback-site/src/site/resources/templates/left.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/left.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,18 @@ +writeMenu('../'); +function writeMenu(prefix) { + document.write('<p class="menu_header">Logback</p>'); + document.write('<p><a href="index.html">Introduction</a>'); + document.write('<a href="' + prefix + 'news.html">News</a>'); + document.write('<a href="' + prefix + 'download.html">Download</a>'); + document.write('<a href="' + prefix + 'documentation.html">Documentation</a>'); + document.write('<a href="' + prefix + 'mailinglist.html">Mailing Lists</a>'); + document.write('<a href="' + prefix + 'repos.html">Source Repository</a>'); + document.write('<a href="' + prefix + 'bugreport.html">Bug Report</a>'); + document.write('<a href="' + prefix + 'license.html">License</a>'); + document.write('<a href="http://logback.qos.ch/translator/">Log4j Properties Translator</a>'); + document.write('<a href="' + prefix + 'team.html">Logback Team</a>'); + document.write('<a href="http://www.qos.ch/"><img src="../images/logos/qosLogo.png" /></a>'); + document.write('</p>'); +} + + Added: logback/trunk/logback-site/src/site/resources/templates/right.js ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/resources/templates/right.js Thu Feb 1 15:44:05 2007 @@ -0,0 +1,7 @@ + +/*document.write('<p class="menu_header"></p>') +document.write('<p><a href="http//www.slf4j.org">Sister Project: SLF4J</a>') +document.write('<a href="."></a>') +document.write('<a href=".">log4j configration ttnaslator</a>') +document.write('<a href=".">xxxxxxxxxxx</a>') +document.write('</p>')*/ Modified: logback/trunk/logback-site/src/site/site.xml ============================================================================== --- logback/trunk/logback-site/src/site/site.xml (original) +++ logback/trunk/logback-site/src/site/site.xml Thu Feb 1 15:44:05 2007 @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="ISO-8859-1"?> <project name="Logback Project"> - <skin> + <!-- <skin> <groupId>ch.qos.logback</groupId> <artifactId>logback-skin</artifactId> <version>0.9.1-SNAPSHOT</version> - </skin> + </skin>--> <publishDate position="navigation-bottom" format="dd-MM-yyyy"/> Modified: logback/trunk/pom.xml ============================================================================== --- logback/trunk/pom.xml (original) +++ logback/trunk/pom.xml Thu Feb 1 15:44:05 2007 @@ -22,7 +22,7 @@ <module>logback-classic</module> <module>logback-access</module> <module>logback-site</module> - <module>logback-skin</module> + <!-- <module>logback-skin</module> --> <module>logback-examples</module> <module>log4j-bridge</module> </modules> Modified: logback/trunk/src/site/site.xml ============================================================================== --- logback/trunk/src/site/site.xml (original) +++ logback/trunk/src/site/site.xml Thu Feb 1 15:44:05 2007 @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="ISO-8859-1"?> <project name="LOGBack Main Site"> - <skin> + <!--<skin> <groupId>ch.qos.logback</groupId> <artifactId>logback-skin</artifactId> <version>0.9.1-SNAPSHOT</version> - </skin> + </skin> --> <!-- <publishDate position="navigation-bottom" format="dd-MM-yyyy"/>