[logback-dev] svn commit: r1063 - in logback/trunk: logback-examples/src/main/java/chapter7 logback-site/src/site/xdocTemplates/manual

noreply.seb at qos.ch noreply.seb at qos.ch
Tue Dec 5 16:31:34 CET 2006


Author: seb
Date: Tue Dec  5 16:31:33 2006
New Revision: 1063

Added:
   logback/trunk/logback-examples/src/main/java/chapter7/
   logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java
   logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java
   logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java
   logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
   logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml
   logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml
Modified:
   logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml

Log:
On going work on chapter 7

Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,26 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * 
+ * Copyright (C) 1999-2006, QOS.ch
+ * 
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package chapter7;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+
+/**
+ * NumberCruncher factors positive integers.
+ */
+public interface NumberCruncher extends Remote {
+  /**
+   * Factor a positive integer <code>number</code> and return its
+   * <em>distinct</em> factor's as an integer array.
+   * */
+  int[] factor(int number) throws RemoteException;
+}

Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,83 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * 
+ * Copyright (C) 1999-2006, QOS.ch
+ * 
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package chapter7;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+
+
+/**
+ * NumberCruncherClient is a simple client for factoring integers. A
+ * remote NumberCruncher is contacted and asked to factor an
+ * integer. The factors returned by the {@link NumberCruncherServer}
+ * are displayed on the screen.
+ * */
+public class NumberCruncherClient {
+  public static void main(String[] args) {
+    if (args.length == 1) {
+      try {
+        String url = "rmi://" + args[0] + "/Factor";
+        NumberCruncher nc = (NumberCruncher) Naming.lookup(url);
+        loop(nc);
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    } else {
+      usage("Wrong number of arguments.");
+    }
+  }
+
+  static void usage(String msg) {
+    System.err.println(msg);
+    System.err.println("Usage: java chapter7.NumberCruncherClient HOST\n" +
+      "   where HOST is the machine where the NumberCruncherServer is running.");
+    System.exit(1);
+  }
+
+  static void loop(NumberCruncher nc) {
+    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+    int i = 0;
+
+    while (true) {
+      System.out.print("Enter a number to factor, '-1' to quit: ");
+
+      try {
+        i = Integer.parseInt(in.readLine());
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+
+      if (i == -1) {
+        System.out.print("Exiting loop.");
+
+        return;
+      } else {
+        try {
+          System.out.println("Will attempt to factor " + i);
+
+          int[] factors = nc.factor(i);
+          System.out.print("The factors of " + i + " are");
+
+          for (int k = 0; k < factors.length; k++) {
+            System.out.print(" " + factors[k]);
+          }
+
+          System.out.println(".");
+        } catch (RemoteException e) {
+          System.err.println("Could not factor " + i);
+          e.printStackTrace();
+        }
+      }
+    }
+  }
+}

Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,159 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * 
+ * Copyright (C) 1999-2006, QOS.ch
+ * 
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package chapter7;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Vector;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.MDC;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+
+
+/**
+ * A simple NumberCruncher implementation that logs its progress when
+ * factoring numbers. The purpose of the whole exercise is to show the
+ * use of mapped diagnostic contexts in order to distinguish the log
+ * output from different client requests.
+ * */
+public class NumberCruncherServer extends UnicastRemoteObject
+  implements NumberCruncher {
+
+  private static final long serialVersionUID = 1L;
+
+  static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
+
+  public NumberCruncherServer() throws RemoteException {
+  }
+
+  public int[] factor(int number) throws RemoteException {
+    // The client's host is an important source of information.
+    try {
+      MDC.put("client", NumberCruncherServer.getClientHost());
+    } catch (java.rmi.server.ServerNotActiveException e) {
+      logger.warn("Caught unexpected ServerNotActiveException.", e);
+    }
+
+    // The information contained within the request is another source
+    // of distinctive information. It might reveal the users name,
+    // date of request, request ID etc. In servlet type environments,
+    // useful information is contained in the HttpRequest or in the  
+    // HttpSession.
+    MDC.put("number", String.valueOf(number));
+
+    logger.info("Beginning to factor.");
+
+    if (number <= 0) {
+      throw new IllegalArgumentException(number +
+        " is not a positive integer.");
+    } else if (number == 1) {
+      return new int[] { 1 };
+    }
+
+    Vector<Integer> factors = new Vector<Integer>();
+    int n = number;
+
+    for (int i = 2; (i <= n) && ((i * i) <= number); i++) {
+      // It is bad practice to place log requests within tight loops.
+      // It is done here to show interleaved log output from
+      // different requests. 
+      logger.debug("Trying " + i + " as a factor.");
+
+      if ((n % i) == 0) {
+        logger.info("Found factor " + i);
+        factors.addElement(new Integer(i));
+
+        do {
+          n /= i;
+        } while ((n % i) == 0);
+      }
+
+      // Placing artificial delays in tight-loops will also lead to
+      // sub-optimal resuts. :-)
+      delay(100);
+    }
+
+    if (n != 1) {
+      logger.info("Found factor " + n);
+      factors.addElement(new Integer(n));
+    }
+
+    int len = factors.size();
+
+    int[] result = new int[len];
+
+    for (int i = 0; i < len; i++) {
+      result[i] = ((Integer) factors.elementAt(i)).intValue();
+    }
+
+    // clean up
+    MDC.remove("client");
+    MDC.remove("number");
+
+    return result;
+  }
+
+  static void usage(String msg) {
+    System.err.println(msg);
+    System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" +
+      "   where configFile is a logback configuration file.");
+    System.exit(1);
+  }
+
+  public static void delay(int millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException e) {
+    }
+  }
+
+  public static void main(String[] args) {
+    if (args.length != 1) {
+      usage("Wrong number of arguments.");
+    }
+
+    String configFile = args[0];
+
+    if (configFile.endsWith(".xml")) {
+      try {
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        JoranConfigurator configurator = new JoranConfigurator();
+        configurator.setContext(lc);
+        lc.shutdownAndReset();
+        configurator.doConfigure(args[0]);
+      } catch (JoranException je) {
+        je.printStackTrace();
+      }
+    }
+
+    NumberCruncherServer ncs;
+
+    try {
+      ncs = new NumberCruncherServer();
+      logger.info("Creating registry.");
+
+      Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
+      registry.rebind("Factor", ncs);
+      logger.info("NumberCruncherServer bound and ready.");
+    } catch (Exception e) {
+      logger.error("Could not bind NumberCruncherServer.", e);
+
+      return;
+    }
+  }
+}

