[logback-dev] svn commit: r2096 - in logback/trunk: logback-access/src/main/java/ch/qos/logback/access/sift logback-access/src/test/input/jetty logback-classic/src/main/java/ch/qos/logback/classic/sift logback-examples/src/main/java/chapter4/sift logback-site/src/site/pages/manual
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Tue Dec 23 19:17:20 CET 2008
Author: ceki
Date: Tue Dec 23 19:17:20 2008
New Revision: 2096
Added:
logback/trunk/logback-examples/src/main/java/chapter4/sift/
logback/trunk/logback-examples/src/main/java/chapter4/sift/SiftExample.java
logback/trunk/logback-examples/src/main/java/chapter4/sift/byUserid.xml
Modified:
logback/trunk/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java
logback/trunk/logback-access/src/test/input/jetty/siftingFile.xml
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java
logback/trunk/logback-site/src/site/pages/manual/appenders.html
Log:
Documenting SiftingAppender
Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java
==============================================================================
--- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java (original)
+++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java Tue Dec 23 19:17:20 2008
@@ -16,16 +16,27 @@
import ch.qos.logback.core.sift.Discriminator;
import ch.qos.logback.core.spi.ContextAwareBase;
+/**
+ *
+ * AccessEventDiscriminator's job is to return the value of a designated field in
+ * an {@link AccessEvent} instance.
+ *
+ * <p>The field is specified via the {@link FieldName} property.
+
+ * @author Ceki Gülcü
+ *
+ */
public class AccessEventDiscriminator extends ContextAwareBase implements
Discriminator<AccessEvent> {
boolean started = false;
/**
- * Allowed field names are: COOKIE, REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE,
- * REMOTE_ADDRESS, LOCAL_PORT,REQUEST_URI
+ * At present time the followed fields can be designated:
+ * COOKIE, REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS,
+ * LOCAL_PORT,REQUEST_URI
*
- * <p> The first three field names require a key attribute.
+ * <p> The first three fields require an additional key.
*/
public enum FieldName {
COOKIE, REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS, LOCAL_PORT, REQUEST_URI
@@ -34,7 +45,7 @@
String defaultValue;
String key;
FieldName fieldName;
- String optionalKey;
+ String additionalKey;
public String getDiscriminatingValue(AccessEvent acccessEvent) {
String rawValue = getRawDiscriminatingValue(acccessEvent);
@@ -48,11 +59,11 @@
public String getRawDiscriminatingValue(AccessEvent acccessEvent) {
switch (fieldName) {
case COOKIE:
- return acccessEvent.getCookie(optionalKey);
+ return acccessEvent.getCookie(additionalKey);
case LOCAL_PORT:
return String.valueOf(acccessEvent.getLocalPort());
case REQUEST_ATTRIBUTE:
- return acccessEvent.getAttribute(optionalKey);
+ return acccessEvent.getAttribute(additionalKey);
case SESSION_ATTRIBUTE:
return getSessionAttribute(acccessEvent);
case REMOTE_ADDRESS:
@@ -78,7 +89,7 @@
if (req != null) {
HttpSession session = req.getSession(false);
if (session != null) {
- Object v = session.getAttribute(optionalKey);
+ Object v = session.getAttribute(additionalKey);
if (v != null) {
return v.toString();
}
@@ -107,7 +118,7 @@
case SESSION_ATTRIBUTE:
case REQUEST_ATTRIBUTE:
case COOKIE:
- if (optionalKey == null) {
+ if (additionalKey == null) {
addError("\"OptionalKey\" property is mandatory for field name "+fieldName.toString());
errorCount++;
}
@@ -130,15 +141,15 @@
return fieldName;
}
- public String getOptionalKey() {
- return optionalKey;
+
+ public String getAdditionalKey() {
+ return additionalKey;
}
- public void setOptionalKey(String optionalKey) {
- this.optionalKey = optionalKey;
+ public void setAdditionalKey(String additionalKey) {
+ this.additionalKey = additionalKey;
}
-
/**
* @see #setDefaultValue(String)
* @return
Modified: logback/trunk/logback-access/src/test/input/jetty/siftingFile.xml
==============================================================================
--- logback/trunk/logback-access/src/test/input/jetty/siftingFile.xml (original)
+++ logback/trunk/logback-access/src/test/input/jetty/siftingFile.xml Tue Dec 23 19:17:20 2008
@@ -3,6 +3,7 @@
<appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender">
<KeyName>client</KeyName>
<Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator">
+ <Key>client</Key>
<DefaultValue>NA</DefaultValue>
<FieldName>REQUEST_URI</FieldName>
</Discriminator>
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java Tue Dec 23 19:17:20 2008
@@ -16,16 +16,30 @@
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.util.OptionHelper;
-public class MDCBasedDiscriminator extends ContextAwareBase implements Discriminator<LoggingEvent> {
-
- String key;
- String defaultValue;
-
- boolean started = false;
+/**
+ * MDCBasedDiscriminator essentially returns the value mapped to an MDC key. If
+ * the said value is null, then a default value is returned.
+ *
+ * <p>Both Key and the DefaultValue are user specified properties.
+ *
+ * @author Ceki Gülcü
+ *
+ */
+public class MDCBasedDiscriminator extends ContextAwareBase implements
+ Discriminator<LoggingEvent> {
+
+ private String key;
+ private String defaultValue;
+ private boolean started = false;
public MDCBasedDiscriminator() {
}
+ /**
+ * Return the value associated with an MDC entry desginated by the Key
+ * property. If that value is null, then return the value assigned to the
+ * DefaultValue property.
+ */
public String getDiscriminatingValue(LoggingEvent event) {
String mdcValue = MDC.get(key);
if (mdcValue == null) {
@@ -76,18 +90,15 @@
/**
* The default MDC value in case the MDC is not set for
- * {@link #setMdcKey(String) mdcKey}.
+ * {@link #setKey(String) mdcKey}.
*
- * <p> For example, if {@link #setMdcKey(String) mdcKey} is set to the value
+ * <p> For example, if {@link #setKey(String) Key} is set to the value
* "someKey", and the MDC is not set for "someKey", then this appender will
- * use the default value, which you can set with the help of method.
+ * use the default value, which you can set with the help of this method.
*
* @param defaultValue
*/
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
-
-
-
}
Added: logback/trunk/logback-examples/src/main/java/chapter4/sift/SiftExample.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter4/sift/SiftExample.java Tue Dec 23 19:17:20 2008
@@ -0,0 +1,51 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+package chapter4.sift;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+
+public class SiftExample {
+
+ public static void main(String[] args) throws JoranException {
+ if (args.length != 1) {
+ usage("Wrong number of arguments.");
+ }
+
+ String configFile = args[0];
+
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ JoranConfigurator configurator = new JoranConfigurator();
+ lc.reset();
+ configurator.setContext(lc);
+ configurator.doConfigure(configFile);
+
+
+ Logger logger = LoggerFactory.getLogger(SiftExample.class);
+ logger.debug("Application started");
+
+ MDC.put("userid", "Alice");
+ logger.debug("Alice says hello");
+
+ //StatusPrinter.print(lc);
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + SiftExample.class.getName()
+ + " configFile\n" + " configFile a logback configuration file");
+ System.exit(1);
+ }
+}
Added: logback/trunk/logback-examples/src/main/java/chapter4/sift/byUserid.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter4/sift/byUserid.xml Tue Dec 23 19:17:20 2008
@@ -0,0 +1,21 @@
+<configuration>
+ <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
+ <discriminator>
+ <Key>userid</Key>
+ <DefaultValue>unknown</DefaultValue>
+ </discriminator>
+ <sift>
+ <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
+ <File>${userid}.log</File>s
+ <Append>false</Append>
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</Pattern>
+ </layout>
+ </appender>
+ </sift>
+ </appender>
+
+ <root level="DEBUG">
+ <appender-ref ref="SIFT" />
+ </root>
+</configuration>
Modified: logback/trunk/logback-site/src/site/pages/manual/appenders.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/appenders.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/appenders.html Tue Dec 23 19:17:20 2008
@@ -3067,27 +3067,227 @@
deny requests coming via a network connection.
</p>
+
+ <h3><a name="SiftingAppender"
+ href="#SiftingAppender">SiftingAppender</a></h3>
+
+ <p>As its name implies, a <code>SiftingAppender</code> can be used
+ to separate (or sift) logging according to some runtime attribute.
+ For example, <code>SiftingAppender</code> can separate logging
+ events into distinct log files, one file per user.
+ </p>
+
+
+ <p><code>SiftingAppender</code> embeds and manages multiple
+ appenders which it builds dynamically depending on discriminating
+ values. The built appender is specified in a configuration file
+ within the <code>SiftingAppender</code> definition itself. By
+ default, <code>SiftingAppender</code> uses MDC key/value pairs as
+ a discriminator.
+ </p>
+
+ <p>After configuring logback, the <a
+ href="../xref/chapter4/sift/SiftExample.html">SiftExample</a>
+ application logs a message stating that the application has
+ started. It then sets the MDC key "userid" to "Alice" and logs a
+ message. Here is the salient code:</p>
+
+ <p class="source">logger.debug("Application started");
+MDC.put("userid", "Alice");
+logger.debug("Alice says hello"); </p>
+
+ <p>The next configuration file illustrates the use of
+ <code>SiftingAppender</code>.</p>
+
+
+ <em>Example 4.<span class="autoEx"/>: <code>SiftingAppender</code>
+ configuration
+ (logback-examples/src/main/java/chapter4/sift/byUserid.xml)</em>
+
+ <p class="source"><configuration>
+
+ <b><appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"></b>
+ <!-- in the absence of the class attribute, it is assumed that the
+ desired discriminator type is
+ ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
+ <b><discriminator></b>
+ <b><Key><span class="green">userid</span></Key></b>
+ <b><DefaultValue>unknown</DefaultValue></b>
+ <b></discriminator></b>
+ <b><sift></b>
+ <b><appender name="FILE-<span class="green">${userid}</span>" class="ch.qos.logback.core.FileAppender"></b>
+ <b><File><span class="green">${userid}</span>.log</File></b>
+ <b><Append>false</Append></b>
+ <b><layout class="ch.qos.logback.classic.PatternLayout"></b>
+ <b><Pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</Pattern></b>
+ <b></layout></b>
+ <b></appender></b>
+ <b></sift></b>
+ </appender>
+
+ <root level="DEBUG">
+ <appender-ref ref="SIFT" />
+ </root>
+</configuration></p>
+
+
+ <p>In the absence of a class attribute, it is assumed that the
+ discriminator type is <a
+ href="xref/ch/qos/logback/classic/sift/MDCBasedDiscriminator.html">MDCBasedDiscriminator</a>. It
+ will use the MDC value associated with the <span
+ class="option">Key</span> property as a districimator. If that
+ value is null, then the value associated with the <span
+ class="option">DefaultValue</span> property will be used.
+ </p>
+
+ <p>The <code>SiftingAppender</code> is unique in its capacity to
+ reference and configure nested appenders. In the above example,
+ within the <code>SiftingAppender</code> there will be nested
+ FileAppender instances, each instance identified by the value
+ associated with the "userid" MDC key. Whenever the "userid" MDC
+ key is assigned a new value, a new <code>FileAppender</code>
+ instance will be built from scratch. The SiftingAppender keeps
+ track of the appenders it creates. Appenders unused for 30 minutes
+ will be automatically closed and discarded.
+ </p>
+
+ <p>It is not enough to have different appender instances, each
+ instance must output to a distinct target resource. To allow such
+ differentiation, within the nested appender (FileAppender above),
+ the key passed to the discriminator, "userid" in the above
+ example, becomes a <a
+ href="joran.html#variableSubstitution">variable</a>. Consequently,
+ this variable can be used to differentiate the actual resource
+ used by a given nested appender.
+ </p>
+
+ <p>Running <code>SiftExample</code> application with the
+ "byUserid.xml" configuration file shown above, will result in two
+ distinct log files, "unknown.log" and "Alice.log".
+ </p>
+
+
+ <h3><a name="WriteYourOwnAppender"
+ href="#WriteYourOwnAppender">Writing your own Appender</a></h3>
+
+
+ <p>You can easily write your appender by sub-classing <code>AppenderBase</code>.
+ It handles support for filters, status among other functionality shared by most appenders.
+ The derived class only needs to implement one method, namely
+ <code>append(Object eventObject)</code>.
+ </p>
+
+ <p>The <code>CountingConsoleAppender</code>, which we list next, appends a limited
+ number of incoming events on the console. It shuts down after the limit is reached.
+ It uses a <code>Layout</code> to format the events and accepts a parameter,
+ thus a few more methods are needed.
+ </p>
+
+ <em>Example 4.<span class="autoExec"/>: <code>CountingConsoleAppender</code> (logback-examples/src/main/java/chapter4/CountingConsoleAppender.java)</em>
+ <p class="source">package chapter4;
+
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.Layout;
+
+
+public class CountingConsoleAppender extends AppenderBase<LoggingEvent> {
+ static int DEFAULT_LIMIT = 16;
+ int counter = 0;
+ int limit = DEFAULT_LIMIT;
+
+ private Layout<LoggingEvent> layout;
+
+ public CountingConsoleAppender() {
+ }
+
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ @Override
+ public void start() {
+ if (this.layout == null) {
+ addError("No layout set for the appender named ["+ name +"].");
+ return;
+ }
+
+ super.start();
+ }
+
+ public void append(LoggingEvent event) {
+
+ if (counter >= limit) {
+ return;
+ }
+
+ // output the events as formatted by our layout
+ System.out.print(this.layout.doLayout(event));
+
+ // prepare for next event
+ counter++;
+ }
+
+ public Layout<LoggingEvent> getLayout() {
+ return layout;
+ }
+
+ public void setLayout(Layout<LoggingEvent> layout) {
+ this.layout = layout;
+ }
+}</p>
+
+ <p>The <code>start()</code> method checks for the presence of a
+ <code>Layout</code>. In case none is found, the appender is not
+ started.
+ </p>
- <a name="Access"></a>
- <h2>Logback Access</h2>
+ <p>This custom appender illustrates a two points:
+ </p>
- <p>Most of the appenders found in logback classic can be used
- within logback access. They function mostly in the same way as
- their logback classic counterpart. In the next section, we will
- cover their use, but will focuse on the differences with the
- classic appenders.
+ <ul>
+ <li>All properties that follow the setter/getter JavaBeans
+ conventions are handled transparently. The <code>start()</code>
+ method, that is called automatically, has the responsability to
+ check that the given properties are coherent.
+ </li>
+ <li>The <code>AppenderBase.doAppend()</code> method invokes the
+ append() method of its derived classes where actual output
+ operations occur. It is in this method that appenders format
+ events by invoking their layouts.
+ </li>
+ </ul>
+
+ <p>The <code>CountingConsoleAppender</code> can be configured like
+ any appender. See sample file
+ <em>logback-examples/src/main/java/chapter4/countingConsole.xml</em>
+ for an example.
+ </p>
+
+
+ <h2><a name="logback_access" href="#logback_access">Logback
+ Access</a></h2>
+
+ <p>Most of the appenders found in logback-classic have their
+ equivalent in logback-access. These work essentialy in the same
+ way as their locback-classic counterparts. In the next section, we
+ will cover their use.
</p>
<a name="AccessSocketAppender"/>
<h3>SocketAppender</h3>
- <p>
- The <a href="../xref/ch/qos/logback/access/net/SocketAppender.html">
- <code>SocketAppender</code></a> is designed to log to a
- remote entity by transmitting serialized <code>AccessEvent</code> objects over the wire.
- Remote logging is non-intrusive as far as the access event is concerned.
- On the receiving end after de-serialization, the event can be logged as
- if it were generated locally.
+ <p>The <a
+ href="../xref/ch/qos/logback/access/net/SocketAppender.html">
+ <code>SocketAppender</code></a> is designed to log to a remote
+ entity by transmitting serialized <code>AccessEvent</code> objects
+ over the wire. Remote logging is non-intrusive as far as the
+ access event is concerned. On the receiving end after
+ de-serialization, the event can be logged as if it were generated
+ locally.
</p>
<p>
The properties of access' <code>SocketAppender</code> are the same as those available
@@ -3280,7 +3480,7 @@
</p>
<em>Example 4.<span class="autoEx"/>: DBAppender configuration (logback-examples/src/main/java/chapter4/conf/access/logback-DB.xml)</em>
-<div class="source"><pre><configuration>
+ <p class="source"><configuration>
<appender name="DB" class="ch.qos.logback.access.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
@@ -3293,110 +3493,32 @@
</appender>
<appender-ref ref="DB" />
-</configuration></pre></div>
-
-
- <a name="WriteYourOwnAppender"></a>
- <h2>Writing your own Appender</h2>
+</configuration></p>
- <p>You can easily write your appender by sub-classing <code>AppenderBase</code>.
- It handles support for filters, status among other functionality shared by most appenders.
- The derived class only needs to implement one method, namely
- <code>append(Object eventObject)</code>.
+ <h3><a name="AccessSiftingAppender"
+ href="#AccessSiftingAppender">SiftingAppender</a></h3>
+
+ <p>The SiftingAppender in logback-access is quite similar to its
+ logbacl-classic counterpart. The main difference is that in
+ logback-access the default discriminator, namely <a
+ href="../xref/ch/qos/logback/access/sift/AccessEventDiscriminator.html">AccessEventDiscriminator</a>,
+ is not MDC based. As its name suggests, AccessEventDiscriminator,
+ uses a designated field in AccessEvent as basis for selecting a
+ nested appender. If the value of the designated field is null,
+ then the value specified in the <span
+ class="option">DefaultValue</span> property is used.
</p>
- <p>The <code>CountingConsoleAppender</code>, which we list next, appends a limited
- number of incoming events on the console. It shuts down after the limit is reached.
- It uses a <code>Layout</code> to format the events and accepts a parameter,
- thus a few more methods are needed.
- </p>
+ <p>The desginated AccessEvent field can be one of COOKIE,
+ REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS, LOCAL_PORT,
+ REQUEST_URI. Note that the fiest three fields require that the
+ <span class="option">AdditionalKey</span> property to be
+ specified.</p>
- <em>Example 4.<span class="autoExec"/>: <code>CountingConsoleAppender</code> (logback-examples/src/main/java/chapter4/CountingConsoleAppender.java)</em>
- <p class="source">package chapter4;
-import ch.qos.logback.core.AppenderBase;
-import ch.qos.logback.core.Layout;
-public class CountingConsoleAppender extends AppenderBase<LoggingEvent> {
- static int DEFAULT_LIMIT = 16;
- int counter = 0;
- int limit = DEFAULT_LIMIT;
-
- private Layout<LoggingEvent> layout;
-
- public CountingConsoleAppender() {
- }
-
- public void setLimit(int limit) {
- this.limit = limit;
- }
-
- public int getLimit() {
- return limit;
- }
-
- @Override
- public void start() {
- if (this.layout == null) {
- addError("No layout set for the appender named ["+ name +"].");
- return;
- }
-
- super.start();
- }
-
- public void append(LoggingEvent event) {
-
- if (counter >= limit) {
- return;
- }
-
- // output the events as formatted by our layout
- System.out.print(this.layout.doLayout(event));
-
- // prepare for next event
- counter++;
- }
-
- public Layout<LoggingEvent> getLayout() {
- return layout;
- }
-
- public void setLayout(Layout<LoggingEvent> layout) {
- this.layout = layout;
- }
-}</p>
-
- <p>The <code>start()</code> method checks for the presence of a
- <code>Layout</code>. In case none is found, the appender is not
- started.
- </p>
-
- <p>This custom appender illustrates a two points:
- </p>
-
- <ul>
- <li>All properties that follow the setter/getter JavaBeans
- conventions are handled transparently. The <code>start()</code>
- method, that is called automatically, has the responsability to
- check that the given properties are coherent.
- </li>
- <li>The <code>AppenderBase.doAppend()</code> method invokes the
- append() method of its derived classes where actual output
- operations occur. It is in this method that appenders format
- events by invoking their layouts.
- </li>
- </ul>
-
- <p>The <code>CountingConsoleAppender</code> can be configured like
- any appender. See sample file
- <em>logback-examples/src/main/java/chapter4/countingConsole.xml</em>
- for an example.
- </p>
-
-
<script src="../templates/footer.js" type="text/javascript"></script>
More information about the logback-dev
mailing list