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