SLF4JBridgeHandler.java revision 664a696f44a70d2799611d1d23d834935ae399ea
1/*
2 * Copyright (c) 2004-2008 QOS.ch
3 *
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to  deal in  the Software without  restriction, including
9 * without limitation  the rights to  use, copy, modify,  merge, publish,
10 * distribute, and/or sell copies of  the Software, and to permit persons
11 * to whom  the Software is furnished  to do so, provided  that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the  Software and  that both  the above  copyright notice(s)  and this
14 * permission notice appear in supporting documentation.
15 *
16 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF  THIRD PARTY  RIGHTS. IN  NO EVENT  SHALL THE  COPYRIGHT  HOLDER OR
20 * HOLDERS  INCLUDED IN  THIS  NOTICE BE  LIABLE  FOR ANY  CLAIM, OR  ANY
21 * SPECIAL INDIRECT  OR CONSEQUENTIAL DAMAGES, OR  ANY DAMAGES WHATSOEVER
22 * RESULTING FROM LOSS  OF USE, DATA OR PROFITS, WHETHER  IN AN ACTION OF
23 * CONTRACT, NEGLIGENCE  OR OTHER TORTIOUS  ACTION, ARISING OUT OF  OR IN
24 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 *
26 * Except as  contained in  this notice, the  name of a  copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
30 */
31
32package org.slf4j.bridge;
33
34import java.text.MessageFormat;
35import java.util.MissingResourceException;
36import java.util.ResourceBundle;
37import java.util.logging.Handler;
38import java.util.logging.Level;
39import java.util.logging.LogManager;
40import java.util.logging.LogRecord;
41
42import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44import org.slf4j.spi.LocationAwareLogger;
45
46// Based on http://bugzilla.slf4j.org/show_bug.cgi?id=38
47
48/**
49 * Bridge/route all JUL log records to the SLF4J API.
50 *
51 * <p>
52 * Essentially, the idea is to install on the root logger an instance of
53 * SLF4JBridgeHandler as the sole JUL handler in the system. Subsequently, the
54 * SLF4JBridgeHandler instance will redirect all JUL log records are redirected
55 * to the SLF4J API based on the following mapping of levels:
56 *
57 * <pre>
58 * FINEST  -&gt; TRACE
59 * FINER   -&gt; DEBUG
60 * FINE    -&gt; DEBUG
61 * INFO    -&gt; INFO
62 * WARNING -&gt; WARN
63 * SEVER   -&gt; ERROR
64 * </pre>
65 *
66 * Usage:
67 *
68 * <pre>
69 * // call only once during initialization time of your application
70 * SLF4JBridgeHandler.install();
71 *
72 * // usual pattern: get a Logger and then log a message
73 * java.util.logging.Logger julLogger = java.util.logging.Logger
74 *     .getLogger(&quot;org.wombat&quot;);
75 * julLogger.fine(&quot;hello world&quot;); // this will get redirected to SLF4J
76 * </pre>
77 *
78 * @author Christian Stein
79 * @author Joern Huxhorn
80 * @author Ceki G&uml;lc&uml;
81 * @author Darryl Smith
82 *
83 * @since 1.5.1
84 */
85public class SLF4JBridgeHandler extends Handler {
86
87  // The caller is java.util.logging.Logger
88  private static final String FQCN = java.util.logging.Logger.class.getName();
89  private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger";
90
91  private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
92  private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
93  private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
94  private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
95
96  /**
97   * Resets the entire JUL logging system and adds new SLF4JHandler instance to
98   * the root logger.
99   */
100  public static void install() {
101    LogManager.getLogManager().reset();
102    LogManager.getLogManager().getLogger("").addHandler(
103        new SLF4JBridgeHandler());
104  }
105
106  /**
107   * Rereads the JUL configuration.
108   *
109   * @see LogManager#readConfiguration();
110   * @throws Exception
111   *                 A <code>SecurityException</code> is thrown, if a security
112   *                 manager exists and if the caller does not have
113   *                 LoggingPermission("control"). <code>IOException</code> if
114   *                 there are IO problems reading the configuration.
115   */
116  public static void uninstall() throws Exception {
117    LogManager.getLogManager().readConfiguration();
118  }
119
120  /**
121   * Initialize this handler.
122   *
123   */
124  public SLF4JBridgeHandler() {
125  }
126
127  /**
128   * No-op implementation.
129   */
130  public void close() {
131    // empty
132  }
133
134  /**
135   * No-op implementation.
136   */
137  public void flush() {
138    // empty
139  }
140
141  /**
142   * Return the Logger instance that will be used for logging.
143   */
144  protected Logger getSLF4JLogger(LogRecord record) {
145    String name = record.getLoggerName();
146    if (name == null) {
147      name = UNKNOWN_LOGGER_NAME;
148    }
149    return LoggerFactory.getLogger(name);
150  }
151
152  protected void callLocationAwareLogger(LocationAwareLogger lal,
153      LogRecord record) {
154    int julLevelValue = record.getLevel().intValue();
155    int slf4jLevel;
156
157    if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
158      slf4jLevel = LocationAwareLogger.TRACE_INT;
159    } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
160      slf4jLevel = LocationAwareLogger.DEBUG_INT;
161    } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
162      slf4jLevel = LocationAwareLogger.INFO_INT;
163    } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
164      slf4jLevel = LocationAwareLogger.WARN_INT;
165    } else {
166      slf4jLevel = LocationAwareLogger.ERROR_INT;
167    }
168    String i18nMessage = getMessageI18N(record);
169    lal.log(null, FQCN, slf4jLevel, i18nMessage, record.getThrown());
170  }
171
172  protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
173    String i18nMessage = getMessageI18N(record);
174    int julLevelValue = record.getLevel().intValue();
175    if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
176      slf4jLogger.trace(i18nMessage, record.getThrown());
177    } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
178      slf4jLogger.debug(i18nMessage, record.getThrown());
179    } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
180      slf4jLogger.info(i18nMessage, record.getThrown());
181    } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
182      slf4jLogger.warn(i18nMessage, record.getThrown());
183    } else {
184      slf4jLogger.error(i18nMessage, record.getThrown());
185    }
186  }
187
188  /**
189   * Get the record's message, possibly via a resource bundle.
190   *
191   * @param record
192   * @return
193   */
194  private String getMessageI18N(LogRecord record) {
195    String message = record.getMessage();
196
197    if (message == null) {
198      return null;
199    }
200
201    ResourceBundle bundle = record.getResourceBundle();
202    if (bundle != null) {
203      try {
204        message = bundle.getString(message);
205      } catch (MissingResourceException e) {
206      }
207    }
208    Object[] params = record.getParameters();
209    if (params != null) {
210      message = MessageFormat.format(message, params);
211    }
212    return message;
213  }
214
215  /**
216   * Publish a LogRecord.
217   * <p>
218   * The logging request was made initially to a Logger object, which
219   * initialized the LogRecord and forwarded it here.
220   * <p>
221   * This handler ignores the Level attached to the LogRecord, as SLF4J cares
222   * about discarding log statements.
223   *
224   * @param record
225   *                Description of the log event. A null record is silently
226   *                ignored and is not published.
227   */
228  public void publish(LogRecord record) {
229    // Silently ignore null records.
230    if (record == null) {
231      return;
232    }
233
234    Logger slf4jLogger = getSLF4JLogger(record);
235    String message = record.getMessage(); // can be null!
236    // this is a check to avoid calling the underlying logging system
237    // with a null message
238    if (message == null) {
239      return;
240    }
241    if (slf4jLogger instanceof LocationAwareLogger) {
242      callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
243    } else {
244      callPlainSLF4JLogger(slf4jLogger, record);
245    }
246  }
247
248}
249