LoggerFactory 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;
    26
    27  import java.io.IOException;
    28  import java.net.URL;
    29  import java.util.*;
    30
    31  import org.slf4j.helpers.NOPLoggerFactory;
    32  import org.slf4j.helpers.SubstituteLoggerFactory;
    33  import org.slf4j.helpers.Util;
    34  import org.slf4j.impl.StaticLoggerBinder;
    35
    36  /**
    37   * The <code>LoggerFactory</code> is a utility class producing Loggers for
    38   * various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
    39   * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and
    40   * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported.
    41   * <p/>
    42   * <p/>
    43   * <code>LoggerFactory</code> is essentially a wrapper around an
    44   * {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
    45   * compile time.
    46   * <p/>
    47   * <p/>
    48   * Please note that all methods in <code>LoggerFactory</code> are static.
    49   *
    50   * @author Ceki G&uuml;lc&uuml;
    51   * @author Robert Elliot
    52   */
    53  public final class LoggerFactory {
    54
    55    static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
    56
    57    static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder";
    58    static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings";
    59    static final String NULL_LF_URL = CODES_PREFIX + "#null_LF";
    60    static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
    61    static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
    62
    63    static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
    64    static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also "
    65            + UNSUCCESSFUL_INIT_URL;
    66
    67    static final int UNINITIALIZED = 0;
    68    static final int ONGOING_INITIALIZATION = 1;
    69    static final int FAILED_INITIALIZATION = 2;
    70    static final int SUCCESSFUL_INITIALIZATION = 3;
    71    static final int NOP_FALLBACK_INITIALIZATION = 4;
    72
    73    static int INITIALIZATION_STATE = UNINITIALIZED;
    74    static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
    75    static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
    76
    77    /**
    78     * It is LoggerFactory's responsibility to track version changes and manage
    79     * the compatibility list.
    80     * <p/>
    81     * <p/>
    82     * It is assumed that all versions in the 1.6 are mutually compatible.
    83     */
    84    static private final String[] API_COMPATIBILITY_LIST = new String[]{"1.6", "1.7"};
    85
    86    // private constructor prevents instantiation
    87    private LoggerFactory() {
    88    }
    89
    90    /**
    91     * Force LoggerFactory to consider itself uninitialized.
    92     * <p/>
    93     * <p/>
    94     * This method is intended to be called by classes (in the same package) for
    95     * testing purposes. This method is internal. It can be modified, renamed or
    96     * removed at any time without notice.
    97     * <p/>
    98     * <p/>
    99     * You are strongly discouraged from calling this method in production code.
    100    */
    101   static void reset() {
    102     INITIALIZATION_STATE = UNINITIALIZED;
    103     TEMP_FACTORY = new SubstituteLoggerFactory();
    104   }
    105
    106   private final static void performInitialization() {
    107     bind();
    108     if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
    109       versionSanityCheck();
    110     }
    111   }
    112
    113   private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
    114     if (msg == null)
    115       return false;
    116     if (msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1)
    117       return true;
    118     if (msg.indexOf("org.slf4j.impl.StaticLoggerBinder") != -1)
    119       return true;
    120     return false;
    121   }
    122
    123   private final static void bind() {
    124     try {
    125       Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
    126       reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
    127       // the next line does the binding
    128       StaticLoggerBinder.getSingleton();
    129       INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
    130       reportActualBinding(staticLoggerBinderPathSet);
    131       emitSubstituteLoggerWarning();
    132     } catch (NoClassDefFoundError ncde) {
    133       String msg = ncde.getMessage();
    134       if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
    135         INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
    136         Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
    137         Util.report("Defaulting to no-operation (NOP) logger implementation");
    138         Util.report("See " + NO_STATICLOGGERBINDER_URL
    139                 + " for further details.");
    140       } else {
    141         failedBinding(ncde);
    142         throw ncde;
    143       }
    144     } catch (java.lang.NoSuchMethodError nsme) {
    145       String msg = nsme.getMessage();
    146       if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
    147         INITIALIZATION_STATE = FAILED_INITIALIZATION;
    148         Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
    149         Util.report("Your binding is version 1.5.5 or earlier.");
    150         Util.report("Upgrade your binding to version 1.6.x.");
    151       }
    152       throw nsme;
    153     } catch (Exception e) {
    154       failedBinding(e);
    155       throw new IllegalStateException("Unexpected initialization failure", e);
    156     }
    157   }
    158
    159   static void failedBinding(Throwable t) {
    160     INITIALIZATION_STATE = FAILED_INITIALIZATION;
    161     Util.report("Failed to instantiate SLF4J LoggerFactory", t);
    162   }
    163
    164   private final static void emitSubstituteLoggerWarning() {
    165     List loggerNameList = TEMP_FACTORY.getLoggerNameList();
    166     if (loggerNameList.size() == 0) {
    167       return;
    168     }
    169     Util.report("The following loggers will not work because they were created");
    170     Util.report("during the default configuration phase of the underlying logging system.");
    171     Util.report("See also " + SUBSTITUTE_LOGGER_URL);
    172     for (int i = 0; i < loggerNameList.size(); i++) {
    173       String loggerName = (String) loggerNameList.get(i);
    174       Util.report(loggerName);
    175     }
    176   }
    177
    178   private final static void versionSanityCheck() {
    179     try {
    180       String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
    181
    182       boolean match = false;
    183       for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
    184         if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
    185           match = true;
    186         }
    187       }
    188       if (!match) {
    189         Util.report("The requested version " + requested
    190                 + " by your slf4j binding is not compatible with "
    191                 + Arrays.asList(API_COMPATIBILITY_LIST).toString());
    192         Util.report("See " + VERSION_MISMATCH + " for further details.");
    193       }
    194     } catch (java.lang.NoSuchFieldError nsfe) {
    195       // given our large user base and SLF4J's commitment to backward
    196       // compatibility, we cannot cry here. Only for implementations
    197       // which willingly declare a REQUESTED_API_VERSION field do we
    198       // emit compatibility warnings.
    199     } catch (Throwable e) {
    200       // we should never reach here
    201       Util.report("Unexpected problem occured during version sanity check", e);
    202     }
    203   }
    204
    205   // We need to use the name of the StaticLoggerBinder class, but we can't reference
    206   // the class itself.
    207   private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
    208
    209   private static Set findPossibleStaticLoggerBinderPathSet() {
    210     // use Set instead of list in order to deal with  bug #138
    211     // LinkedHashSet appropriate here because it preserves insertion order during iteration
    212     Set staticLoggerBinderPathSet = new LinkedHashSet();
    213     try {
    214       ClassLoader loggerFactoryClassLoader = LoggerFactory.class
    215               .getClassLoader();
    216       Enumeration paths;
    217       if (loggerFactoryClassLoader == null) {
    218         paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
    219       } else {
    220         paths = loggerFactoryClassLoader
    221                 .getResources(STATIC_LOGGER_BINDER_PATH);
    222       }
    223       while (paths.hasMoreElements()) {
    224         URL path = (URL) paths.nextElement();
    225         staticLoggerBinderPathSet.add(path);
    226       }
    227     } catch (IOException ioe) {
    228       Util.report("Error getting resources from path", ioe);
    229     }
    230     return staticLoggerBinderPathSet;
    231   }
    232
    233   private static boolean isAmbiguousStaticLoggerBinderPathSet(Set staticLoggerBinderPathSet) {
    234     return staticLoggerBinderPathSet.size() > 1;
    235   }
    236
    237   /**
    238    * Prints a warning message on the console if multiple bindings were found on the class path.
    239    * No reporting is done otherwise.
    240    *
    241    */
    242   private static void reportMultipleBindingAmbiguity(Set staticLoggerBinderPathSet) {
    243     if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
    244       Util.report("Class path contains multiple SLF4J bindings.");
    245       Iterator iterator = staticLoggerBinderPathSet.iterator();
    246       while (iterator.hasNext()) {
    247         URL path = (URL) iterator.next();
    248         Util.report("Found binding in [" + path + "]");
    249       }
    250       Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
    251     }
    252   }
    253
    254   private static void reportActualBinding(Set staticLoggerBinderPathSet) {
    255     if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
    256       Util.report("Actual binding is of type ["+StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr()+"]");
    257     }
    258   }
    259
    260
    261   /**
    262    * Return a logger named according to the name parameter using the statically
    263    * bound {@link ILoggerFactory} instance.
    264    *
    265    * @param name The name of the logger.
    266    * @return logger
    267    */
    268   public static Logger getLogger(String name) {
    269     ILoggerFactory iLoggerFactory = getILoggerFactory();
    270     return iLoggerFactory.getLogger(name);
    271   }
    272
    273   /**
    274    * Return a logger named corresponding to the class passed as parameter, using
    275    * the statically bound {@link ILoggerFactory} instance.
    276    *
    277    * @param clazz the returned logger will be named after clazz
    278    * @return logger
    279    */
    280   public static Logger getLogger(Class clazz) {
    281     return getLogger(clazz.getName());
    282   }
    283
    284   /**
    285    * Return the {@link ILoggerFactory} instance in use.
    286    * <p/>
    287    * <p/>
    288    * ILoggerFactory instance is bound with this class at compile time.
    289    *
    290    * @return the ILoggerFactory instance in use
    291    */
    292   public static ILoggerFactory getILoggerFactory() {
    293     if (INITIALIZATION_STATE == UNINITIALIZED) {
    294       INITIALIZATION_STATE = ONGOING_INITIALIZATION;
    295       performInitialization();
    296     }
    297     switch (INITIALIZATION_STATE) {
    298       case SUCCESSFUL_INITIALIZATION:
    299         return StaticLoggerBinder.getSingleton().getLoggerFactory();
    300       case NOP_FALLBACK_INITIALIZATION:
    301         return NOP_FALLBACK_FACTORY;
    302       case FAILED_INITIALIZATION:
    303         throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    304       case ONGOING_INITIALIZATION:
    305         // support re-entrant behavior.
    306         // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
    307         return TEMP_FACTORY;
    308     }
    309     throw new IllegalStateException("Unreachable code");
    310   }
    311 }