Added: logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,52 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * 
+ * Copyright (C) 1999-2006, QOS.ch
+ * 
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package chapter7;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.MDC;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.core.ConsoleAppender;
+
+public class SimpleMDC {
+  static public void main(String[] args) throws Exception {
+    // You can put values in the MDC at any time. We first put the
+    // first name
+    MDC.put("first", "Dorothy");
+
+    // Configure logback
+    PatternLayout layout = new PatternLayout();
+    layout.setPattern("%X{first} %X{last} - %m%n");
+    layout.start();
+    ConsoleAppender appender = new ConsoleAppender();
+    appender.setLayout(layout);
+    appender.start();
+    Logger root = (Logger)LoggerFactory.getLogger("root");
+    root.addAppender(appender);
+    
+    // get a logger
+    Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class);
+
+    // We now put the last name
+    MDC.put("last", "Parker");
+
+    // The most beautiful two words in the English language according
+    // to Dorothy Parker:
+    logger.info("Check enclosed.");
+    logger.debug("The most beautiful two words in English.");
+
+    MDC.put("first", "Richard");
+    MDC.put("last", "Nixon");
+    logger.info("I am not a crook.");
+    logger.info("Attributed to the former US president. 17 Nov 1973.");
+  }
+}

Added: logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<configuration>
+
+  <appender name="CONSOLE"
+            class="ch.qos.logback.core.ConsoleAppender">
+    <layout class="ch.qos.logback.classic.PatternLayout">
+      <Pattern>%-4r [%thread] %-5level C:%X{client} N:%X{number} - %msg%n</Pattern>
+    </layout>	    
+  </appender>
+  
+  <root>
+    <level value ="debug"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>  
+</configuration>

