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.Formatter;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.SortedMap;
24 import java.util.TreeMap;
25
26 import org.apache.logging.log4j.message.MessageFactory;
27 import org.apache.logging.log4j.message.StringFormatterMessageFactory;
28 import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
29 import org.apache.logging.log4j.spi.LoggerContext;
30 import org.apache.logging.log4j.spi.LoggerContextFactory;
31 import org.apache.logging.log4j.spi.Provider;
32 import org.apache.logging.log4j.status.StatusLogger;
33 import org.apache.logging.log4j.util.PropertiesUtil;
34 import org.apache.logging.log4j.util.ProviderUtil;
35
36 /**
37 * The anchor point for the logging system.
38 */
39 public class LogManager {
40 /**
41 * The name of the root Logger.
42 */
43 public static final String ROOT_LOGGER_NAME = "";
44
45 private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
46
47 private static LoggerContextFactory factory;
48
49 private static final Logger LOGGER = StatusLogger.getLogger();
50
51 /**
52 * Scans the classpath to find all logging implementation. Currently, only one will
53 * be used but this could be extended to allow multiple implementations to be used.
54 */
55 static {
56 // Shortcut binding to force a specific logging implementation.
57 final PropertiesUtil managerProps = PropertiesUtil.getProperties();
58 final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
59 final ClassLoader cl = ProviderUtil.findClassLoader();
60 if (factoryClass != null) {
61 try {
62 final Class<?> clazz = cl.loadClass(factoryClass);
63 if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
64 factory = (LoggerContextFactory) clazz.newInstance();
65 }
66 } catch (final ClassNotFoundException cnfe) {
67 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
68 } catch (final Exception ex) {
69 LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
70 }
71 }
72
73 if (factory == null) {
74 final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
75
76 if (ProviderUtil.hasProviders()) {
77 final Iterator<Provider> providers = ProviderUtil.getProviders();
78 while (providers.hasNext()) {
79 final Provider provider = providers.next();
80 final String className = provider.getClassName();
81 if (className != null) {
82 try {
83 final Class<?> clazz = cl.loadClass(className);
84 if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
85 factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
86 } else {
87 LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName());
88 }
89 } catch (final ClassNotFoundException cnfe) {
90 LOGGER.error("Unable to locate class " + className + " specified in " +
91 provider.getURL().toString(), cnfe);
92 } catch (final IllegalAccessException iae) {
93 LOGGER.error("Unable to create class " + className + " specified in " +
94 provider.getURL().toString(), iae);
95 } catch (final Exception e) {
96 LOGGER.error("Unable to create class " + className + " specified in " +
97 provider.getURL().toString(), e);
98 e.printStackTrace();
99 }
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 }