SLF4JBridgeHandler.java revision 6f9b373977390c1e094d98caa83ee9ea13ffcea7
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.io.IOException;
35import java.text.MessageFormat;
36import java.util.MissingResourceException;
37import java.util.ResourceBundle;
38import java.util.logging.Handler;
39import java.util.logging.Level;
40import java.util.logging.LogManager;
41import java.util.logging.LogRecord;
42
43import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45import org.slf4j.spi.LocationAwareLogger;
46
47// Based on http://bugzilla.slf4j.org/show_bug.cgi?id=38
48
49/**
50 * Bridge/route all JUL log records to the SLF4J API.
51 *
52 * <p>
53 * Essentially, the idea is to install on the root logger an instance of
54 * SLF4JBridgeHandler as the sole JUL handler in the system. Subsequently, the
55 * SLF4JBridgeHandler instance will redirect all JUL log records are redirected
56 * to the SLF4J API based on the following mapping of levels:
57 *
58 * <pre>
59 * FINEST  -&gt; TRACE
60 * FINER   -&gt; DEBUG
61 * FINE    -&gt; DEBUG
62 * INFO    -&gt; INFO
63 * WARNING -&gt; WARN
64 * SEVER   -&gt; ERROR
65 * </pre>
66 *
67 * Usage:
68 *
69 * <pre>
70 * // call only once during initialization time of your application
71 * SLF4JBridgeHandler.install();
72 *
73 * // usual pattern: get a Logger and then log a message
74 * java.util.logging.Logger julLogger = java.util.logging.Logger
75 *     .getLogger(&quot;org.wombat&quot;);
76 * julLogger.fine(&quot;hello world&quot;); // this will get redirected to SLF4J
77 * </pre>
78 *
79 * @author Christian Stein
80 * @author Joern Huxhorn
81 * @author Ceki G&uuml;lc&uuml;
82 * @author Darryl Smith
83 *
84 * @since 1.5.1
85 */
86public class SLF4JBridgeHandler extends Handler {
87
88  // The caller is java.util.logging.Logger
89  private static final String FQCN = java.util.logging.Logger.class.getName();
90  private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger";
91
92  private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
93  private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
94  private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
95  private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
96
97  /**
98   * Resets the entire JUL logging system and adds new SLF4JHandler instance to
99   * the root logger.
100   */
101  public static void install() {
102    LogManager.getLogManager().reset();
103    LogManager.getLogManager().getLogger("").addHandler(
104        new SLF4JBridgeHandler());
105  }
106
107  /**
108   * Rereads the JUL configuration.
109   *
110   * @see LogManager#readConfiguration()
111   *
112   * @throws IOException
113   *                 <code>IOException</code> if there are IO problems reading
114   *                 the configuration.
115   * @throws SecurityException
116   *                 A <code>SecurityException</code> is thrown, if a security
117   *                 manager exists and if the caller does not have
118   *                 LoggingPermission("control").
119   */
120  public static void uninstall() throws SecurityException, IOException {
121    LogManager.getLogManager().readConfiguration();
122  }
123
124  /**
125   * Initialize this handler.
126   *
127   */
128  public SLF4JBridgeHandler() {
129  }
130
131  /**
132   * No-op implementation.
133   */
134  public void close() {
135    // empty
136  }
137
138  /**
139   * No-op implementation.
140   */
141  public void flush() {
142    // empty
143  }
144
145  /**
146   * Return the Logger instance that will be used for logging.
147   */
148  protected Logger getSLF4JLogger(LogRecord record) {
149    String name = record.getLoggerName();
150    if (name == null) {
151      name = UNKNOWN_LOGGER_NAME;
152    }
153    return LoggerFactory.getLogger(name);
154  }
155
156  protected void callLocationAwareLogger(LocationAwareLogger lal,
157      LogRecord record) {
158    int julLevelValue = record.getLevel().intValue();
159    int slf4jLevel;
160
161    if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
162      slf4jLevel = LocationAwareLogger.TRACE_INT;
163    } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
164      slf4jLevel = LocationAwareLogger.DEBUG_INT;
165    } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
166      slf4jLevel = LocationAwareLogger.INFO_INT;
167    } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
168      slf4jLevel = LocationAwareLogger.WARN_INT;
169    } else {
170      slf4jLevel = LocationAwareLogger.ERROR_INT;
171    }
172    String i18nMessage = getMessageI18N(record);
173    lal.log(null, FQCN, slf4jLevel, i18nMessage, record.getThrown());
174  }
175
176  protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
177    String i18nMessage = getMessageI18N(record);
178    int julLevelValue = record.getLevel().intValue();
179    if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
180      slf4jLogger.trace(i18nMessage, record.getThrown());
181    } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
182      slf4jLogger.debug(i18nMessage, record.getThrown());
183    } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
184      slf4jLogger.info(i18nMessage, record.getThrown());
185    } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
186      slf4jLogger.warn(i18nMessage, record.getThrown());
187    } else {
188      slf4jLogger.error(i18nMessage, record.getThrown());
189    }
190  }
191
192  /**
193   * Get the record's message, possibly via a resource bundle.
194   *
195   * @param record
196   * @return
197   */
198  private String getMessageI18N(LogRecord record) {
199    String message = record.getMessage();
200
201    if (message == null) {
202      return null;
203    }
204
205    ResourceBundle bundle = record.getResourceBundle();
206    if (bundle != null) {
207      try {
208        message = bundle.getString(message);
209      } catch (MissingResourceException e) {
210      }
211    }
212    Object[] params = record.getParameters();
213    if (params != null) {
214      message = MessageFormat.format(message, params);
215    }
216    return message;
217  }
218
219  /**
220   * Publish a LogRecord.
221   * <p>
222   * The logging request was made initially to a Logger object, which
223   * initialized the LogRecord and forwarded it here.
224   * <p>
225   * This handler ignores the Level attached to the LogRecord, as SLF4J cares
226   * about discarding log statements.
227   *
228   * @param record
229   *                Description of the log event. A null record is silently
230   *                ignored and is not published.
231   */
232  public void publish(LogRecord record) {
233    // Silently ignore null records.
234    if (record == null) {
235      return;
236    }
237
238    Logger slf4jLogger = getSLF4JLogger(record);
239    String message = record.getMessage(); // can be null!
240    // this is a check to avoid calling the underlying logging system
241    // with a null message
242    if (message == null) {
243      return;
244    }
245    if (slf4jLogger instanceof LocationAwareLogger) {
246      callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
247    } else {
248      callPlainSLF4JLogger(slf4jLogger, record);
249    }
250  }
251
252}
253