Modified: logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml
==============================================================================
--- logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml	(original)
+++ logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml	Tue Dec  5 16:31:33 2006
@@ -157,11 +157,11 @@
 		</p>
 		
 		<table>
-			<th>
-				<td>Name</td>
-				<td>Type</td>
-				<td>Description</td>
-			</th>
+			<tr>
+				<th>Name</th>
+				<th>Type</th>
+				<th>Description</th>
+			</tr>
 			<tr>
 				<td>event
 				</td>
@@ -285,21 +285,21 @@
 <em>Example 6.1: Basic event evaluator usage (logback-examples/src/main/java/chapter6/turboFilters.xml)</em>
 <div class="source"><pre>&lt;configuration>
 
-	&lt;turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
-		&lt;MDCKey>username&lt;/MDCKey>
-		&lt;Value>sebastien&lt;/Value>
-		&lt;OnMatch>ACCEPT&lt;/OnMatch>
-	&lt;/turboFilter>
+  &lt;turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
+    &lt;MDCKey>username&lt;/MDCKey>
+    &lt;Value>sebastien&lt;/Value>
+    &lt;OnMatch>ACCEPT&lt;/OnMatch>
+  &lt;/turboFilter>
 	
-	&lt;turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
-		&lt;Marker>billing&lt;/Marker>
-		&lt;OnMatch>DENY&lt;/OnMatch>
-	&lt;/turboFilter>
+  &lt;turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
+    &lt;Marker>billing&lt;/Marker>
+    &lt;OnMatch>DENY&lt;/OnMatch>
+  &lt;/turboFilter>
 
   &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender">
     &lt;layout class="ch.qos.logback.classic.PatternLayout">
       &lt;Pattern>%date [%thread] %-5level %logger - %msg%n&lt;/Pattern>
-    &lt;/layout>
+  &lt;/layout>
   &lt;/appender>
 
   &lt;root>
@@ -358,7 +358,7 @@
 		</p>
     
     
-    <h3>Logback Access</h3>
+    <h2>Logback Access</h2>
     
     <p>
     	Logback access benefits from most of the possibilities available
@@ -368,7 +368,7 @@
     	<code>TurboFilter</code> objects are not available to the access module.
     </p>
     
-    <h2>Filters</h2>
+    <h3>Filters</h3>
     
     <p>
     	<code>EvaluatorFilter</code> objects, with their expressions, are available to

