SLF4JBridgeHandler xref

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   * <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    */
    119   public static void install() {
    120     LogManager.getLogManager().getLogger("").addHandler(
    121             new SLF4JBridgeHandler());
    122   }
    123
    124   private static java.util.logging.Logger getRootLogger() {
    125     return LogManager.getLogManager().getLogger("");
    126   }
    127
    128   /**
    129    * Removes previously installed SLF4JBridgeHandler instances. See also
    130    * {@link #install()}.
    131    *
    132    * @throws SecurityException A <code>SecurityException</code> is thrown, if a security manager
    133    *                           exists and if the caller does not have
    134    *                           LoggingPermission("control").
    135    */
    136   public static void uninstall() throws SecurityException {
    137     java.util.logging.Logger rootLogger = getRootLogger();
    138     Handler[] handlers = rootLogger.getHandlers();
    139     for (int i = 0; i < handlers.length; i++) {
    140       if (handlers[i] instanceof SLF4JBridgeHandler) {
    141         rootLogger.removeHandler(handlers[i]);
    142       }
    143     }
    144   }
    145
    146   /**
    147    * Returns true if SLF4JBridgeHandler has been previously installed, returns false otherwise.
    148    *
    149    * @return true if SLF4JBridgeHandler is already installed, false other wise
    150    * @throws SecurityException
    151    */
    152   public static boolean isInstalled() throws SecurityException {
    153     java.util.logging.Logger rootLogger = getRootLogger();
    154     Handler[] handlers = rootLogger.getHandlers();
    155     for (int i = 0; i < handlers.length; i++) {
    156       if (handlers[i] instanceof SLF4JBridgeHandler) {
    157         return true;
    158       }
    159     }
    160     return false;
    161   }
    162
    163   /**
    164    * Invoking this method removes/unregisters/detaches all handlers currently attached to the root logger
    165    * @since 1.6.5
    166    */
    167   public static void removeHandlersForRootLogger() {
    168     java.util.logging.Logger rootLogger =  getRootLogger();
    169     java.util.logging.Handler[] handlers = rootLogger.getHandlers();
    170     for (int i = 0; i < handlers.length; i++) {
    171       rootLogger.removeHandler(handlers[i]);
    172     }
    173   }
    174
    175
    176   /**
    177    * Initialize this handler.
    178    */
    179   public SLF4JBridgeHandler() {
    180   }
    181
    182   /**
    183    * No-op implementation.
    184    */
    185   public void close() {
    186     // empty
    187   }
    188
    189   /**
    190    * No-op implementation.
    191    */
    192   public void flush() {
    193     // empty
    194   }
    195
    196   /**
    197    * Return the Logger instance that will be used for logging.
    198    */
    199   protected Logger getSLF4JLogger(LogRecord record) {
    200     String name = record.getLoggerName();
    201     if (name == null) {
    202       name = UNKNOWN_LOGGER_NAME;
    203     }
    204     return LoggerFactory.getLogger(name);
    205   }
    206
    207   protected void callLocationAwareLogger(LocationAwareLogger lal,
    208                                          LogRecord record) {
    209     int julLevelValue = record.getLevel().intValue();
    210     int slf4jLevel;
    211
    212     if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
    213       slf4jLevel = LocationAwareLogger.TRACE_INT;
    214     } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
    215       slf4jLevel = LocationAwareLogger.DEBUG_INT;
    216     } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
    217       slf4jLevel = LocationAwareLogger.INFO_INT;
    218     } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
    219       slf4jLevel = LocationAwareLogger.WARN_INT;
    220     } else {
    221       slf4jLevel = LocationAwareLogger.ERROR_INT;
    222     }
    223     String i18nMessage = getMessageI18N(record);
    224     lal.log(null, FQCN, slf4jLevel, i18nMessage, null, record.getThrown());
    225   }
    226
    227   protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
    228     String i18nMessage = getMessageI18N(record);
    229     int julLevelValue = record.getLevel().intValue();
    230     if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
    231       slf4jLogger.trace(i18nMessage, record.getThrown());
    232     } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
    233       slf4jLogger.debug(i18nMessage, record.getThrown());
    234     } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
    235       slf4jLogger.info(i18nMessage, record.getThrown());
    236     } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
    237       slf4jLogger.warn(i18nMessage, record.getThrown());
    238     } else {
    239       slf4jLogger.error(i18nMessage, record.getThrown());
    240     }
    241   }
    242
    243
    244   /**
    245    * Get the record's message, possibly via a resource bundle.
    246    *
    247    * @param record
    248    * @return
    249    */
    250   private String getMessageI18N(LogRecord record) {
    251     String message = record.getMessage();
    252
    253     if (message == null) {
    254       return null;
    255     }
    256
    257     ResourceBundle bundle = record.getResourceBundle();
    258     if (bundle != null) {
    259       try {
    260         message = bundle.getString(message);
    261       } catch (MissingResourceException e) {
    262       }
    263     }
    264     Object[] params = record.getParameters();
    265     // avoid formatting when there are no or 0 parameters. see also
    266     // http://bugzilla.slf4j.org/show_bug.cgi?id=212
    267     if (params != null && params.length > 0) {
    268       message = MessageFormat.format(message, params);
    269     }
    270     return message;
    271   }
    272
    273   /**
    274    * Publish a LogRecord.
    275    * <p/>
    276    * The logging request was made initially to a Logger object, which
    277    * initialized the LogRecord and forwarded it here.
    278    * <p/>
    279    * This handler ignores the Level attached to the LogRecord, as SLF4J cares
    280    * about discarding log statements.
    281    *
    282    * @param record Description of the log event. A null record is silently ignored
    283    *               and is not published.
    284    */
    285   public void publish(LogRecord record) {
    286     // Silently ignore null records.
    287     if (record == null) {
    288       return;
    289     }
    290
    291     Logger slf4jLogger = getSLF4JLogger(record);
    292     String message = record.getMessage(); // can be null!
    293     // this is a check to avoid calling the underlying logging system
    294     // with a null message. While it is legitimate to invoke j.u.l. with
    295     // a null message, other logging frameworks do not support this.
    296     // see also http://bugzilla.slf4j.org/show_bug.cgi?id=108
    297     if (message == null) {
    298       message = "";
    299     }
    300     if (slf4jLogger instanceof LocationAwareLogger) {
    301       callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
    302     } else {
    303       callPlainSLF4JLogger(slf4jLogger, record);
    304     }
    305   }
    306
    307 }