
Author: seb Date: Fri Jan 5 14:30:28 2007 New Revision: 1162 Modified: logback/trunk/logback-site/src/site/xdocTemplates/manual/joran.xml Log: Generic documentation about Joran is now at the end of the document Modified: logback/trunk/logback-site/src/site/xdocTemplates/manual/joran.xml ============================================================================== --- logback/trunk/logback-site/src/site/xdocTemplates/manual/joran.xml (original) +++ logback/trunk/logback-site/src/site/xdocTemplates/manual/joran.xml Fri Jan 5 14:30:28 2007 @@ -60,619 +60,594 @@ sweet water reserve. </p> -<h2>Introduction</h2> +<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> - This document begins with a generic explanation of how the configuration - framework in logback works. Then, a second part explains how to use it - within logback to configure precisely a logging strategy. +Logback can be configured both programmatically and thanks to an xml configuration +file. Here are the steps that logback follows to try to configure itself: </p> -<p>For it's configuration, logback relies on Joran, a -mature, flexible and powerful configuration framework. Many of the -capabilities offered by logback modules are possible thanks to Joran. +<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 thanks to 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>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> +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>The examples for this chapter can be found under -<em>LOGBACK_HOME/logback-examples/src/main/java/chapter3</em>. +<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> -<p>To install joran, simply <a href="download.html">download</a> -logback and add <em>logback-core-VERSION.jar</em> to your classpath.</p> +<em>Example 3.5: 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; -<h2>Historical perspective</h2> +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -<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> +public class MyApp1 { + final static Logger logger = LoggerFactory.getLogger(MyApp1.class); -<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. + 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>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> +The output of the command <em>java chapter3.MyApp1</em> should be similar to: </p> -<h2>SAX or DOM?</h2> +<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>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> +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>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> +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>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> +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> -<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> +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.6: 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; -<div class="source"><pre>package ch.qos.logback.core.joran.action; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.spi.ExecutionContext; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; -public abstract class Action { - +public class MyApp2 { + final static Logger logger = LoggerFactory.getLogger(MyApp2.class); - /** - * Called when the parser first encounters an element. - */ - public abstract void begin(ExecutionContext ec, - String name, - Attributes attributes); + 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(); + } - /** - * 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); + logger.info("Entering application."); + + Foo foo = new Foo(); + foo.doIt(); + logger.info("Exiting application."); + } }</pre></div> -<p>Thus, every action must implement the begin and end methods.</p> +<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.7: Basic configuration file (logback-examples/src/main/java/chapter3/sample0.xml)</em> +<div class="source"><pre><configuration> -<h2>Execution context</h2> + <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> -<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> + <root> + <level value="debug" /> + <appender-ref ref="STDOUT" /> + </root> +</configuration></pre></div> -<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> +Assuming the current directory is <em>logback-examples</em>, try running the +<code>MyApp2</code> class by issuing the following command: </p> -<a name="helloWorld" /> -<h3>A hello world example</h3> +<div class="source"><pre>java chapter3.MyApp2 src/main/java/chapter3/sample0.xml</pre></div> -<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: +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> -<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> +<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'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. +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.8: 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> -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. +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> -<a name="calculator" /> -<h3>Collaborating actions</h3> +<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> -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. +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> -The <em>calculator1.xml</em> file contains a <code>computation</code> element, -with a nested <code>literal</code> element. +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> -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: +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> -<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. +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>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.1: 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. +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> -<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.8: Setting the level of a logger (logback-examples/src/main/java/chapter3/sample2.xml)</em> +<div class="source"><pre><configuration> -<em>Example 3.2: 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> + <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> -<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> + <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> -<a name="newRule" /> -<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> +This new configuration will yield the following output, when used with the +same <code>chapter3.MyApp2</code> class. </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> -<source>ruleStore.addRule(new Pattern("/computation/new-rule"), new NewRuleAction());</source> +<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>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> +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> -<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> +<em>Example 3.8: Setting the level of multiple loggers (logback-examples/src/main/java/chapter3/sample3.xml)</em> +<div class="source"><pre><configuration> -<p>Here is how new rules can be declared in an xml file:</p> + <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> -<div class="source"><pre><new-rule pattern="*/computation/literal" actionClass="chapter3.calculator.LiteralAction"/></pre></div> + <b><logger name="chapter3"> + <level value="INFO" /> + </logger> -<p>Using new rule declarations, the preceding example, involving the calculation, could be -expressed this way:</p> + <logger name="chapter3.Foo"> + <level value="DEBUG" /> + </logger></b> -<em>Example 3.3: 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"/> + <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> - <computation> - <literal value="7"/> - <literal value="3"/> - <add/> - </computation> - - <literal value="3"/> - <multiply/> -</computation></pre></div> +</configuration></pre></div> -<a name="implicit" /> -<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> +Running <code>MyApp2</code> with this configuration file will result in the +following output on the console: </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> +<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>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> +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> -<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> +<table> + <tr> + <th>Logger name</th> + <th>Assigned Level</th> + <th>Effective Level</th> + </tr> + <tr> + <td>root</td> + <td><code>DEBUG</code></td> + <td><code>DEBUG</code></td> + </tr> + <tr> + <td>chapter3</td> + <td><code>INFO</code></td> + <td><code>INFO</code></td> + </tr> + <tr> + <td>chapter3.MyApp2</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> + <tr> + <td>chapter3.Foo</td> + <td><code>DEBUG</code></td> + <td><code>DEBUG</code></td> + </tr> +</table> -<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> +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.9: Logger level sample (logback-examples/src/main/java/chapter3/sample4.xml)</em> +<div class="source"><pre><configuration> -<p>Both <code>ImplicitAction</code> and <code>NestedComponentIA</code> are located in the -<code>ch.qos.logback.core.joran.action</code> package. -</p> + <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> -<p>Refer to the <em>logback-examples/src/main/java/joran/implicit</em> -directory for an example of an implicit action. -</p> + <b><logger name="chapter3"> + <level value="INFO" /> + </logger></b> -<p>In that directory, you will find two actions classes, one xml file and one -class containing the setup of Joran.</p> + <root> + <b><level value="OFF" /></b> + <appender-ref ref="STDOUT" /> + </root> -<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> +</configuration></pre></div> -<source>ruleStore.addRule(new Pattern("*/foo"), new NOPAction());</source> +<p> +The following table lists the loggers and their level setting after applying the +<em>sample4.xml</em> configuration file. +</p> -<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> +<table> + <tr> + <th>Logger name</th> + <th>Assigned Level</th> + <th>Effective Level</th> + </tr> + <tr> + <td>root</td> + <td><code>OFF</code></td> + <td><code>OFF</code></td> + </tr> + <tr> + <td>chapter3</td> + <td><code>INFO</code></td> + <td><code>INFO</code></td> + </tr> + <tr> + <td>chapter3.MyApp2</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> + <tr> + <td>chapter3.Foo</td> + <td><code>null</code></td> + <td><code>INFO</code></td> + </tr> +</table> -<source>ji.addImplicitAction(new PrintMeImplicitAction());</source> +<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> -<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> +<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>The <em>implicit1.xml</em> file contains the following lines:</p> +<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> -<em>Example 3.4: Usage of implicit rules (logback-examples/src/main/java/chapter3/implicit/implicit1.xml)</em> -<div class="source"><pre><foo> +<h4>Configuring Appenders</h4> - <xyz printme="true"> - <abc printme="true"/> - </xyz> +<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> - <xyz/> +<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> - <foo printme="true"/> +<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> -</foo></pre></div> +<em>Example 3.10: Multiple loggers (logback-examples/src/main/java/chapter3/multiple.xml)</em> +<div class="source"><pre><configuration> -<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> + <appender name="<b>FILE</b>" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>myApp.log</file> -<p>The second element will not be printed, because no <em>printme</em> attibute is present.</p> + <rollingPolicy + class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <FileNamePattern> + myApp-%d{yyyy-MM-dd-HH-mm-ss}.log + </FileNamePattern> + </rollingPolicy> -<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> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %date %level [%thread] %logger{10} [%file : %line] %msg%n + </Pattern> + </layout> + </appender> -<p>Running the example yields the following output:</p> + <appender name="<b>STDOUT</b>" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %msg%n + </Pattern> + </layout> + </appender> -<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> - -<h2>Configuration in logback</h2> - -<p> -Logback can be configured both programmatically and thanks to 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 thanks to 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.5: 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> + <root> + <level value="debug" /> + <b><appender-ref ref="FILE" /> + <appender-ref ref="STDOUT" /></b> + </root> +</configuration></pre></div> <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>. +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> -<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>. +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> -<em>Example 3.6: 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: +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.7: Basic configuration file (logback-examples/src/main/java/chapter3/sample0.xml)</em> +<em>Example 3.11: 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> + <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" /> @@ -680,39 +655,62 @@ </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: +Running <code>MyApp2</code> with <em>duplicate.xml</em> will yield the following output: </p> -<div class="source"><pre>java chapter3.MyApp2 src/main/java/chapter3/sample0.xml</pre></div> +<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> -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. +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> -<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: +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.8: Basic configuration file using debug mode (logback-examples/src/main/java/chapter3/sample1.xml)</em> -<div class="source"><pre><configuration debug="true"> +<em>Example 3.11: 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>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> + <Pattern>%msg%n</Pattern> </layout> </appender> + <logger name="chapter3"> + <appender-ref ref="FILE" /> + </logger> + <root> <level value="debug" /> <appender-ref ref="STDOUT" /> @@ -720,316 +718,169 @@ </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. +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> -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. +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.8: Setting the level of a logger (logback-examples/src/main/java/chapter3/sample2.xml)</em> +<em>Example 3.12: 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>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> + <Pattern>%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" /> + <logger name="chapter3.Foo" <b>additivity="false"</b>> + <appender-ref ref="FILE" /> + </logger> + + <root> + <level value="debug" /> <appender-ref ref="STDOUT" /> - </root> - + </root> </configuration></pre></div> <p> -This new configuration will yield the following output, when used with the -same <code>chapter3.MyApp2</code> class. +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> -<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> +<h4>Variable substitution</h4> <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>. +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>${java.home}</em> will be +interpreted as <em>/home/xyz</em>. </p> -<em>Example 3.8: Setting the level of multiple loggers (logback-examples/src/main/java/chapter3/sample3.xml)</em> +<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> - <appender name="STDOUT" - class="ch.qos.logback.core.ConsoleAppender"> + <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> - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - </Pattern> + %date %level [%thread] %logger{10} [%file : %line] %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" /> + <level value="debug" /> + <appender-ref ref="FILE" /> </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. +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> -<table> - <tr> - <th>Logger name</th> - <th>Assigned Level</th> - <th>Effective Level</th> - </tr> - <tr> - <td>root</td> - <td><code>DEBUG</code></td> - <td><code>DEBUG</code></td> - </tr> - <tr> - <td>chapter3</td> - <td><code>INFO</code></td> - <td><code>INFO</code></td> - </tr> - <tr> - <td>chapter3.MyApp2</td> - <td><code>null</code></td> - <td><code>INFO</code></td> - </tr> - <tr> - <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.9: Logger level sample (logback-examples/src/main/java/chapter3/sample4.xml)</em> +<em>Example 3.13: System Variable substitution (logback-examples/src/main/java/chapter3/variableSubstitution2.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 + <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> - <b><logger name="chapter3"> - <level value="INFO" /> - </logger></b> - <root> - <b><level value="OFF" /></b> - <appender-ref ref="STDOUT" /> + <level value="debug" /> + <appender-ref ref="FILE" /> </root> - </configuration></pre></div> <p> -The following table lists the loggers and their level setting after applying the -<em>sample4.xml</em> configuration file. +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> -<table> - <tr> - <th>Logger name</th> - <th>Assigned Level</th> - <th>Effective Level</th> - </tr> - <tr> - <td>root</td> - <td><code>OFF</code></td> - <td><code>OFF</code></td> - </tr> - <tr> - <td>chapter3</td> - <td><code>INFO</code></td> - <td><code>INFO</code></td> - </tr> - <tr> - <td>chapter3.MyApp2</td> - <td><code>null</code></td> - <td><code>INFO</code></td> - </tr> - <tr> - <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.10: Multiple loggers (logback-examples/src/main/java/chapter3/multiple.xml)</em> +<em>Example 3.14: Variable substitution using a separate file (logback-examples/src/main/java/chapter3/variableSubstitution3.xml)</em> <div class="source"><pre><configuration> - <appender name="<b>FILE</b>" - class="ch.qos.logback.core.rolling.RollingFileAppender"> - <file>myApp.log</file> + <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-mm-ss}.log + 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 @@ -1037,106 +888,54 @@ </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> + <appender-ref ref="FILE" /> </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. +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> -<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> +<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> -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. +Nothing more is needed to declare the variable. </p> -<em>Example 3.11: 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: +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> -<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> +<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 +file=myApp.log +destination=${user.home.dir}/${file}</pre></div> <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. +In the configuration file, only the last variable, <em>${destination}</em> will +be used, as shown below: </p> -<em>Example 3.11: Multiple appender (logback-examples/src/main/java/chapter3/restricted.xml)</em> +<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"> - <file>myApp.log</file> + <b><file>${destination}</file></b> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern> - myApp-%d{yyyy-MM-dd-HH-mm-ss}.log + myApp-%d{yyyy-MM-dd-HH}.log </FileNamePattern> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> @@ -1146,260 +945,461 @@ </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" /> + <appender-ref ref="FILE" /> </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. +<a name="Joran" /> +<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 thanks to Joran. </p> -<em>Example 3.12: Additivity flag (logback-examples/src/main/java/chapter3/additivityFlag.xml)</em> -<div class="source"><pre><configuration> +<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> - <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> +<p>The examples for this chapter can be found under +<em>LOGBACK_HOME/logback-examples/src/main/java/chapter3</em>. +</p> - <appender name="STDOUT" - class="ch.qos.logback.core.ConsoleAppender"> - <layout class="ch.qos.logback.classic.PatternLayout"> - <Pattern>%msg%n</Pattern> - </layout> - </appender> +<p>To install joran, simply <a href="download.html">download</a> +logback and add <em>logback-core-VERSION.jar</em> to your classpath.</p> - <logger name="chapter3.Foo" <b>additivity="false"</b>> - <appender-ref ref="FILE" /> - </logger> +<h2>Historical perspective</h2> - <root> - <level value="debug" /> - <appender-ref ref="STDOUT" /> - </root> -</configuration></pre></div> +<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> -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>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> -<h4>Variable substitution</h4> +<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" /> +<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> -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>${java.home}</em> will be -interpreted as <em>/home/xyz</em>. +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" /> +<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.1: 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.2: 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" /> +<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> -<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> +<source>ruleStore.addRule(new Pattern("/computation/new-rule"), new NewRuleAction());</source> -<em>Example 3.12: Simple Variable substitution (logback-examples/src/main/java/chapter3/variableSubstitution1.xml)</em> -<div class="source"><pre><configuration> +<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> - <b><substitutionProperty name="user.home.dir" value="/Users/seb" /></b> +<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> - <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> +<p>Here is how new rules can be declared in an xml file:</p> - <root> - <level value="debug" /> - <appender-ref ref="FILE" /> - </root> -</configuration></pre></div> +<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> -<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. +<em>Example 3.3: 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" /> +<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> -<em>Example 3.13: System Variable substitution (logback-examples/src/main/java/chapter3/variableSubstitution2.xml)</em> -<div class="source"><pre><configuration> +<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> - <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> +<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> - <root> - <level value="debug" /> - <appender-ref ref="FILE" /> - </root> -</configuration></pre></div> +<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> -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>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> -<em>Example 3.14: Variable substitution using a separate file (logback-examples/src/main/java/chapter3/variableSubstitution3.xml)</em> -<div class="source"><pre><configuration> +<p>Both <code>ImplicitAction</code> and <code>NestedComponentIA</code> are located in the +<code>ch.qos.logback.core.joran.action</code> package. +</p> - <substitutionProperty file="variables1.properties" /> +<p>Refer to the <em>logback-examples/src/main/java/joran/implicit</em> +directory for an example of an implicit action. +</p> - <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> +<p>In that directory, you will find two actions classes, one xml file and one +class containing the setup of Joran.</p> - <root> - <level value="debug" /> - <appender-ref ref="FILE" /> - </root> -</configuration></pre></div> +<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> -<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> +<source>ruleStore.addRule(new Pattern("*/foo"), new NOPAction());</source> -<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>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> -<p> -Nothing more is needed to declare the variable. -</p> +<source>ji.addImplicitAction(new PrintMeImplicitAction());</source> -<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> +<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.16: Recursive use of variables (logback-examples/src/main/java/chapter3/variables2.properties)</em> -<div class="source"><pre>user.home.dir=/Users/seb -file=myApp.log -destination=${user.home.dir}/${file}</pre></div> +<em>Example 3.4: Usage of implicit rules (logback-examples/src/main/java/chapter3/implicit/implicit1.xml)</em> +<div class="source"><pre><foo> -<p> -In the configuration file, only the last variable, <em>${destination}</em> will -be used, as shown below: -</p> + <xyz printme="true"> + <abc printme="true"/> + </xyz> -<em>Example 3.17: Variable substitution using a separate file (logback-examples/src/main/java/chapter3/variableSubstitution4.xml)</em> -<div class="source"><pre><configuration> + <xyz/> - <substitutionProperty file="variables1.properties" /> + <foo printme="true"/> - <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> +</foo></pre></div> - <root> - <level value="debug" /> - <appender-ref ref="FILE" /> - </root> -</configuration></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>