Added: logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml	Tue Dec  5 16:31:33 2006
@@ -0,0 +1,478 @@
+<document>
+<!-- 
+		
+		Warning: do not use any auto-format function on this file.
+		Since "source" divs use pre as white-space, it affects the
+		look of the code parts in this document.
+		
+	-->
+
+	<body>
+		<h2>Chapter 7: Mapped Diagnostic Context</h2>
+		<div class="author">
+			Authors: Ceki G&#252;lc&#252;, S&#233;bastien Pennec
+		</div>
+
+		<table>
+			<tr>
+				<td valign="top" align="top">
+						<a rel="license"
+							href="http://creativecommons.org/licenses/by-nc-sa/2.5/">
+							<img alt="Creative Commons License"
+								style="border-width: 0"
+								src="http://creativecommons.org/images/public/somerights20.png" />
+						</a>
+				</td>
+				<td>
+					<p>Copyright &#169; 2000-2006, QOS.ch</p>
+
+					<p>
+						<!--Creative Commons License-->
+						This work is licensed under a
+						<a rel="license"
+							href="http://creativecommons.org/licenses/by-nc-sa/2.5/">
+							Creative Commons
+							Attribution-NonCommercial-ShareAlike 2.5
+							License
+						</a>.
+						<!--/Creative Commons License-->
+					</p>
+				</td>
+			</tr>
+		</table>
+		
+		<p>
+				One of the design goals of logback is to audit and debug complex distributed applications. 
+				Most real-world distributed systems need to deal with multiple clients simultaneously. 
+				In a typical multithreaded implementation of such a system, different threads will handle 
+				different clients. A possible but discouraged approach to differentiate the logging output of 
+				one client from another consists of instantiating a new and separate logger for each client. 
+				This technique promotes the proliferation of loggers and considerably increases 
+				their management overhead.
+		</p>
+		<p> 
+				A lighter technique consists of uniquely stamping each 
+				log request servicing a given client. Neil Harrison described this method in the book 
+				<em>"Patterns for Logging Diagnostic Messages,"</em> in 
+				Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, 
+				and F. Buschmann (Addison-Wesley, 1997). Logback offers a variant of this technique: 
+				Mapped Diagnostic Contexts (MDC).
+		</p>
+		
+		<p>
+			To uniquely stamp each request, the user puts contextual information into the 
+			<code><a href="../xref/ch/qos/logback/classic/MDC.html">MDC</a></code>, 
+			the abbreviation of Mapped Diagnostic Context. 
+			The public interface of the MDC class is shown below. 
+		</p>
+
+<div class="source"><pre>package ch.qos.logback.classic;
+
+public class MDC {
+  //Put a context value as identified by <em>key</em>
+  //into the current thread's context map.
+  <b>public static void put(String key, String val);</b>
+
+  //Get the context identified by the <code>key</code> parameter.
+  <b>public static String get(String key);</b>
+
+  //Remove the the context identified by the <code>key</code> parameter.
+  <b>public static void remove(String key);</b>
+
+  //Clear all entries in the MDC.
+  <b>public static void clear();</b>
+
+  //Returns the keys in the MDC as a Set. The returned value can be null.
+  <b>public static Set&lt;String> getKeys();</b>
+}</pre></div>
+
+		<p>
+			The <code>MDC</code> class contains only static methods. 
+			It lets the developer place information in a “diagnostic context” that can be 
+			subsequently retrieved by certain logback components. The 
+			<code>MDC</code> manages contextual information on a per thread basis.  
+			Typically, while starting to service a new client request, the developer will 
+			insert pertinent contextual information, such as the client id, client's IP 
+			address, request parameters etc. into the <code>MDC</code>. Logback components, 
+			if appropriately configured, will automatically include this information 
+			in each log entry.
+		</p>
+
+		<p>
+			The next application named 
+			<code><a href="../xref/chapter7/SimpleMDC.html">SimpleMDC</a></code> 
+			demonstrates this basic principle.
+		</p>
+<em>Example 7.1: Basic MDC usage (<a href="../xref/chapter7/SimpleMDC.html">
+logback-examples/src/main/java/chapter7/SimpleMDC.java)</a></em>
+<div class="source"><pre>package chapter7;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.MDC;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.core.ConsoleAppender;
+
+public class SimpleMDC {
+  static public void main(String[] args) throws Exception {
+    // You can put values in the MDC at any time. We first put the
+    // first name
+    <b>MDC.put("first", "Dorothy");</b>
+
+    // Configure logback
+    PatternLayout layout = new PatternLayout();
+    layout.setPattern("%X{first} %X{last} - %m%n");
+    layout.start();
+    ConsoleAppender appender = new ConsoleAppender();
+    appender.setLayout(layout);
+    appender.start();
+    Logger root = (Logger)LoggerFactory.getLogger("root");
+    root.addAppender(appender);
+    
+    // get a logger
+    Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class);
+
+    // We now put the last name
+    <b>MDC.put("last", "Parker");</b>
+
+    // The most beautiful two words in the English language according
+    // to Dorothy Parker:
+    logger.info("Check enclosed.");
+    logger.debug("The most beautiful two words in English.");
+
+    MDC.put("first", "Richard");
+    MDC.put("last", "Nixon");
+    logger.info("I am not a crook.");
+    logger.info("Attributed to the former US president. 17 Nov 1973.");
+  }
+}</pre></div>
+
+		<p>
+			The main method starts by associating the value <em>Dorothy</em> with 
+			the key <em>first</em> in the <code>MDC</code>. You can place as many 
+			value/key associations in the <code>MDC</code> as you wish. 
+			Multiple insertions with the same key will overwrite older values. 
+			The code then proceeds to configure logback. 
+			Note the usage of the <em>%X</em> specifier within the 
+			<code>PatternLayout</code> conversion pattern. The <em>%X</em> 
+			conversion specifier is employed twice, once for the key <em>first</em> 
+			and once for the key <em>last</em>. After configuring the root logger, 
+			the code associates the value <em>Parker</em> with the key <em>last</em>. 
+			It then invokes the logger twice with different messages. 
+			The code finishes by setting the <code>MDC</code> to different values 
+			and issuing several logging requests. Running SimpleMDC yields:
+		</p>
+
+<div class="source"><pre>Dorothy Parker - Check enclosed.
+Dorothy Parker - The most beautiful two words in English.
+Richard Nixon - I am not a crook.
+Richard Nixon - Attributed to the former US president. 17 Nov 1973.</pre></div>
+
+
+		<p>
+			The <code>SimpleMDC</code> application illustrates how logback layouts, 
+			if configured appropriately, automatically output <code>MDC</code> information. 
+			Moreover, the information placed into the <code>MDC</code> can be used by 
+			multiple logger invocations.
+		</p>
+		
+		<p>
+			Mapped Diagnostic Contexts shine brightest within client server architectures. 
+			Typically, multiple clients will be served by multiple threads on the server. 
+			Although the methods in the <code>MDC</code> class are static, 
+			the diagnostic context is managed on a per thread basis, allowing each server 
+			thread to bear a distinct <code>MDC</code> stamp. <code>MDC</code> operations 
+			such as <code>put()</code> and <code>get()</code> affect the <code>MDC</code> 
+			of the <em>current</em> thread only. The <code>MDC</code> in other threads remain 
+			unaffected. Given that <code>MDC</code> information is managed on a 
+			per thread basis, each thread will have its own copy of the <code>MDC</code>. 
+			Thus, there is no need for the developer to worry about thread-safety or 
+			synchronization when programming with the <code>MDC</code> because 
+			it safely and transparently handles these issues.
+		</p>
+
+		<p>
+			The next example is somewhat more advanced. 
+			It shows how the 	<code>MDC</code> can be used in a client-server setting. 
+			The server-side implements the <code>NumberCruncher</code> interface shown in 
+			Example 7.2 below. <code>The NumberCruncher</code> interface contains a single 
+			method named <code>factor()</code>. Using RMI technology, client invokes the 
+			<code>factor()</code> method of the server application to retrieve the distinct 
+			factors of an integer.
+		</p>
+
+<em>Example 7.2: The service interface (<a href="../xref/chapter7/NumberCruncher.html">
+logback-examples/src/main/java/chapter7/NumberCruncher.java)</a></em>
+<div class="source"><pre>package chapter7;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * NumberCruncher factors positive integers.
+ */
+public interface NumberCruncher extends Remote {
+  /**
+   * Factor a positive integer <code>number</code> and return its
+   * <em>distinct</em> factor's as an integer array.
+   * */
+  int[] factor(int number) throws RemoteException;
+}</pre></div>
+
+		<p>
+			The <code>NumberCruncherServer</code> application, listed in Example 7.3 below, 
+			implements the <code>NumberCruncher</code> interface. Its main method exports 
+			an RMI Registry on the local host that accepts requests on a well-known port.  
+		</p>
+
+<em>Example 7.2: The server side (<a href="../xref/chapter7/NumberCruncherServer.html">
+logback-examples/src/main/java/chapter7/NumberCruncherServer.java)</a></em>
+<div class="source"><pre>package chapter7;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Vector;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.MDC;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+
+
+/**
+ * A simple NumberCruncher implementation that logs its progress when
+ * factoring numbers. The purpose of the whole exercise is to show the
+ * use of mapped diagnostic contexts in order to distinguish the log
+ * output from different client requests.
+ * */
+public class NumberCruncherServer extends UnicastRemoteObject
+  implements NumberCruncher {
+
+  private static final long serialVersionUID = 1L;
+
+  static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
+
+  public NumberCruncherServer() throws RemoteException {
+  }
+
+  public int[] factor(int number) throws RemoteException {
+    // The client's host is an important source of information.
+    try {
+      <b>MDC.put("client", NumberCruncherServer.getClientHost());</b>
+    } catch (java.rmi.server.ServerNotActiveException e) {
+      logger.warn("Caught unexpected ServerNotActiveException.", e);
+    }
+
+    // The information contained within the request is another source
+    // of distinctive information. It might reveal the users name,
+    // date of request, request ID etc. In servlet type environments,
+    // useful information is contained in the HttpRequest or in the  
+    // HttpSession.
+    <b>MDC.put("number", String.valueOf(number));</b>
+
+    logger.info("Beginning to factor.");
+
+    if (number &lt;= 0) {
+      throw new IllegalArgumentException(number +
+        " is not a positive integer.");
+    } else if (number == 1) {
+      return new int[] { 1 };
+    }
+
+    Vector&lt;Integer> factors = new Vector&lt;Integer>();
+    int n = number;
+
+    for (int i = 2; (i &lt;= n) &amp;&amp; ((i * i) &lt;= number); i++) {
+      // It is bad practice to place log requests within tight loops.
+      // It is done here to show interleaved log output from
+      // different requests. 
+      logger.debug("Trying " + i + " as a factor.");
+
+      if ((n % i) == 0) {
+        logger.info("Found factor " + i);
+        factors.addElement(new Integer(i));
+
+        do {
+          n /= i;
+        } while ((n % i) == 0);
+      }
+
+      // Placing artificial delays in tight-loops will also lead to
+      // sub-optimal resuts. :-)
+      delay(100);
+    }
+
+    if (n != 1) {
+      logger.info("Found factor " + n);
+      factors.addElement(new Integer(n));
+    }
+
+    int len = factors.size();
+
+    int[] result = new int[len];
+
+    for (int i = 0; i &lt; len; i++) {
+      result[i] = ((Integer) factors.elementAt(i)).intValue();
+    }
+
+    <b>// clean up
+    MDC.remove("client");
+    MDC.remove("number");</b>
+
+    return result;
+  }
+
+  static void usage(String msg) {
+    System.err.println(msg);
+    System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" +
+      "   where configFile is a logback configuration file.");
+    System.exit(1);
+  }
+
+  public static void delay(int millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException e) {
+    }
+  }
+
+  public static void main(String[] args) {
+    if (args.length != 1) {
+      usage("Wrong number of arguments.");
+    }
+
+    String configFile = args[0];
+
+    if (configFile.endsWith(".xml")) {
+      try {
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        JoranConfigurator configurator = new JoranConfigurator();
+        configurator.setContext(lc);
+        lc.shutdownAndReset();
+        configurator.doConfigure(args[0]);
+      } catch (JoranException je) {
+        je.printStackTrace();
+      }
+    }
+
+    NumberCruncherServer ncs;
+
+    try {
+      ncs = new NumberCruncherServer();
+      logger.info("Creating registry.");
+
+      Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
+      registry.rebind("Factor", ncs);
+      logger.info("NumberCruncherServer bound and ready.");
+    } catch (Exception e) {
+      logger.error("Could not bind NumberCruncherServer.", e);
+
+      return;
+    }
+  }
+}</pre></div>
+
+		<p>
+				The implementation of the <code>factor(int number)</code> method is 
+				of particular relevance. It starts by putting the client's hostname into the 
+				<code>MDC</code> under the key <em>client</em>. The number to factor, 
+				as requested by the client, is put into the <code>MDC</code> under the key 
+				<em>number</em>. After computing the distinct factors of the integer 
+				parameter, the result is returned to the client. Before returning the 
+				result however, the values for the <em>client</em> and <em>number</em> are 
+				cleared by calling the <code>MDC.remove(9</code> method. Normally, 
+				a <code>put()</code> operation should be balanced by the corresponding 
+				<code>remove()</code> operation. Otherwise, the <code>MDC</code> will 
+				contain stale values for certain keys. We would recommend that whenever 
+				possible <code>remove()</code> operations be performed within finally blocks, 
+				ensuring their invocation regardless of the execution path of the code.
+		</p>	
+		
+		<p>
+			After these theoretical explanations, we are ready to run the number 
+			cruncher example. Start the server with the following command:
+		</p>
+		
+<div class="source"><pre>java chapter7.NumberCruncherServer src/main/java/chapter7/mdc1.xml</pre></div>
+		
+		<p>
+			The <em>mdc1.xml</em> configuration file is listed below:
+		</p>
+
+<div class="source"><pre>&lt;configuration>
+
+  &lt;appender name="CONSOLE"
+    class="ch.qos.logback.core.ConsoleAppender">
+    &lt;layout class="ch.qos.logback.classic.PatternLayout">
+      &lt;Pattern>%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n&lt;/Pattern>
+    &lt;/layout>	    
+  &lt;/appender>
+  
+  &lt;root>
+    &lt;level value ="debug"/>
+    &lt;appender-ref ref="CONSOLE"/>
+  &lt;/root>  
+&lt;/configuration></pre></div>
+
+		<p>
+			Note the use of the <em>%X</em> conversion specifier within the 
+			<span class="option">Pattern</span> option.
+		</p>
+	
+		<p>
+			The following command starts an instance of <code>NumberCruncherClient</code> 
+			application:  	
+		</p>
+		
+<div class="source"><pre>java chapter7.NumberCruncherClient <em>hostname</em></pre></div>
+
+		<p>
+			where <em>hostname</em> is the host where the 
+			<code>NumberCruncherServer</code> is running
+		</p>
+		
+		<p>
+			Executing multiple instances of the client and requesting the server to factor 
+			the numbers 129 from the first client and shortly thereafter 
+			the number 71 from the second client, the server outputs the following (edited to fit):
+		</p>
+		
+<div class="source"><pre>
+70984 [RMI TCP Connection(4)-192.168.1.6] INFO  C:192.168.1.6 N:129 - Beginning to factor.
+70984 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 2 as a factor.
+71093 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 3 as a factor.
+71093 [RMI TCP Connection(4)-192.168.1.6] INFO  C:192.168.1.6 N:129 - Found factor 3
+71187 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 4 as a factor.
+71297 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 5 as a factor.
+71390 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 6 as a factor.
+71453 [RMI TCP Connection(5)-192.168.1.6] INFO  C:192.168.1.6 N:71 - Beginning to factor.
+71453 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 2 as a factor.
+71484 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 7 as a factor.
+71547 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 3 as a factor.
+71593 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 8 as a factor.
+71656 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 4 as a factor.
+71687 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 9 as a factor.
+71750 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 5 as a factor.
+71797 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 10 as a factor.
+71859 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 6 as a factor.
+71890 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 11 as a factor.
+71953 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 7 as a factor.
+72000 [RMI TCP Connection(4)-192.168.1.6] INFO  C:192.168.1.6 N:129 - Found factor 43
+72062 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 8 as a factor.
+72156 [RMI TCP Connection(5)-192.168.1.6] INFO  C:192.168.1.6 N:71 - Found factor 71</pre></div>
+		
+		
+		
+
+
+
+
+
+  </body>
+</document>
\ No newline at end of file



More information about the logback-dev mailing list