• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * <p>Bridge/route all JUL log records to the SLF4J API.</p>
43  * <p>Essentially, the idea is to install on the root logger an instance of
44  * <code>SLF4JBridgeHandler</code> as the sole JUL handler in the system. Subsequently, the
45  * SLF4JBridgeHandler instance will redirect all JUL log records are redirected
46  * to the SLF4J API based on the following mapping of levels:
47  * </p>
48  * <pre>
49  * FINEST  -&gt; TRACE
50  * FINER   -&gt; DEBUG
51  * FINE    -&gt; DEBUG
52  * INFO    -&gt; INFO
53  * WARNING -&gt; WARN
54  * SEVERE  -&gt; ERROR</pre>
55  * <p><b>Programmatic installation:</b></p>
56  * <pre>
57  * // Optionally remove existing handlers attached to j.u.l root logger
58  * SLF4JBridgeHandler.removeHandlersForRootLogger();  // (since SLF4J 1.6.5)
59 
60  * // add SLF4JBridgeHandler to j.u.l's root logger, should be done once during
61  * // the initialization phase of your application
62  * SLF4JBridgeHandler.install();</pre>
63  * <p><b>Installation via <em>logging.properties</em> configuration file:</b></p>
64  * <pre>
65  * // register SLF4JBridgeHandler as handler for the j.u.l. root logger
66  * handlers = org.slf4j.bridge.SLF4JBridgeHandler</pre>
67  * <p>Once SLF4JBridgeHandler is installed, logging by j.u.l. loggers will be directed to
68  * SLF4J. Example: </p>
69  * <pre>
70  * import  java.util.logging.Logger;
71  * ...
72  * // usual pattern: get a Logger and then log a message
73  * Logger julLogger = Logger.getLogger(&quot;org.wombat&quot;);
74  * julLogger.fine(&quot;hello world&quot;); // this will get redirected to SLF4J</pre>
75  *
76  * <p>Please note that translating a java.util.logging event into SLF4J incurs the
77  * cost of constructing {@link LogRecord} instance regardless of whether the
78  * SLF4J logger is disabled for the given level. <b>Consequently, j.u.l. to
79  * SLF4J translation can seriously increase the cost of disabled logging
80  * statements (60 fold or 6000% increase) and measurably impact the performance of enabled log
81  * statements (20% overall increase).</b> Please note that as of logback-version 0.9.25,
82  * it is possible to completely eliminate the 60 fold translation overhead for disabled
83  * log statements with the help of <a href="http://logback.qos.ch/manual/configuration.html#LevelChangePropagator">LevelChangePropagator</a>.
84  * </p>
85  *
86  * <p>If you are concerned about application performance, then use of <code>SLF4JBridgeHandler</code>
87  * is appropriate only if any one the following two conditions is true:</p>
88  * <ol>
89  * <li>few j.u.l. logging statements are in play</li>
90  * <li>LevelChangePropagator has been installed</li>
91  * </ol>
92  *
93  * @author Christian Stein
94  * @author Joern Huxhorn
95  * @author Ceki G&uuml;lc&uuml;
96  * @author Darryl Smith
97  * @since 1.5.1
98  */
99 public class SLF4JBridgeHandler extends Handler {
100 
101     // The caller is java.util.logging.Logger
102     private static final String FQCN = java.util.logging.Logger.class.getName();
103     private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger";
104 
105     private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
106     private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
107     private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
108     private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
109 
110     /**
111      * Adds a SLF4JBridgeHandler instance to jul's root logger.
112      * <p/>
113      * <p/>
114      * This handler will redirect j.u.l. logging to SLF4J. However, only logs enabled
115      * in j.u.l. will be redirected. For example, if a log statement invoking a
116      * j.u.l. logger is disabled, then the corresponding non-event will <em>not</em>
117      * reach SLF4JBridgeHandler and cannot be redirected.
118      */
install()119     public static void install() {
120         LogManager.getLogManager().getLogger("").addHandler(new SLF4JBridgeHandler());
121     }
122 
getRootLogger()123     private static java.util.logging.Logger getRootLogger() {
124         return LogManager.getLogManager().getLogger("");
125     }
126 
127     /**
128      * Removes previously installed SLF4JBridgeHandler instances. See also
129      * {@link #install()}.
130      *
131      * @throws SecurityException A <code>SecurityException</code> is thrown, if a security manager
132      *                           exists and if the caller does not have
133      *                           LoggingPermission("control").
134      */
uninstall()135     public static void uninstall() throws SecurityException {
136         java.util.logging.Logger rootLogger = getRootLogger();
137         Handler[] handlers = rootLogger.getHandlers();
138         for (int i = 0; i < handlers.length; i++) {
139             if (handlers[i] instanceof SLF4JBridgeHandler) {
140                 rootLogger.removeHandler(handlers[i]);
141             }
142         }
143     }
144 
145     /**
146      * Returns true if SLF4JBridgeHandler has been previously installed, returns false otherwise.
147      *
148      * @return true if SLF4JBridgeHandler is already installed, false other wise
149      * @throws SecurityException
150      */
isInstalled()151     public static boolean isInstalled() throws SecurityException {
152         java.util.logging.Logger rootLogger = getRootLogger();
153         Handler[] handlers = rootLogger.getHandlers();
154         for (int i = 0; i < handlers.length; i++) {
155             if (handlers[i] instanceof SLF4JBridgeHandler) {
156                 return true;
157             }
158         }
159         return false;
160     }
161 
162     /**
163      * Invoking this method removes/unregisters/detaches all handlers currently attached to the root logger
164      * @since 1.6.5
165      */
removeHandlersForRootLogger()166     public static void removeHandlersForRootLogger() {
167         java.util.logging.Logger rootLogger = getRootLogger();
168         java.util.logging.Handler[] handlers = rootLogger.getHandlers();
169         for (int i = 0; i < handlers.length; i++) {
170             rootLogger.removeHandler(handlers[i]);
171         }
172     }
173 
174     /**
175      * Initialize this handler.
176      */
SLF4JBridgeHandler()177     public SLF4JBridgeHandler() {
178     }
179 
180     /**
181      * No-op implementation.
182      */
close()183     public void close() {
184         // empty
185     }
186 
187     /**
188      * No-op implementation.
189      */
flush()190     public void flush() {
191         // empty
192     }
193 
194     /**
195      * Return the Logger instance that will be used for logging.
196      */
getSLF4JLogger(LogRecord record)197     protected Logger getSLF4JLogger(LogRecord record) {
198         String name = record.getLoggerName();
199         if (name == null) {
200             name = UNKNOWN_LOGGER_NAME;
201         }
202         return LoggerFactory.getLogger(name);
203     }
204 
callLocationAwareLogger(LocationAwareLogger lal, LogRecord record)205     protected void callLocationAwareLogger(LocationAwareLogger lal, LogRecord record) {
206         int julLevelValue = record.getLevel().intValue();
207         int slf4jLevel;
208 
209         if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
210             slf4jLevel = LocationAwareLogger.TRACE_INT;
211         } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
212             slf4jLevel = LocationAwareLogger.DEBUG_INT;
213         } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
214             slf4jLevel = LocationAwareLogger.INFO_INT;
215         } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
216             slf4jLevel = LocationAwareLogger.WARN_INT;
217         } else {
218             slf4jLevel = LocationAwareLogger.ERROR_INT;
219         }
220         String i18nMessage = getMessageI18N(record);
221         lal.log(null, FQCN, slf4jLevel, i18nMessage, null, record.getThrown());
222     }
223 
callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record)224     protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
225         String i18nMessage = getMessageI18N(record);
226         int julLevelValue = record.getLevel().intValue();
227         if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
228             slf4jLogger.trace(i18nMessage, record.getThrown());
229         } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
230             slf4jLogger.debug(i18nMessage, record.getThrown());
231         } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
232             slf4jLogger.info(i18nMessage, record.getThrown());
233         } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
234             slf4jLogger.warn(i18nMessage, record.getThrown());
235         } else {
236             slf4jLogger.error(i18nMessage, record.getThrown());
237         }
238     }
239 
240     /**
241      * Get the record's message, possibly via a resource bundle.
242      *
243      * @param record
244      * @return
245      */
getMessageI18N(LogRecord record)246     private String getMessageI18N(LogRecord record) {
247         String message = record.getMessage();
248 
249         if (message == null) {
250             return null;
251         }
252 
253         ResourceBundle bundle = record.getResourceBundle();
254         if (bundle != null) {
255             try {
256                 message = bundle.getString(message);
257             } catch (MissingResourceException e) {
258             }
259         }
260         Object[] params = record.getParameters();
261         // avoid formatting when there are no or 0 parameters. see also
262         // http://bugzilla.slf4j.org/show_bug.cgi?id=212
263         if (params != null && params.length > 0) {
264             message = MessageFormat.format(message, params);
265         }
266         return message;
267     }
268 
269     /**
270      * Publish a LogRecord.
271      * <p/>
272      * The logging request was made initially to a Logger object, which
273      * initialized the LogRecord and forwarded it here.
274      * <p/>
275      * This handler ignores the Level attached to the LogRecord, as SLF4J cares
276      * about discarding log statements.
277      *
278      * @param record Description of the log event. A null record is silently ignored
279      *               and is not published.
280      */
publish(LogRecord record)281     public void publish(LogRecord record) {
282         // Silently ignore null records.
283         if (record == null) {
284             return;
285         }
286 
287         Logger slf4jLogger = getSLF4JLogger(record);
288         String message = record.getMessage(); // can be null!
289         // this is a check to avoid calling the underlying logging system
290         // with a null message. While it is legitimate to invoke j.u.l. with
291         // a null message, other logging frameworks do not support this.
292         // see also http://bugzilla.slf4j.org/show_bug.cgi?id=108
293         if (message == null) {
294             message = "";
295         }
296         if (slf4jLogger instanceof LocationAwareLogger) {
297             callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
298         } else {
299             callPlainSLF4JLogger(slf4jLogger, record);
300         }
301     }
302 
303 }
304