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     */
017    package org.apache.logging.log4j;
018    
019    import java.net.URI;
020    import java.util.Formatter;
021    import java.util.Iterator;
022    import java.util.Map;
023    import java.util.SortedMap;
024    import java.util.TreeMap;
025    
026    import org.apache.logging.log4j.message.MessageFactory;
027    import org.apache.logging.log4j.message.StringFormatterMessageFactory;
028    import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
029    import org.apache.logging.log4j.spi.LoggerContext;
030    import org.apache.logging.log4j.spi.LoggerContextFactory;
031    import org.apache.logging.log4j.spi.Provider;
032    import org.apache.logging.log4j.status.StatusLogger;
033    import org.apache.logging.log4j.util.PropertiesUtil;
034    import org.apache.logging.log4j.util.ProviderUtil;
035    
036    /**
037     * The anchor point for the logging system.
038     */
039    public class LogManager {
040        /**
041         * The name of the root Logger.
042         */
043        public static final String ROOT_LOGGER_NAME = "";
044    
045        private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
046    
047        private static LoggerContextFactory factory;
048    
049        private static final Logger LOGGER = StatusLogger.getLogger();
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         * Prevents instantiation
126         */
127        protected LogManager() {
128        }
129    
130        /**
131         * Returns the current LoggerContext.
132         * <p>
133         * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
134         * for the calling class.
135         * @return  The current LoggerContext.
136         */
137        public static LoggerContext getContext() {
138            return factory.getContext(LogManager.class.getName(), null, true);
139        }
140    
141        /**
142         * Returns a LoggerContext.
143         *
144         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
145         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
146         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
147         * returned. If true then only a single LoggerContext will be returned.
148         * @return a LoggerContext.
149         */
150        public static LoggerContext getContext(final boolean currentContext) {
151            return factory.getContext(LogManager.class.getName(), null, currentContext);
152        }
153    
154        /**
155         * Returns a LoggerContext.
156         *
157         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
158         * ClassLoader.
159         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
160         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
161         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
162         * returned. If true then only a single LoggerContext will be returned.
163         * @return a LoggerContext.
164         */
165        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
166            return factory.getContext(LogManager.class.getName(), loader, currentContext);
167        }
168    
169        /**
170         * Returns a LoggerContext.
171         *
172         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
173         * ClassLoader.
174         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
175         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
176         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
177         * returned. If true then only a single LoggerContext will be returned.
178         * @param configLocation The URI for the configuration to use.
179         * @return a LoggerContext.
180         */
181        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
182                                               URI configLocation) {
183            return factory.getContext(LogManager.class.getName(), loader, currentContext, configLocation);
184        }
185    
186        /**
187         * Returns a LoggerContext
188         * @param fqcn The fully qualified class name of the Class that this method is a member of.
189         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
190         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
191         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
192         * returned. If true then only a single LoggerContext will be returned.
193         * @return a LoggerContext.
194         */
195        protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
196            return factory.getContext(fqcn, null, currentContext);
197        }
198    
199        /**
200         * Returns a LoggerContext
201         * @param fqcn The fully qualified class name of the Class that this method is a member of.
202         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
203         * ClassLoader.
204         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
205         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
206         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
207         * returned. If true then only a single LoggerContext will be returned.
208         * @return a LoggerContext.
209         */
210        protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
211                                                  final boolean currentContext) {
212            return factory.getContext(fqcn, loader, currentContext);
213        }
214    
215        /**
216         * Returns the LoggerContextFactory.
217         * @return The LoggerContextFactory.
218         */
219        public static LoggerContextFactory getFactory() {
220            return factory;
221        }
222    
223        /**
224         * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
225         * <p>
226         * This logger let you use a {@link Formatter} string in the message to format parameters.
227         * </p>
228         * <p>
229         * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
230         * </p>
231         *
232         * @param clazz
233         *            The Class whose name should be used as the Logger name.
234         * @return The Logger, created with a {@link StringFormatterMessageFactory}
235         * @see Logger#fatal(Marker, String, Object...)
236         * @see Logger#fatal(String, Object...)
237         * @see Logger#error(Marker, String, Object...)
238         * @see Logger#error(String, Object...)
239         * @see Logger#warn(Marker, String, Object...)
240         * @see Logger#warn(String, Object...)
241         * @see Logger#info(Marker, String, Object...)
242         * @see Logger#info(String, Object...)
243         * @see Logger#debug(Marker, String, Object...)
244         * @see Logger#debug(String, Object...)
245         * @see Logger#trace(Marker, String, Object...)
246         * @see Logger#trace(String, Object...)
247         * @see StringFormatterMessageFactory
248         */
249        public static Logger getFormatterLogger(final Class<?> clazz) {
250            return getLogger(clazz, StringFormatterMessageFactory.INSTANCE);
251        }
252    
253        /**
254         * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
255         * <p>
256         * This logger let you use a {@link Formatter} string in the message to format parameters.
257         * </p>
258         * <p>
259         * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
260         * </p>
261         *
262         * @param value
263         *            The value's whose class name should be used as the Logger name.
264         * @return The Logger, created with a {@link StringFormatterMessageFactory}
265         * @see Logger#fatal(Marker, String, Object...)
266         * @see Logger#fatal(String, Object...)
267         * @see Logger#error(Marker, String, Object...)
268         * @see Logger#error(String, Object...)
269         * @see Logger#warn(Marker, String, Object...)
270         * @see Logger#warn(String, Object...)
271         * @see Logger#info(Marker, String, Object...)
272         * @see Logger#info(String, Object...)
273         * @see Logger#debug(Marker, String, Object...)
274         * @see Logger#debug(String, Object...)
275         * @see Logger#trace(Marker, String, Object...)
276         * @see Logger#trace(String, Object...)
277         * @see StringFormatterMessageFactory
278         */
279        public static Logger getFormatterLogger(final Object value) {
280            return getLogger(value, StringFormatterMessageFactory.INSTANCE);
281        }
282    
283        /**
284         * Returns a formatter Logger with the specified name.
285         * <p>
286         * This logger let you use a {@link Formatter} string in the message to format parameters.
287         * </p>
288         * <p>
289         * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
290         * </p>
291         *
292         * @param name
293         *            The logger name.
294         * @return The Logger, created with a {@link StringFormatterMessageFactory}
295         * @see Logger#fatal(Marker, String, Object...)
296         * @see Logger#fatal(String, Object...)
297         * @see Logger#error(Marker, String, Object...)
298         * @see Logger#error(String, Object...)
299         * @see Logger#warn(Marker, String, Object...)
300         * @see Logger#warn(String, Object...)
301         * @see Logger#info(Marker, String, Object...)
302         * @see Logger#info(String, Object...)
303         * @see Logger#debug(Marker, String, Object...)
304         * @see Logger#debug(String, Object...)
305         * @see Logger#trace(Marker, String, Object...)
306         * @see Logger#trace(String, Object...)
307         * @see StringFormatterMessageFactory
308         */
309        public static Logger getFormatterLogger(final String name) {
310            return getLogger(name, StringFormatterMessageFactory.INSTANCE);
311        }
312    
313        /**
314         * Returns a Logger using the fully qualified name of the Class as the Logger name.
315         * @param clazz The Class whose name should be used as the Logger name.
316         * @return The Logger.
317         */
318        public static Logger getLogger(final Class<?> clazz) {
319            return getLogger(clazz != null ? clazz.getName() : null);
320        }
321    
322        /**
323         * Returns a Logger using the fully qualified name of the Class as the Logger name.
324         * @param clazz The Class whose name should be used as the Logger name.
325         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
326         *                       the logger but will log a warning if mismatched.
327         * @return The Logger.
328         */
329        public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
330            return getLogger(clazz != null ? clazz.getName() : null, messageFactory);
331        }
332    
333        /**
334         * Returns a Logger using the fully qualified class name of the value as the Logger name.
335         * @param value The value whose class name should be used as the Logger name.
336         * @return The Logger.
337         */
338        public static Logger getLogger(final Object value) {
339            return getLogger(value != null ? value.getClass() : null);
340        }
341    
342        /**
343         * Returns a Logger using the fully qualified class name of the value as the Logger name.
344         * @param value The value whose class name should be used as the Logger name.
345         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
346         *                       the logger but will log a warning if mismatched.
347         * @return The Logger.
348         */
349        public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
350            return getLogger(value != null ? value.getClass() : null, messageFactory);
351        }
352    
353        /**
354         * Returns a Logger with the specified name.
355         *
356         * @param name The logger name.
357         * @return The Logger.
358         */
359        public static Logger getLogger(final String name) {
360            return factory.getContext(LogManager.class.getName(), null, false).getLogger(name);
361        }
362    
363        /**
364         * Returns a Logger with the specified name.
365         *
366         * @param name The logger name.
367         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
368         *                       the logger but will log a warning if mismatched.
369         * @return The Logger.
370         */
371        public static Logger getLogger(final String name, final MessageFactory messageFactory) {
372            return factory.getContext(LogManager.class.getName(), null, false).getLogger(name, messageFactory);
373        }
374    
375        /**
376         * Returns a Logger with the specified name.
377         *
378         * @param fqcn The fully qualified class name of the class that this method is a member of.
379         * @param name The logger name.
380         * @return The Logger.
381         */
382        protected static Logger getLogger(final String fqcn, final String name) {
383            return factory.getContext(fqcn, null, false).getLogger(name);
384        }
385    }