View Javadoc

1   /**
2    * Copyright (c) 2004-2011 QOS.ch
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
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
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   *
24   */
25  package org.slf4j.bridge;
26  
27  import java.text.MessageFormat;
28  import java.util.MissingResourceException;
29  import java.util.ResourceBundle;
30  import java.util.logging.Handler;
31  import java.util.logging.Level;
32  import java.util.logging.LogManager;
33  import java.util.logging.LogRecord;
34  
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  import org.slf4j.spi.LocationAwareLogger;
38  
39  // Based on http://bugzilla.slf4j.org/show_bug.cgi?id=38
40  
41  /**
42   * Bridge/route all JUL log records to the SLF4J API.
43   * 
44   * <p>
45   * Essentially, the idea is to install on the root logger an instance of
46   * SLF4JBridgeHandler as the sole JUL handler in the system. Subsequently, the
47   * SLF4JBridgeHandler instance will redirect all JUL log records are redirected
48   * to the SLF4J API based on the following mapping of levels:
49   * 
50   * <pre>
51   * FINEST  -&gt; TRACE
52   * FINER   -&gt; DEBUG
53   * FINE    -&gt; DEBUG
54   * INFO    -&gt; INFO
55   * WARNING -&gt; WARN
56   * SEVER   -&gt; ERROR
57   * </pre>
58   * 
59   * Usage:
60   * 
61   * <pre>
62   * // call only once during initialization time of your application
63   * SLF4JBridgeHandler.install();
64   * 
65   * // usual pattern: get a Logger and then log a message
66   * java.util.logging.Logger julLogger = java.util.logging.Logger
67   *     .getLogger(&quot;org.wombat&quot;);
68   * julLogger.fine(&quot;hello world&quot;); // this will get redirected to SLF4J
69   * </pre>
70   * 
71   * <p>
72   * Please note that translating a java.util.logging event into SLF4J incurs the
73   * cost of constructing {@link LogRecord} instance regardless of whether the
74   * SLF4J logger is disabled for the given level. <b>Consequently, j.u.l. to
75   * SLF4J translation can seriously impact on the cost of disabled logging
76   * statements (60 fold increase) and a measurable impact on enabled log
77   * statements (20% overall increase). </b>
78   * </p>
79   * 
80   * <p>
81   * If application performance is a concern, then use of SLF4JBridgeHandler is
82   * appropriate only if few j.u.l. logging statements are in play.
83   * 
84   * @author Christian Stein
85   * @author Joern Huxhorn
86   * @author Ceki G&uuml;lc&uuml;
87   * @author Darryl Smith
88   * 
89   * @since 1.5.1
90   */
91  public class SLF4JBridgeHandler extends Handler {
92  
93    // The caller is java.util.logging.Logger
94    private static final String FQCN = java.util.logging.Logger.class.getName();
95    private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger";
96  
97    private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
98    private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
99    private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
100   private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
101 
102   /**
103    * Adds a SLF4JBridgeHandler instance to jul's root logger.
104    * 
105    * <p>
106    * This handler will redirect jul logging to SLF4J. However, only logs enabled
107    * in j.u.l. will be redirected. For example, if a log statement invoking a
108    * j.u.l. logger disabled that statement, by definition, will <em>not</em>
109    * reach any SLF4JBridgeHandler instance and cannot be redirected.
110    */
111   public static void install() {
112     LogManager.getLogManager().getLogger("").addHandler(
113         new SLF4JBridgeHandler());
114   }
115 
116   /**
117    * Removes previously installed SLF4JBridgeHandler instances. See also
118    * {@link #install()}.
119    * 
120    * @throws SecurityException
121    *           A <code>SecurityException</code> is thrown, if a security manager
122    *           exists and if the caller does not have
123    *           LoggingPermission("control").
124    */
125   public static void uninstall() throws SecurityException {
126     java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger(
127         "");
128     Handler[] handlers = rootLogger.getHandlers();
129     for (int i = 0; i < handlers.length; i++) {
130       if (handlers[i] instanceof SLF4JBridgeHandler) {
131         rootLogger.removeHandler(handlers[i]);
132       }
133     }
134   }
135 
136     /**
137      * Returns true if SLF4JBridgeHandler has been previously installed, returns false otherwise.
138      * @return
139      * @throws SecurityException
140      */
141     public static boolean isInstalled() throws SecurityException {
142         java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger(
143             "");
144         Handler[] handlers = rootLogger.getHandlers();
145         for (int i = 0; i < handlers.length; i++) {
146           if (handlers[i] instanceof SLF4JBridgeHandler) {
147             return true;
148           }
149         }
150         return false;
151       }
152 
153 
154   /**
155    * Initialize this handler.
156    * 
157    */
158   public SLF4JBridgeHandler() {
159   }
160 
161   /**
162    * No-op implementation.
163    */
164   public void close() {
165     // empty
166   }
167 
168   /**
169    * No-op implementation.
170    */
171   public void flush() {
172     // empty
173   }
174 
175   /**
176    * Return the Logger instance that will be used for logging.
177    */
178   protected Logger getSLF4JLogger(LogRecord record) {
179     String name = record.getLoggerName();
180     if (name == null) {
181       name = UNKNOWN_LOGGER_NAME;
182     }
183     return LoggerFactory.getLogger(name);
184   }
185 
186   protected void callLocationAwareLogger(LocationAwareLogger lal,
187       LogRecord record) {
188     int julLevelValue = record.getLevel().intValue();
189     int slf4jLevel;
190 
191     if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
192       slf4jLevel = LocationAwareLogger.TRACE_INT;
193     } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
194       slf4jLevel = LocationAwareLogger.DEBUG_INT;
195     } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
196       slf4jLevel = LocationAwareLogger.INFO_INT;
197     } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
198       slf4jLevel = LocationAwareLogger.WARN_INT;
199     } else {
200       slf4jLevel = LocationAwareLogger.ERROR_INT;
201     }
202     String i18nMessage = getMessageI18N(record);
203     lal.log(null, FQCN, slf4jLevel, i18nMessage, null, record.getThrown());
204   }
205 
206   protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
207     String i18nMessage = getMessageI18N(record);
208     int julLevelValue = record.getLevel().intValue();
209     if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
210       slf4jLogger.trace(i18nMessage, record.getThrown());
211     } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
212       slf4jLogger.debug(i18nMessage, record.getThrown());
213     } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
214       slf4jLogger.info(i18nMessage, record.getThrown());
215     } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
216       slf4jLogger.warn(i18nMessage, record.getThrown());
217     } else {
218       slf4jLogger.error(i18nMessage, record.getThrown());
219     }
220   }
221 
222   /**
223    * Get the record's message, possibly via a resource bundle.
224    * 
225    * @param record
226    * @return
227    */
228   private String getMessageI18N(LogRecord record) {
229     String message = record.getMessage();
230 
231     if (message == null) {
232       return null;
233     }
234 
235     ResourceBundle bundle = record.getResourceBundle();
236     if (bundle != null) {
237       try {
238         message = bundle.getString(message);
239       } catch (MissingResourceException e) {
240       }
241     }
242     Object[] params = record.getParameters();
243     if (params != null) {
244       message = MessageFormat.format(message, params);
245     }
246     return message;
247   }
248 
249   /**
250    * Publish a LogRecord.
251    * <p>
252    * The logging request was made initially to a Logger object, which
253    * initialized the LogRecord and forwarded it here.
254    * <p>
255    * This handler ignores the Level attached to the LogRecord, as SLF4J cares
256    * about discarding log statements.
257    * 
258    * @param record
259    *          Description of the log event. A null record is silently ignored
260    *          and is not published.
261    */
262   public void publish(LogRecord record) {
263     // Silently ignore null records.
264     if (record == null) {
265       return;
266     }
267 
268     Logger slf4jLogger = getSLF4JLogger(record);
269     String message = record.getMessage(); // can be null!
270     // this is a check to avoid calling the underlying logging system
271     // with a null message. While it is legitimate to invoke j.u.l. with
272     // a null message, other logging frameworks do not support this.
273     // see also http://bugzilla.slf4j.org/show_bug.cgi?id=108
274     if (message == null) {
275       message = "";
276     }
277     if (slf4jLogger instanceof LocationAwareLogger) {
278       callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
279     } else {
280       callPlainSLF4JLogger(slf4jLogger, record);
281     }
282   }
283 
284 }