View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j;
    18
    19  import java.net.URI;
    20  import java.util.Map;
    21  import java.util.SortedMap;
    22  import java.util.TreeMap;
    23
    24  import org.apache.logging.log4j.message.MessageFactory;
    25  import org.apache.logging.log4j.message.StringFormatterMessageFactory;
    26  import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
    27  import org.apache.logging.log4j.spi.LoggerContext;
    28  import org.apache.logging.log4j.spi.LoggerContextFactory;
    29  import org.apache.logging.log4j.spi.Provider;
    30  import org.apache.logging.log4j.status.StatusLogger;
    31  import org.apache.logging.log4j.util.LoaderUtil;
    32  import org.apache.logging.log4j.util.PropertiesUtil;
    33  import org.apache.logging.log4j.util.ProviderUtil;
    34  import org.apache.logging.log4j.util.ReflectionUtil;
    35  import org.apache.logging.log4j.util.Strings;
    36
    37  /**
    38   * The anchor point for the logging system. The most common usage of this class is to obtain a named
    39   * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger
    40   * based on the calling class name. This class also provides method for obtaining named Loggers that use
    41   * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages.
    42   * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods
    43   * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not
    44   * normally useful for typical usage of Log4j.
    45   */
    46  public class LogManager {
    47
    48      private static volatile LoggerContextFactory factory;
    49
    50      /**
    51       * Log4j property to set to the fully qualified class name of a custom implementation of
    52       * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
    53       */
    54      public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
    55
    56      private static final Logger LOGGER = StatusLogger.getLogger();
    57
    58      /**
    59       * The name of the root Logger.
    60       */
    61      public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
    62
    63      // for convenience
    64      private static final String FQCN = LogManager.class.getName();
    65
    66      /**
    67       * Scans the classpath to find all logging implementation. Currently, only one will
    68       * be used but this could be extended to allow multiple implementations to be used.
    69       */
    70      static {
    71          // Shortcut binding to force a specific logging implementation.
    72          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
    73          final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
    74          if (factoryClassName != null) {
    75              try {
    76                  final Class<?> clazz = LoaderUtil.loadClass(factoryClassName);
    77                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
    78                      factory = clazz.asSubclass(LoggerContextFactory.class).newInstance();
    79                  }
    80              } catch (final ClassNotFoundException cnfe) {
    81                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
    82              } catch (final Exception ex) {
    83                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
    84              }
    85          }
    86
    87          if (factory == null) {
    88              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
    89              // note that the following initial call to ProviderUtil may block until a Provider has been installed when
    90              // running in an OSGi environment
    91              if (ProviderUtil.hasProviders()) {
    92                  for (final Provider provider : ProviderUtil.getProviders()) {
    93                      final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
    94                      if (factoryClass != null) {
    95                          try {
    96                              factories.put(provider.getPriority(), factoryClass.newInstance());
    97                          } catch (final Exception e) {
    98                              LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
    99                                  provider.getUrl().toString(), e);
    100                         }
    101                     }
    102                 }
    103
    104                 if (factories.isEmpty()) {
    105                     LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
    106                     factory = new SimpleLoggerContextFactory();
    107                 } else {
    108                     final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
    109                     for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
    110                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
    111                         sb.append(", Weighting: ").append(entry.getKey()).append('\n');
    112                     }
    113                     factory = factories.get(factories.lastKey());
    114                     sb.append("Using factory: ").append(factory.getClass().getName());
    115                     LOGGER.warn(sb.toString());
    116
    117                 }
    118             } else {
    119                 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
    120                 factory = new SimpleLoggerContextFactory();
    121             }
    122         }
    123     }
    124
    125     /**
    126      * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
    127      *
    128      * @param name
    129      *            The Logger name to search for.
    130      * @return true if the Logger exists, false otherwise.
    131      * @see LoggerContext#hasLogger(String)
    132      */
    133     public static boolean exists(final String name) {
    134         return getContext().hasLogger(name);
    135     }
    136
    137     /**
    138      * Returns the current LoggerContext.
    139      * <p>
    140      * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
    141      * for the calling class.
    142      * </p>
    143      * @return  The current LoggerContext.
    144      */
    145     public static LoggerContext getContext() {
    146         return factory.getContext(FQCN, null, null, true);
    147     }
    148
    149     /**
    150      * Returns a LoggerContext.
    151      *
    152      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    153      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    154      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    155      * returned. If true then only a single LoggerContext will be returned.
    156      * @return a LoggerContext.
    157      */
    158     public static LoggerContext getContext(final boolean currentContext) {
    159         // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
    160         return factory.getContext(FQCN, null, null, currentContext, null, null);
    161     }
    162
    163     /**
    164      * Returns a LoggerContext.
    165      *
    166      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    167      * ClassLoader.
    168      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    169      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    170      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    171      * returned. If true then only a single LoggerContext will be returned.
    172      * @return a LoggerContext.
    173      */
    174     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
    175         return factory.getContext(FQCN, loader, null, currentContext);
    176     }
    177
    178     /**
    179      * Returns a LoggerContext.
    180      *
    181      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    182      * ClassLoader.
    183      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    184      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    185      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    186      * returned. If true then only a single LoggerContext will be returned.
    187      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    188      * @return a LoggerContext.
    189      */
    190     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    191                                            final Object externalContext) {
    192         return factory.getContext(FQCN, loader, externalContext, currentContext);
    193     }
    194
    195     /**
    196      * Returns a LoggerContext.
    197      *
    198      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    199      * ClassLoader.
    200      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    201      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    202      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    203      * returned. If true then only a single LoggerContext will be returned.
    204      * @param configLocation The URI for the configuration to use.
    205      * @return a LoggerContext.
    206      */
    207     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    208                                            final URI configLocation) {
    209         return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
    210     }
    211
    212
    213     /**
    214      * Returns a LoggerContext.
    215      *
    216      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    217      * ClassLoader.
    218      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    219      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    220      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    221      * returned. If true then only a single LoggerContext will be returned.
    222      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    223      * @param configLocation The URI for the configuration to use.
    224      * @return a LoggerContext.
    225      */
    226     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    227                                            final Object externalContext, final URI configLocation) {
    228         return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
    229     }
    230
    231
    232     /**
    233      * Returns a LoggerContext.
    234      *
    235      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    236      * ClassLoader.
    237      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    238      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    239      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    240      * returned. If true then only a single LoggerContext will be returned.
    241      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    242      * @param configLocation The URI for the configuration to use.
    243      * @param name The LoggerContext name.
    244      * @return a LoggerContext.
    245      */
    246     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    247                                            final Object externalContext, final URI configLocation,
    248                                            final String name) {
    249         return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
    250     }
    251
    252     /**
    253      * Returns a LoggerContext
    254      * @param fqcn The fully qualified class name of the Class that this method is a member of.
    255      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    256      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    257      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    258      * returned. If true then only a single LoggerContext will be returned.
    259      * @return a LoggerContext.
    260      */
    261     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
    262         return factory.getContext(fqcn, null, null, currentContext);
    263     }
    264
    265     /**
    266      * Returns a LoggerContext
    267      * @param fqcn The fully qualified class name of the Class that this method is a member of.
    268      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    269      * ClassLoader.
    270      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    271      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    272      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    273      * returned. If true then only a single LoggerContext will be returned.
    274      * @return a LoggerContext.
    275      */
    276     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
    277                                               final boolean currentContext) {
    278         return factory.getContext(fqcn, loader, null, currentContext);
    279     }
    280
    281     /**
    282      * Returns the current LoggerContextFactory.
    283      * @return The LoggerContextFactory.
    284      */
    285     public static LoggerContextFactory getFactory() {
    286         return factory;
    287     }
    288
    289     /**
    290      * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
    291      * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
    292      * Thus, an alternative LoggerContextFactory can be set at runtime.
    293      *
    294      * <p>
    295      * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
    296      * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
    297      * Generally, this method should be used only during startup before any code starts caching Logger objects.
    298      * </p>
    299      *
    300      * @param factory the LoggerContextFactory to use.
    301      */
    302     // FIXME: should we allow only one update of the factory?
    303     public static void setFactory(final LoggerContextFactory factory) {
    304         LogManager.factory = factory;
    305     }
    306
    307     /**
    308      * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
    309      * <p>
    310      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    311      * </p>
    312      * <p>
    313      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
    314      * </p>
    315      *
    316      * @param clazz
    317      *            The Class whose name should be used as the Logger name.
    318      * @return The Logger, created with a {@link StringFormatterMessageFactory}
    319      * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    320      * @see Logger#fatal(Marker, String, Object...)
    321      * @see Logger#fatal(String, Object...)
    322      * @see Logger#error(Marker, String, Object...)
    323      * @see Logger#error(String, Object...)
    324      * @see Logger#warn(Marker, String, Object...)
    325      * @see Logger#warn(String, Object...)
    326      * @see Logger#info(Marker, String, Object...)
    327      * @see Logger#info(String, Object...)
    328      * @see Logger#debug(Marker, String, Object...)
    329      * @see Logger#debug(String, Object...)
    330      * @see Logger#trace(Marker, String, Object...)
    331      * @see Logger#trace(String, Object...)
    332      * @see StringFormatterMessageFactory
    333      */
    334     public static Logger getFormatterLogger(final Class<?> clazz) {
    335         return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2),
    336             StringFormatterMessageFactory.INSTANCE);
    337     }
    338
    339     /**
    340      * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
    341      * <p>
    342      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    343      * </p>
    344      * <p>
    345      * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
    346      * </p>
    347      *
    348      * @param value
    349      *            The value's whose class name should be used as the Logger name.
    350      * @return The Logger, created with a {@link StringFormatterMessageFactory}
    351      * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    352      * @see Logger#fatal(Marker, String, Object...)
    353      * @see Logger#fatal(String, Object...)
    354      * @see Logger#error(Marker, String, Object...)
    355      * @see Logger#error(String, Object...)
    356      * @see Logger#warn(Marker, String, Object...)
    357      * @see Logger#warn(String, Object...)
    358      * @see Logger#info(Marker, String, Object...)
    359      * @see Logger#info(String, Object...)
    360      * @see Logger#debug(Marker, String, Object...)
    361      * @see Logger#debug(String, Object...)
    362      * @see Logger#trace(Marker, String, Object...)
    363      * @see Logger#trace(String, Object...)
    364      * @see StringFormatterMessageFactory
    365      */
    366     public static Logger getFormatterLogger(final Object value) {
    367         return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2),
    368             StringFormatterMessageFactory.INSTANCE);
    369     }
    370
    371     /**
    372      * Returns a formatter Logger with the specified name.
    373      * <p>
    374      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    375      * </p>
    376      * <p>
    377      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
    378      * </p>
    379      *
    380      * @param name The logger name. If null it will default to the name of the calling class.
    381      * @return The Logger, created with a {@link StringFormatterMessageFactory}
    382      * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    383      * @see Logger#fatal(Marker, String, Object...)
    384      * @see Logger#fatal(String, Object...)
    385      * @see Logger#error(Marker, String, Object...)
    386      * @see Logger#error(String, Object...)
    387      * @see Logger#warn(Marker, String, Object...)
    388      * @see Logger#warn(String, Object...)
    389      * @see Logger#info(Marker, String, Object...)
    390      * @see Logger#info(String, Object...)
    391      * @see Logger#debug(Marker, String, Object...)
    392      * @see Logger#debug(String, Object...)
    393      * @see Logger#trace(Marker, String, Object...)
    394      * @see Logger#trace(String, Object...)
    395      * @see StringFormatterMessageFactory
    396      */
    397     public static Logger getFormatterLogger(final String name) {
    398         return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name,
    399             StringFormatterMessageFactory.INSTANCE);
    400     }
    401
    402     /**
    403      * Returns a Logger with the name of the calling class.
    404      * @return The Logger for the calling class.
    405      * @throws UnsupportedOperationException if the calling class cannot be determined.
    406      */
    407     public static Logger getLogger() {
    408         return getLogger(ReflectionUtil.getCallerClass(2));
    409     }
    410
    411     /**
    412      * Returns a Logger using the fully qualified name of the Class as the Logger name.
    413      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
    414      *              class.
    415      * @return The Logger.
    416      * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    417      */
    418     public static Logger getLogger(final Class<?> clazz) {
    419         if (clazz == null) {
    420             final Class<?> candidate = ReflectionUtil.getCallerClass(2);
    421             if (candidate == null) {
    422                 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
    423             }
    424             return getLogger(candidate);
    425         }
    426         return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName());
    427     }
    428
    429     /**
    430      * Returns a Logger using the fully qualified name of the Class as the Logger name.
    431      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
    432      *              class.
    433      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    434      *                       the logger but will log a warning if mismatched.
    435      * @return The Logger.
    436      * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    437      */
    438     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
    439         if (clazz == null) {
    440             final Class<?> candidate = ReflectionUtil.getCallerClass(2);
    441             if (candidate == null) {
    442                 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
    443             }
    444             return getLogger(candidate, messageFactory);
    445         }
    446         return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory);
    447     }
    448
    449     /**
    450      * Returns a Logger with the name of the calling class.
    451      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    452      *                       the logger but will log a warning if mismatched.
    453      * @return The Logger for the calling class.
    454      * @throws UnsupportedOperationException if the calling class cannot be determined.
    455      */
    456     public static Logger getLogger(final MessageFactory messageFactory) {
    457         return getLogger(ReflectionUtil.getCallerClass(2), messageFactory);
    458     }
    459
    460     /**
    461      * Returns a Logger using the fully qualified class name of the value as the Logger name.
    462      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
    463      *              class will be used as the logger name.
    464      * @return The Logger.
    465      * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    466      */
    467     public static Logger getLogger(final Object value) {
    468         return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2));
    469     }
    470
    471     /**
    472      * Returns a Logger using the fully qualified class name of the value as the Logger name.
    473      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
    474      *              class will be used as the logger name.
    475      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    476      *                       the logger but will log a warning if mismatched.
    477      * @return The Logger.
    478      * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    479      */
    480     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
    481         return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory);
    482     }
    483
    484     /**
    485      * Returns a Logger with the specified name.
    486      *
    487      * @param name The logger name. If null the name of the calling class will be used.
    488      * @return The Logger.
    489      * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    490      */
    491     public static Logger getLogger(final String name) {
    492         return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2));
    493     }
    494
    495     /**
    496      * Returns a Logger with the specified name.
    497      *
    498      * @param name The logger name. If null the name of the calling class will be used.
    499      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    500      *                       the logger but will log a warning if mismatched.
    501      * @return The Logger.
    502      * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    503      */
    504     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
    505         return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
    506             ReflectionUtil.getCallerClass(2), messageFactory);
    507     }
    508
    509     /**
    510      * Returns a Logger with the specified name.
    511      *
    512      * @param fqcn The fully qualified class name of the class that this method is a member of.
    513      * @param name The logger name.
    514      * @return The Logger.
    515      */
    516     protected static Logger getLogger(final String fqcn, final String name) {
    517         return factory.getContext(fqcn, null, null, false).getLogger(name);
    518     }
    519
    520     /**
    521      * Returns the root logger.
    522      *
    523      * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
    524      */
    525     public static Logger getRootLogger() {
    526         return getLogger(ROOT_LOGGER_NAME);
    527     }
    528
    529     /**
    530      * Prevents instantiation
    531      */
    532     protected LogManager() {
    533     }
    534
    535 }