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.util.Formatter;
020    import java.util.Iterator;
021    import java.util.Map;
022    import java.util.SortedMap;
023    import java.util.TreeMap;
024    
025    import org.apache.logging.log4j.message.MessageFactory;
026    import org.apache.logging.log4j.message.StringFormatterMessageFactory;
027    import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
028    import org.apache.logging.log4j.spi.LoggerContext;
029    import org.apache.logging.log4j.spi.LoggerContextFactory;
030    import org.apache.logging.log4j.spi.Provider;
031    import org.apache.logging.log4j.status.StatusLogger;
032    import org.apache.logging.log4j.util.PropertiesUtil;
033    import org.apache.logging.log4j.util.ProviderUtil;
034    
035    /**
036     * The anchor point for the logging system.
037     */
038    public class LogManager {
039        /**
040         * The name of the root Logger.
041         */
042        public static final String ROOT_LOGGER_NAME = "";
043    
044        private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
045    
046        private static LoggerContextFactory factory;
047    
048        private static final Logger LOGGER = StatusLogger.getLogger();
049    
050        /**
051         * Scans the classpath to find all logging implementation. Currently, only one will
052         * be used but this could be extended to allow multiple implementations to be used.
053         */
054        static {
055            // Shortcut binding to force a specific logging implementation.
056            final PropertiesUtil managerProps = PropertiesUtil.getProperties();
057            final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
058            final ClassLoader cl = ProviderUtil.findClassLoader();
059            if (factoryClass != null) {
060                try {
061                    final Class<?> clazz = cl.loadClass(factoryClass);
062                    if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
063                        factory = (LoggerContextFactory) clazz.newInstance();
064                    }
065                } catch (final ClassNotFoundException cnfe) {
066                    LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
067                } catch (final Exception ex) {
068                    LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
069                }
070            }
071    
072            if (factory == null) {
073                final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
074    
075                if (ProviderUtil.hasProviders()) {
076                    final Iterator<Provider> providers = ProviderUtil.getProviders();
077                    while (providers.hasNext()) {
078                        final Provider provider = providers.next();
079                        final String className = provider.getClassName();
080                        if (className != null) {
081                            try {
082                                final Class<?> clazz = cl.loadClass(className);
083                                if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
084                                    factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
085                                } else {
086                                    LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName());
087                                }
088                            } catch (final ClassNotFoundException cnfe) {
089                                LOGGER.error("Unable to locate class " + className + " specified in " +
090                                    provider.getURL().toString(), cnfe);
091                            } catch (final IllegalAccessException iae) {
092                                LOGGER.error("Unable to create class " + className + " specified in " +
093                                    provider.getURL().toString(), iae);
094                            } catch (final Exception e) {
095                                LOGGER.error("Unable to create class " + className + " specified in " +
096                                    provider.getURL().toString(), e);
097                                e.printStackTrace();
098                            }
099                        }
100                    }
101    
102                    if (factories.size() == 0) {
103                        LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
104                        factory = new SimpleLoggerContextFactory();
105                    } else {
106                        final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
107                        for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
108                            sb.append("Factory: ").append(entry.getValue().getClass().getName());
109                            sb.append(", Weighting: ").append(entry.getKey()).append("\n");
110                        }
111                        factory = factories.get(factories.lastKey());
112                        sb.append("Using factory: ").append(factory.getClass().getName());
113                        LOGGER.warn(sb.toString());
114    
115                    }
116                } else {
117                    LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
118                    factory = new SimpleLoggerContextFactory();
119                }
120            }
121        }
122    
123        /**
124         * Prevents instantiation
125         */
126        protected LogManager() {
127        }
128    
129        /**
130         * Returns the current LoggerContext.
131         * <p>
132         * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
133         * for the calling class.
134         * @return  The current LoggerContext.
135         */
136        public static LoggerContext getContext() {
137            return factory.getContext(LogManager.class.getName(), null, true);
138        }
139    
140        /**
141         * Returns a LoggerContext.
142         *
143         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
144         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
145         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
146         * returned. If true then only a single LoggerContext will be returned.
147         * @return a LoggerContext.
148         */
149        public static LoggerContext getContext(final boolean currentContext) {
150            return factory.getContext(LogManager.class.getName(), null, currentContext);
151        }
152    
153        /**
154         * Returns a LoggerContext.
155         *
156         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
157         * ClassLoader.
158         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
159         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
160         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
161         * returned. If true then only a single LoggerContext will be returned.
162         * @return a LoggerContext.
163         */
164        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
165            return factory.getContext(LogManager.class.getName(), loader, currentContext);
166        }
167    
168        /**
169         * Returns a LoggerContext
170         * @param fqcn The fully qualified class name of the Class that this method is a member of.
171         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
172         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
173         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
174         * returned. If true then only a single LoggerContext will be returned.
175         * @return a LoggerContext.
176         */
177        protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
178            return factory.getContext(fqcn, null, currentContext);
179        }
180    
181        /**
182         * Returns a LoggerContext
183         * @param fqcn The fully qualified class name of the Class that this method is a member of.
184         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
185         * ClassLoader.
186         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
187         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
188         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
189         * returned. If true then only a single LoggerContext will be returned.
190         * @return a LoggerContext.
191         */
192        protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
193                                                  final boolean currentContext) {
194            return factory.getContext(fqcn, loader, currentContext);
195        }
196    
197        /**
198         * Returns the LoggerContextFactory.
199         * @return The LoggerContextFactory.
200         */
201        public static LoggerContextFactory getFactory() {
202            return factory;
203        }
204    
205        /**
206         * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
207         * <p>
208         * This logger let you use a {@link Formatter} string in the message to format parameters.
209         * </p>
210         * <p>
211         * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
212         * </p>
213         *
214         * @param clazz
215         *            The Class whose name should be used as the Logger name.
216         * @return The Logger, created with a {@link StringFormatterMessageFactory}
217         * @see Logger#fatal(Marker, String, Object...)
218         * @see Logger#fatal(String, Object...)
219         * @see Logger#error(Marker, String, Object...)
220         * @see Logger#error(String, Object...)
221         * @see Logger#warn(Marker, String, Object...)
222         * @see Logger#warn(String, Object...)
223         * @see Logger#info(Marker, String, Object...)
224         * @see Logger#info(String, Object...)
225         * @see Logger#debug(Marker, String, Object...)
226         * @see Logger#debug(String, Object...)
227         * @see Logger#trace(Marker, String, Object...)
228         * @see Logger#trace(String, Object...)
229         * @see StringFormatterMessageFactory
230         */
231        public static Logger getFormatterLogger(final Class<?> clazz) {
232            return getLogger(clazz, StringFormatterMessageFactory.INSTANCE);
233        }
234    
235        /**
236         * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
237         * <p>
238         * This logger let you use a {@link Formatter} string in the message to format parameters.
239         * </p>
240         * <p>
241         * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
242         * </p>
243         *
244         * @param value
245         *            The value's whose class name should be used as the Logger name.
246         * @return The Logger, created with a {@link StringFormatterMessageFactory}
247         * @see Logger#fatal(Marker, String, Object...)
248         * @see Logger#fatal(String, Object...)
249         * @see Logger#error(Marker, String, Object...)
250         * @see Logger#error(String, Object...)
251         * @see Logger#warn(Marker, String, Object...)
252         * @see Logger#warn(String, Object...)
253         * @see Logger#info(Marker, String, Object...)
254         * @see Logger#info(String, Object...)
255         * @see Logger#debug(Marker, String, Object...)
256         * @see Logger#debug(String, Object...)
257         * @see Logger#trace(Marker, String, Object...)
258         * @see Logger#trace(String, Object...)
259         * @see StringFormatterMessageFactory
260         */
261        public static Logger getFormatterLogger(final Object value) {
262            return getLogger(value, StringFormatterMessageFactory.INSTANCE);
263        }
264    
265        /**
266         * Returns a formatter Logger with the specified name.
267         * <p>
268         * This logger let you use a {@link Formatter} string in the message to format parameters.
269         * </p>
270         * <p>
271         * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
272         * </p>
273         *
274         * @param name
275         *            The logger name.
276         * @return The Logger, created with a {@link StringFormatterMessageFactory}
277         * @see Logger#fatal(Marker, String, Object...)
278         * @see Logger#fatal(String, Object...)
279         * @see Logger#error(Marker, String, Object...)
280         * @see Logger#error(String, Object...)
281         * @see Logger#warn(Marker, String, Object...)
282         * @see Logger#warn(String, Object...)
283         * @see Logger#info(Marker, String, Object...)
284         * @see Logger#info(String, Object...)
285         * @see Logger#debug(Marker, String, Object...)
286         * @see Logger#debug(String, Object...)
287         * @see Logger#trace(Marker, String, Object...)
288         * @see Logger#trace(String, Object...)
289         * @see StringFormatterMessageFactory
290         */
291        public static Logger getFormatterLogger(final String name) {
292            return getLogger(name, StringFormatterMessageFactory.INSTANCE);
293        }
294    
295        /**
296         * Returns a Logger using the fully qualified name of the Class as the Logger name.
297         * @param clazz The Class whose name should be used as the Logger name.
298         * @return The Logger.
299         */
300        public static Logger getLogger(final Class<?> clazz) {
301            return getLogger(clazz != null ? clazz.getName() : null);
302        }
303    
304        /**
305         * Returns a Logger using the fully qualified name of the Class as the Logger name.
306         * @param clazz The Class whose name should be used as the Logger name.
307         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
308         *                       the logger but will log a warning if mismatched.
309         * @return The Logger.
310         */
311        public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
312            return getLogger(clazz != null ? clazz.getName() : null, messageFactory);
313        }
314    
315        /**
316         * Returns a Logger using the fully qualified class name of the value as the Logger name.
317         * @param value The value whose class name should be used as the Logger name.
318         * @return The Logger.
319         */
320        public static Logger getLogger(final Object value) {
321            return getLogger(value != null ? value.getClass() : null);
322        }
323    
324        /**
325         * Returns a Logger using the fully qualified class name of the value as the Logger name.
326         * @param value The value whose class name should be used as the Logger name.
327         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
328         *                       the logger but will log a warning if mismatched.
329         * @return The Logger.
330         */
331        public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
332            return getLogger(value != null ? value.getClass() : null, messageFactory);
333        }
334    
335        /**
336         * Returns a Logger with the specified name.
337         *
338         * @param name The logger name.
339         * @return The Logger.
340         */
341        public static Logger getLogger(final String name) {
342            return factory.getContext(LogManager.class.getName(), null, false).getLogger(name);
343        }
344    
345        /**
346         * Returns a Logger with the specified name.
347         *
348         * @param name The logger name.
349         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
350         *                       the logger but will log a warning if mismatched.
351         * @return The Logger.
352         */
353        public static Logger getLogger(final String name, final MessageFactory messageFactory) {
354            return factory.getContext(LogManager.class.getName(), null, false).getLogger(name, messageFactory);
355        }
356    
357        /**
358         * Returns a Logger with the specified name.
359         *
360         * @param fqcn The fully qualified class name of the class that this method is a member of.
361         * @param name The logger name.
362         * @return The Logger.
363         */
364        protected static Logger getLogger(final String fqcn, final String name) {
365            return factory.getContext(fqcn, null, false).getLogger(name);
366        }
367    }