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 org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
020    import org.apache.logging.log4j.status.StatusLogger;
021    import org.apache.logging.log4j.spi.LoggerContext;
022    import org.apache.logging.log4j.spi.LoggerContextFactory;
023    import org.apache.logging.log4j.util.PropsUtil;
024    
025    import java.io.IOException;
026    import java.net.URL;
027    import java.util.Enumeration;
028    import java.util.Map;
029    import java.util.Properties;
030    import java.util.SortedMap;
031    import java.util.TreeMap;
032    
033    /**
034     * The anchor point for the logging system.
035     */
036    public class LogManager {
037        /**
038         * The name of the root Logger.
039         */
040        public static final String ROOT_LOGGER_NAME = "";
041    
042        private static final String LOGGER_RESOURCE = "META-INF/log4j-provider.properties";
043        private static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory";
044        private static final String API_VERSION = "Log4jAPIVersion";
045        private static final String FACTORY_PRIORITY = "FactoryPriority";
046        private static final String[] COMPATIBLE_API_VERSIONS = {
047            "2.0.0"
048        };
049    
050        private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
051    
052        private static LoggerContextFactory factory;
053    
054        private static final Logger logger = StatusLogger.getLogger();
055    
056        /**
057         * Prevents instantiation
058         */
059        protected LogManager() {
060        }
061    
062        /**
063         * Scans the classpath to find all logging implementation. Currently, only one will
064         * be used but this could be extended to allow multiple implementations to be used.
065         */
066        static {
067            // Shortcut binding to force a specific logging implementation.
068            PropsUtil managerProps = new PropsUtil("log4j2.LogManager.properties");
069            String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
070            ClassLoader cl = findClassLoader();
071            if (factoryClass != null) {
072                try {
073                    Class<?> clazz = cl.loadClass(factoryClass);
074                    if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
075                        factory = (LoggerContextFactory) clazz.newInstance();
076                    }
077                } catch (ClassNotFoundException cnfe) {
078                    logger.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
079                } catch (Exception ex) {
080                    logger.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
081                }
082            }
083    
084            if (factory == null) {
085                SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
086    
087                Enumeration<URL> enumResources = null;
088                try {
089                    enumResources = cl.getResources(LOGGER_RESOURCE);
090                } catch (IOException e) {
091                    logger.fatal("Unable to locate " + LOGGER_RESOURCE, e);
092                }
093    
094                if (enumResources != null) {
095                    while (enumResources.hasMoreElements()) {
096                        Properties props = new Properties();
097                        URL url = enumResources.nextElement();
098                        try {
099                            props.load(url.openStream());
100                        } catch (IOException ioe) {
101                            logger.error("Unable to read " + url.toString(), ioe);
102                        }
103                        if (!validVersion(props.getProperty(API_VERSION))) {
104                            continue;
105                        }
106                        String weight = props.getProperty(FACTORY_PRIORITY);
107                        Integer priority = weight == null ? -1 : Integer.valueOf(weight);
108                        String className = props.getProperty(LOGGER_CONTEXT_FACTORY);
109                        if (className != null) {
110                            try {
111                                Class<?> clazz = cl.loadClass(className);
112                                if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
113                                    factories.put(priority, (LoggerContextFactory) clazz.newInstance());
114                                } else {
115                                    logger.error(className + " does not implement " + LoggerContextFactory.class.getName());
116                                }
117                            } catch (ClassNotFoundException cnfe) {
118                                logger.error("Unable to locate class " + className + " specified in " + url.toString(),
119                                    cnfe);
120                            } catch (IllegalAccessException iae) {
121                                logger.error("Unable to create class " + className + " specified in " + url.toString(),
122                                    iae);
123                            } catch (Exception e) {
124                                logger.error("Unable to create class " + className + " specified in " + url.toString(), e);
125                                e.printStackTrace();
126                            }
127                        }
128                    }
129                    if (factories.size() == 0) {
130                        logger.error("Unable to locate a logging implementation, using SimpleLogger");
131                        factory = new SimpleLoggerContextFactory();
132                    } else {
133                        StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
134                        for (Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
135                            sb.append("Factory: ").append(entry.getValue().getClass().getName());
136                            sb.append(", Weighting: ").append(entry.getKey()).append("\n");
137                        }
138                        factory = factories.get(factories.lastKey());
139                        sb.append("Using factory: ").append(factory.getClass().getName());
140                        logger.warn(sb.toString());
141    
142                    }
143                } else {
144                    logger.error("Unable to locate a logging implementation, using SimpleLogger");
145                    factory = new SimpleLoggerContextFactory();
146                }
147            }
148        }
149    
150        /**
151         * Returns the LoggerContextFactory.
152         * @return The LoggerContextFactory.
153         */
154        public static LoggerContextFactory getFactory() {
155            return factory;
156        }
157    
158        /**
159         * Returns a Logger with the specified name.
160         *
161         * @param name The logger name.
162         * @return The Logger.
163         */
164        public static Logger getLogger(String name) {
165            return factory.getContext(LogManager.class.getName(), null, false).getLogger(name);
166        }
167    
168        /**
169         * Returns a Logger using the fully qualified name of the Class as the Logger name.
170         * @param clazz The Class whose name should be used as the Logger name.
171         * @return The Logger.
172         */
173        public static Logger getLogger(Class<?> clazz) {
174            return getLogger(clazz != null ? clazz.getName() : null);
175        }
176    
177        /**
178         * Returns a Logger using the fully qualified class name of the value as the Logger name.
179         * @param value The value whose class name should be used as the Logger name.
180         * @return The Logger.
181         */
182        public static Logger getLogger(Object value) {
183            return getLogger(value != null ? value.getClass() : null);
184        }
185    
186        /**
187         * Returns a Logger with the specified name.
188         *
189         * @param fqcn The fully qualified class name of the class that this method is a member of.
190         * @param name The logger name.
191         * @return The Logger.
192         */
193        protected static Logger getLogger(String fqcn, String name) {
194            return factory.getContext(fqcn, null, false).getLogger(name);
195        }
196    
197        /**
198         * Returns the current LoggerContext.
199         * <p>
200         * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
201         * for the calling class.
202         * @return  The current LoggerContext.
203         */
204        public static LoggerContext getContext() {
205            return factory.getContext(LogManager.class.getName(), null, true);
206        }
207    
208        /**
209         * Returns a LoggerContext.
210         *
211         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
212         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
213         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
214         * returned. If true then only a single LoggerContext will be returned.
215         * @return a LoggerContext.
216         */
217        public static LoggerContext getContext(boolean currentContext) {
218            return factory.getContext(LogManager.class.getName(), null, currentContext);
219        }
220    
221        /**
222         * Returns a LoggerContext.
223         *
224         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
225         * ClassLoader.
226         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
227         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
228         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
229         * returned. If true then only a single LoggerContext will be returned.
230         * @return a LoggerContext.
231         */
232        public static LoggerContext getContext(ClassLoader loader, boolean currentContext) {
233            return factory.getContext(LogManager.class.getName(), loader, currentContext);
234        }
235    
236    
237        /**
238         * Returns a LoggerContext
239         * @param fqcn The fully qualified class name of the Class that this method is a member of.
240         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
241         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
242         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
243         * returned. If true then only a single LoggerContext will be returned.
244         * @return a LoggerContext.
245         */
246        protected static LoggerContext getContext(String fqcn, boolean currentContext) {
247            return factory.getContext(fqcn, null, currentContext);
248        }
249    
250    
251        /**
252         * Returns a LoggerContext
253         * @param fqcn The fully qualified class name of the Class that this method is a member of.
254         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
255         * ClassLoader.
256         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
257         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
258         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
259         * returned. If true then only a single LoggerContext will be returned.
260         * @return a LoggerContext.
261         */
262        protected static LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext) {
263            return factory.getContext(fqcn, loader, currentContext);
264        }
265    
266        private static ClassLoader findClassLoader() {
267            ClassLoader cl;
268            if (System.getSecurityManager() == null) {
269                cl = Thread.currentThread().getContextClassLoader();
270            } else {
271                cl = java.security.AccessController.doPrivileged(
272                    new java.security.PrivilegedAction<ClassLoader>() {
273                        public ClassLoader run() {
274                            return Thread.currentThread().getContextClassLoader();
275                        }
276                    }
277                );
278            }
279            if (cl == null) {
280                cl = LogManager.class.getClassLoader();
281            }
282    
283            return cl;
284        }
285    
286        private static boolean validVersion(String version) {
287            for (String v : COMPATIBLE_API_VERSIONS) {
288                if (version.startsWith(v)) {
289                    return true;
290                }
291            }
292            return false;
293        }
294    
295    }