001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j;
018
019import java.net.URI;
020import java.util.Iterator;
021import java.util.Map;
022import java.util.SortedMap;
023import java.util.TreeMap;
024
025import org.apache.logging.log4j.message.MessageFactory;
026import org.apache.logging.log4j.message.StringFormatterMessageFactory;
027import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
028import org.apache.logging.log4j.spi.LoggerContext;
029import org.apache.logging.log4j.spi.LoggerContextFactory;
030import org.apache.logging.log4j.spi.Provider;
031import org.apache.logging.log4j.status.StatusLogger;
032import org.apache.logging.log4j.util.PropertiesUtil;
033import org.apache.logging.log4j.util.ProviderUtil;
034
035/**
036 * The anchor point for the logging system.
037 */
038public class LogManager {
039
040    private static LoggerContextFactory factory;
041
042    private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
043
044    private static final Logger LOGGER = StatusLogger.getLogger();
045
046    /**
047     * The name of the root Logger.
048     */
049    public static final String ROOT_LOGGER_NAME = "";
050
051    /**
052     * Scans the classpath to find all logging implementation. Currently, only one will
053     * be used but this could be extended to allow multiple implementations to be used.
054     */
055    static {
056        // Shortcut binding to force a specific logging implementation.
057        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
058        final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
059        final ClassLoader cl = ProviderUtil.findClassLoader();
060        if (factoryClass != null) {
061            try {
062                final Class<?> clazz = cl.loadClass(factoryClass);
063                if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
064                    factory = (LoggerContextFactory) clazz.newInstance();
065                }
066            } catch (final ClassNotFoundException cnfe) {
067                LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
068            } catch (final Exception ex) {
069                LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
070            }
071        }
072
073        if (factory == null) {
074            final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
075
076            if (ProviderUtil.hasProviders()) {
077                final Iterator<Provider> providers = ProviderUtil.getProviders();
078                while (providers.hasNext()) {
079                    final Provider provider = providers.next();
080                    final String className = provider.getClassName();
081                    if (className != null) {
082                        try {
083                            final Class<?> clazz = cl.loadClass(className);
084                            if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
085                                factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
086                            } else {
087                                LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName());
088                            }
089                        } catch (final ClassNotFoundException cnfe) {
090                            LOGGER.error("Unable to locate class " + className + " specified in " +
091                                provider.getURL().toString(), cnfe);
092                        } catch (final IllegalAccessException iae) {
093                            LOGGER.error("Unable to create class " + className + " specified in " +
094                                provider.getURL().toString(), iae);
095                        } catch (final Exception e) {
096                            LOGGER.error("Unable to create class " + className + " specified in " +
097                                provider.getURL().toString(), e);
098                            e.printStackTrace();
099                        }
100                    }
101                }
102
103                if (factories.size() == 0) {
104                    LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
105                    factory = new SimpleLoggerContextFactory();
106                } else {
107                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
108                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
109                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
110                        sb.append(", Weighting: ").append(entry.getKey()).append("\n");
111                    }
112                    factory = factories.get(factories.lastKey());
113                    sb.append("Using factory: ").append(factory.getClass().getName());
114                    LOGGER.warn(sb.toString());
115
116                }
117            } else {
118                LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
119                factory = new SimpleLoggerContextFactory();
120            }
121        }
122    }
123
124    /**
125     * Gets the class name of the caller in the current stack at the given {@code depth}.
126     *
127     * @param depth a 0-based index in the current stack.
128     * @return a class name
129     */
130    private static String getClassName(final int depth) {
131        return new Throwable().getStackTrace()[depth].getClassName();
132    }
133
134    /**
135     * Returns the current LoggerContext.
136     * <p>
137     * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
138     * for the calling class.
139     * @return  The current LoggerContext.
140     */
141    public static LoggerContext getContext() {
142        return factory.getContext(LogManager.class.getName(), null, null, true);
143    }
144
145    /**
146     * Returns a LoggerContext.
147     *
148     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
149     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
150     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
151     * returned. If true then only a single LoggerContext will be returned.
152     * @return a LoggerContext.
153     */
154    public static LoggerContext getContext(final boolean currentContext) {
155        return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
156    }
157
158    /**
159     * Returns a LoggerContext.
160     *
161     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
162     * ClassLoader.
163     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
164     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
165     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
166     * returned. If true then only a single LoggerContext will be returned.
167     * @return a LoggerContext.
168     */
169    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
170        return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
171    }
172
173    /**
174     * Returns a LoggerContext.
175     *
176     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
177     * ClassLoader.
178     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
179     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
180     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
181     * returned. If true then only a single LoggerContext will be returned.
182     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
183     * @return a LoggerContext.
184     */
185    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
186                                           final Object externalContext) {
187        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
188    }
189
190    /**
191     * Returns a LoggerContext.
192     *
193     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
194     * ClassLoader.
195     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
196     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
197     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
198     * returned. If true then only a single LoggerContext will be returned.
199     * @param configLocation The URI for the configuration to use.
200     * @return a LoggerContext.
201     */
202    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
203                                           final URI configLocation) {
204        return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
205    }
206
207
208    /**
209     * Returns a LoggerContext.
210     *
211     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
212     * ClassLoader.
213     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
214     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
215     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
216     * returned. If true then only a single LoggerContext will be returned.
217     * @param configLocation The URI for the configuration to use.
218     * @return a LoggerContext.
219     */
220    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
221                                           final Object externalContext, final URI configLocation) {
222        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
223            null);
224    }
225
226
227    /**
228     * Returns a LoggerContext.
229     *
230     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
231     * ClassLoader.
232     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
233     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
234     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
235     * returned. If true then only a single LoggerContext will be returned.
236     * @param configLocation The URI for the configuration to use.
237     * @param name The LoggerContext name.
238     * @return a LoggerContext.
239     */
240    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
241                                           final Object externalContext, final URI configLocation,
242                                           final String name) {
243        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
244            name);
245    }
246
247    /**
248     * Returns a LoggerContext
249     * @param fqcn The fully qualified class name of the Class that this method is a member of.
250     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
251     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
252     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
253     * returned. If true then only a single LoggerContext will be returned.
254     * @return a LoggerContext.
255     */
256    protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
257        return factory.getContext(fqcn, null, null, currentContext);
258    }
259
260    /**
261     * Returns a LoggerContext
262     * @param fqcn The fully qualified class name of the Class that this method is a member of.
263     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
264     * ClassLoader.
265     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
266     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
267     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
268     * returned. If true then only a single LoggerContext will be returned.
269     * @return a LoggerContext.
270     */
271    protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
272                                              final boolean currentContext) {
273        return factory.getContext(fqcn, loader, null, currentContext);
274    }
275
276    /**
277     * Returns the LoggerContextFactory.
278     * @return The LoggerContextFactory.
279     */
280    public static LoggerContextFactory getFactory() {
281        return factory;
282    }
283
284    /**
285     * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
286     * <p>
287     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
288     * </p>
289     * <p>
290     * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
291     * </p>
292     *
293     * @param clazz
294     *            The Class whose name should be used as the Logger name.
295     * @return The Logger, created with a {@link StringFormatterMessageFactory}
296     * @see Logger#fatal(Marker, String, Object...)
297     * @see Logger#fatal(String, Object...)
298     * @see Logger#error(Marker, String, Object...)
299     * @see Logger#error(String, Object...)
300     * @see Logger#warn(Marker, String, Object...)
301     * @see Logger#warn(String, Object...)
302     * @see Logger#info(Marker, String, Object...)
303     * @see Logger#info(String, Object...)
304     * @see Logger#debug(Marker, String, Object...)
305     * @see Logger#debug(String, Object...)
306     * @see Logger#trace(Marker, String, Object...)
307     * @see Logger#trace(String, Object...)
308     * @see StringFormatterMessageFactory
309     */
310    public static Logger getFormatterLogger(final Class<?> clazz) {
311        return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
312    }
313
314    /**
315     * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
316     * <p>
317     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
318     * </p>
319     * <p>
320     * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
321     * </p>
322     *
323     * @param value
324     *            The value's whose class name should be used as the Logger name.
325     * @return The Logger, created with a {@link StringFormatterMessageFactory}
326     * @see Logger#fatal(Marker, String, Object...)
327     * @see Logger#fatal(String, Object...)
328     * @see Logger#error(Marker, String, Object...)
329     * @see Logger#error(String, Object...)
330     * @see Logger#warn(Marker, String, Object...)
331     * @see Logger#warn(String, Object...)
332     * @see Logger#info(Marker, String, Object...)
333     * @see Logger#info(String, Object...)
334     * @see Logger#debug(Marker, String, Object...)
335     * @see Logger#debug(String, Object...)
336     * @see Logger#trace(Marker, String, Object...)
337     * @see Logger#trace(String, Object...)
338     * @see StringFormatterMessageFactory
339     */
340    public static Logger getFormatterLogger(final Object value) {
341        return getLogger(value != null ? value.getClass().getName() : getClassName(2),
342                StringFormatterMessageFactory.INSTANCE);
343    }
344
345    /**
346     * Returns a formatter Logger with the specified name.
347     * <p>
348     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
349     * </p>
350     * <p>
351     * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
352     * </p>
353     *
354     * @param name The logger name. If null it will default to the name of the calling class.
355     * @return The Logger, created with a {@link StringFormatterMessageFactory}
356     * @see Logger#fatal(Marker, String, Object...)
357     * @see Logger#fatal(String, Object...)
358     * @see Logger#error(Marker, String, Object...)
359     * @see Logger#error(String, Object...)
360     * @see Logger#warn(Marker, String, Object...)
361     * @see Logger#warn(String, Object...)
362     * @see Logger#info(Marker, String, Object...)
363     * @see Logger#info(String, Object...)
364     * @see Logger#debug(Marker, String, Object...)
365     * @see Logger#debug(String, Object...)
366     * @see Logger#trace(Marker, String, Object...)
367     * @see Logger#trace(String, Object...)
368     * @see StringFormatterMessageFactory
369     */
370    public static Logger getFormatterLogger(final String name) {
371        return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
372    }
373
374    /**
375     * Returns a Logger with the name of the calling class.
376     * @return The Logger for the calling class.
377     */
378    public static Logger getLogger() {
379        return getLogger(getClassName(2));
380    }
381
382    /**
383     * Returns a Logger using the fully qualified name of the Class as the Logger name.
384     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
385     *              class.
386     * @return The Logger.
387     */
388    public static Logger getLogger(final Class<?> clazz) {
389        return getLogger(clazz != null ? clazz.getName() : getClassName(2));
390    }
391
392    /**
393     * Returns a Logger using the fully qualified name of the Class as the Logger name.
394     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
395     *              class.
396     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
397     *                       the logger but will log a warning if mismatched.
398     * @return The Logger.
399     */
400    public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
401        return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
402    }
403
404    /**
405     * Returns a Logger with the name of the calling class.
406     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
407     *                       the logger but will log a warning if mismatched.
408     * @return The Logger for the calling class.
409     */
410    public static Logger getLogger(final MessageFactory messageFactory) {
411        return getLogger(getClassName(2), messageFactory);
412    }
413
414    /**
415     * Returns a Logger using the fully qualified class name of the value as the Logger name.
416     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
417     *              class will be used as the logger name.
418     * @return The Logger.
419     */
420    public static Logger getLogger(final Object value) {
421        return getLogger(value != null ? value.getClass().getName() : getClassName(2));
422    }
423
424    /**
425     * Returns a Logger using the fully qualified class name of the value as the Logger name.
426     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
427     *              class will be used as the logger name.
428     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
429     *                       the logger but will log a warning if mismatched.
430     * @return The Logger.
431     */
432    public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
433        return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
434    }
435
436    /**
437     * Returns a Logger with the specified name.
438     *
439     * @param name The logger name. If null the name of the calling class will be used.
440     * @return The Logger.
441     */
442    public static Logger getLogger(final String name) {
443        final String actualName = name != null ? name : getClassName(2);
444        return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
445    }
446
447    /**
448     * Returns a Logger with the specified name.
449     *
450     * @param name The logger name. If null the name of the calling class will be used.
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.
454     */
455    public static Logger getLogger(final String name, final MessageFactory messageFactory) {
456        final String actualName = name != null ? name : getClassName(2);
457        return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
458    }
459
460    /**
461     * Returns a Logger with the specified name.
462     *
463     * @param fqcn The fully qualified class name of the class that this method is a member of.
464     * @param name The logger name.
465     * @return The Logger.
466     */
467    protected static Logger getLogger(final String fqcn, final String name) {
468        return factory.getContext(fqcn, null, null, false).getLogger(name);
469    }
470
471    /**
472     * Returns the root logger.
473     *
474     * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
475     */
476    public static Logger getRootLogger() {
477        return getLogger(ROOT_LOGGER_NAME);
478    }
479
480    /**
481     * Prevents instantiation
482     */
483    protected LogManager() {
484    }
485
486}