[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><configuration>
- <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
- <MDCKey>username</MDCKey>
- <Value>sebastien</Value>
- <OnMatch>ACCEPT</OnMatch>
- </turboFilter>
+ <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
+ <MDCKey>username</MDCKey>
+ <Value>sebastien</Value>
+ <OnMatch>ACCEPT</OnMatch>
+ </turboFilter>
- <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
- <Marker>billing</Marker>
- <OnMatch>DENY</OnMatch>
- </turboFilter>
+ <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
+ <Marker>billing</Marker>
+ <OnMatch>DENY</OnMatch>
+ </turboFilter>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger - %msg%n</Pattern>
- </layout>
+ </layout>
</appender>
<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ülcü, Sé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 © 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<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 <= 0) {
+ throw new IllegalArgumentException(number +
+ " is not a positive integer.");
+ } else if (number == 1) {
+ return new int[] { 1 };
+ }
+
+ Vector<Integer> factors = new Vector<Integer>();
+ int n = number;
+
+ for (int i = 2; (i <= n) && ((i * i) <= number); i++) {
+ // It is bad practice to place log requests within tight loops.
+ // It is done here to show interleaved log output from
+ // different requests.
+ logger.debug("Trying " + i + " as a factor.");
+
+ if ((n % i) == 0) {
+ logger.info("Found factor " + i);
+ factors.addElement(new Integer(i));
+
+ do {
+ n /= i;
+ } while ((n % i) == 0);
+ }
+
+ // Placing artificial delays in tight-loops will also lead to
+ // sub-optimal resuts. :-)
+ delay(100);
+ }
+
+ if (n != 1) {
+ logger.info("Found factor " + n);
+ factors.addElement(new Integer(n));
+ }
+
+ int len = factors.size();
+
+ int[] result = new int[len];
+
+ for (int i = 0; i < len; i++) {
+ result[i] = ((Integer) factors.elementAt(i)).intValue();
+ }
+
+ <b>// clean up
+ MDC.remove("client");
+ MDC.remove("number");</b>
+
+ return result;
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" +
+ " where configFile is a logback configuration file.");
+ System.exit(1);
+ }
+
+ public static void delay(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ usage("Wrong number of arguments.");
+ }
+
+ String configFile = args[0];
+
+ if (configFile.endsWith(".xml")) {
+ try {
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(lc);
+ lc.shutdownAndReset();
+ configurator.doConfigure(args[0]);
+ } catch (JoranException je) {
+ je.printStackTrace();
+ }
+ }
+
+ NumberCruncherServer ncs;
+
+ try {
+ ncs = new NumberCruncherServer();
+ logger.info("Creating registry.");
+
+ Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
+ registry.rebind("Factor", ncs);
+ logger.info("NumberCruncherServer bound and ready.");
+ } catch (Exception e) {
+ logger.error("Could not bind NumberCruncherServer.", e);
+
+ return;
+ }
+ }
+}</pre></div>
+
+ <p>
+ The implementation of the <code>factor(int number)</code> method is
+ of particular relevance. It starts by putting the client's hostname into the
+ <code>MDC</code> under the key <em>client</em>. The number to factor,
+ as requested by the client, is put into the <code>MDC</code> under the key
+ <em>number</em>. After computing the distinct factors of the integer
+ parameter, the result is returned to the client. Before returning the
+ result however, the values for the <em>client</em> and <em>number</em> are
+ cleared by calling the <code>MDC.remove(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><configuration>
+
+ <appender name="CONSOLE"
+ class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n</Pattern>
+ </layout>
+ </appender>
+
+ <root>
+ <level value ="debug"/>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+</configuration></pre></div>
+
+ <p>
+ Note the use of the <em>%X</em> conversion specifier within the
+ <span class="option">Pattern</span> option.
+ </p>
+
+ <p>
+ The following command starts an instance of <code>NumberCruncherClient</code>
+ application:
+ </p>
+
+<div class="source"><pre>java chapter7.NumberCruncherClient <em>hostname</em></pre></div>
+
+ <p>
+ where <em>hostname</em> is the host where the
+ <code>NumberCruncherServer</code> is running
+ </p>
+
+ <p>
+ Executing multiple instances of the client and requesting the server to factor
+ the numbers 129 from the first client and shortly thereafter
+ the number 71 from the second client, the server outputs the following (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