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.Map;
21 import java.util.SortedMap;
22 import java.util.TreeMap;
23
24 import org.apache.logging.log4j.message.MessageFactory;
25 import org.apache.logging.log4j.message.StringFormatterMessageFactory;
26 import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
27 import org.apache.logging.log4j.spi.LoggerContext;
28 import org.apache.logging.log4j.spi.LoggerContextFactory;
29 import org.apache.logging.log4j.spi.Provider;
30 import org.apache.logging.log4j.status.StatusLogger;
31 import org.apache.logging.log4j.util.LoaderUtil;
32 import org.apache.logging.log4j.util.PropertiesUtil;
33 import org.apache.logging.log4j.util.ProviderUtil;
34 import org.apache.logging.log4j.util.ReflectionUtil;
35 import org.apache.logging.log4j.util.Strings;
36
37 /**
38 * The anchor point for the logging system. The most common usage of this class is to obtain a named
39 * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger
40 * based on the calling class name. This class also provides method for obtaining named Loggers that use
41 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages.
42 * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods
43 * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not
44 * normally useful for typical usage of Log4j.
45 */
46 public class LogManager {
47
48 private static volatile LoggerContextFactory factory;
49
50 /**
51 * Log4j property to set to the fully qualified class name of a custom implementation of
52 * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
53 */
54 public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
55
56 private static final Logger LOGGER = StatusLogger.getLogger();
57
58 /**
59 * The name of the root Logger.
60 */
61 public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
62
63 // for convenience
64 private static final String FQCN = LogManager.class.getName();
65
66 /**
67 * Scans the classpath to find all logging implementation. Currently, only one will
68 * be used but this could be extended to allow multiple implementations to be used.
69 */
70 static {
71 // Shortcut binding to force a specific logging implementation.
72 final PropertiesUtil managerProps = PropertiesUtil.getProperties();
73 final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
74 if (factoryClassName != null) {
75 try {
76 final Class<?> clazz = LoaderUtil.loadClass(factoryClassName);
77 if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
78 factory = clazz.asSubclass(LoggerContextFactory.class).newInstance();
79 }
80 } catch (final ClassNotFoundException cnfe) {
81 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
82 } catch (final Exception ex) {
83 LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
84 }
85 }
86
87 if (factory == null) {
88 final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
89 // note that the following initial call to ProviderUtil may block until a Provider has been installed when
90 // running in an OSGi environment
91 if (ProviderUtil.hasProviders()) {
92 for (final Provider provider : ProviderUtil.getProviders()) {
93 final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
94 if (factoryClass != null) {
95 try {
96 factories.put(provider.getPriority(), factoryClass.newInstance());
97 } catch (final Exception e) {
98 LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
99 provider.getUrl().toString(), e);
100 }
101 }
102 }
103
104 if (factories.isEmpty()) {
105 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
106 factory = new SimpleLoggerContextFactory();
107 } else {
108 final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
109 for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
110 sb.append("Factory: ").append(entry.getValue().getClass().getName());
111 sb.append(", Weighting: ").append(entry.getKey()).append('\n');
112 }
113 factory = factories.get(factories.lastKey());
114 sb.append("Using factory: ").append(factory.getClass().getName());
115 LOGGER.warn(sb.toString());
116
117 }
118 } else {
119 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
120 factory = new SimpleLoggerContextFactory();
121 }
122 }
123 }
124
125 /**
126 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
127 *
128 * @param name
129 * The Logger name to search for.
130 * @return true if the Logger exists, false otherwise.
131 * @see LoggerContext#hasLogger(String)
132 */
133 public static boolean exists(final String name) {
134 return getContext().hasLogger(name);
135 }
136
137 /**
138 * Returns the current LoggerContext.
139 * <p>
140 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
141 * for the calling class.
142 * </p>
143 * @return The current LoggerContext.
144 */
145 public static LoggerContext getContext() {
146 return factory.getContext(FQCN, null, null, true);
147 }
148
149 /**
150 * Returns a LoggerContext.
151 *
152 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
153 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
154 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
155 * returned. If true then only a single LoggerContext will be returned.
156 * @return a LoggerContext.
157 */
158 public static LoggerContext getContext(final boolean currentContext) {
159 // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
160 return factory.getContext(FQCN, null, null, currentContext, null, null);
161 }
162
163 /**
164 * Returns a LoggerContext.
165 *
166 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
167 * ClassLoader.
168 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
169 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
170 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
171 * returned. If true then only a single LoggerContext will be returned.
172 * @return a LoggerContext.
173 */
174 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
175 return factory.getContext(FQCN, loader, null, currentContext);
176 }
177
178 /**
179 * Returns a LoggerContext.
180 *
181 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
182 * ClassLoader.
183 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
184 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
185 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
186 * returned. If true then only a single LoggerContext will be returned.
187 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
188 * @return a LoggerContext.
189 */
190 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
191 final Object externalContext) {
192 return factory.getContext(FQCN, loader, externalContext, currentContext);
193 }
194
195 /**
196 * Returns a LoggerContext.
197 *
198 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
199 * ClassLoader.
200 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
201 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
202 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
203 * returned. If true then only a single LoggerContext will be returned.
204 * @param configLocation The URI for the configuration to use.
205 * @return a LoggerContext.
206 */
207 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
208 final URI configLocation) {
209 return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
210 }
211
212
213 /**
214 * Returns a LoggerContext.
215 *
216 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
217 * ClassLoader.
218 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
219 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
220 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
221 * returned. If true then only a single LoggerContext will be returned.
222 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
223 * @param configLocation The URI for the configuration to use.
224 * @return a LoggerContext.
225 */
226 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
227 final Object externalContext, final URI configLocation) {
228 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
229 }
230
231
232 /**
233 * Returns a LoggerContext.
234 *
235 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
236 * ClassLoader.
237 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
238 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
239 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
240 * returned. If true then only a single LoggerContext will be returned.
241 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
242 * @param configLocation The URI for the configuration to use.
243 * @param name The LoggerContext name.
244 * @return a LoggerContext.
245 */
246 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
247 final Object externalContext, final URI configLocation,
248 final String name) {
249 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
250 }
251
252 /**
253 * Returns a LoggerContext
254 * @param fqcn The fully qualified class name of the Class that this method is a member of.
255 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
256 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
257 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
258 * returned. If true then only a single LoggerContext will be returned.
259 * @return a LoggerContext.
260 */
261 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
262 return factory.getContext(fqcn, null, null, currentContext);
263 }
264
265 /**
266 * Returns a LoggerContext
267 * @param fqcn The fully qualified class name of the Class that this method is a member of.
268 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
269 * ClassLoader.
270 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
271 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
272 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
273 * returned. If true then only a single LoggerContext will be returned.
274 * @return a LoggerContext.
275 */
276 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
277 final boolean currentContext) {
278 return factory.getContext(fqcn, loader, null, currentContext);
279 }
280
281 /**
282 * Returns the current LoggerContextFactory.
283 * @return The LoggerContextFactory.
284 */
285 public static LoggerContextFactory getFactory() {
286 return factory;
287 }
288
289 /**
290 * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
291 * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
292 * Thus, an alternative LoggerContextFactory can be set at runtime.
293 *
294 * <p>
295 * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
296 * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
297 * Generally, this method should be used only during startup before any code starts caching Logger objects.
298 * </p>
299 *
300 * @param factory the LoggerContextFactory to use.
301 */
302 // FIXME: should we allow only one update of the factory?
303 public static void setFactory(final LoggerContextFactory factory) {
304 LogManager.factory = factory;
305 }
306
307 /**
308 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
309 * <p>
310 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
311 * </p>
312 * <p>
313 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
314 * </p>
315 *
316 * @param clazz
317 * The Class whose name should be used as the Logger name.
318 * @return The Logger, created with a {@link StringFormatterMessageFactory}
319 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
320 * @see Logger#fatal(Marker, String, Object...)
321 * @see Logger#fatal(String, Object...)
322 * @see Logger#error(Marker, String, Object...)
323 * @see Logger#error(String, Object...)
324 * @see Logger#warn(Marker, String, Object...)
325 * @see Logger#warn(String, Object...)
326 * @see Logger#info(Marker, String, Object...)
327 * @see Logger#info(String, Object...)
328 * @see Logger#debug(Marker, String, Object...)
329 * @see Logger#debug(String, Object...)
330 * @see Logger#trace(Marker, String, Object...)
331 * @see Logger#trace(String, Object...)
332 * @see StringFormatterMessageFactory
333 */
334 public static Logger getFormatterLogger(final Class<?> clazz) {
335 return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2),
336 StringFormatterMessageFactory.INSTANCE);
337 }
338
339 /**
340 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
341 * <p>
342 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
343 * </p>
344 * <p>
345 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
346 * </p>
347 *
348 * @param value
349 * The value's whose class name should be used as the Logger name.
350 * @return The Logger, created with a {@link StringFormatterMessageFactory}
351 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
352 * @see Logger#fatal(Marker, String, Object...)
353 * @see Logger#fatal(String, Object...)
354 * @see Logger#error(Marker, String, Object...)
355 * @see Logger#error(String, Object...)
356 * @see Logger#warn(Marker, String, Object...)
357 * @see Logger#warn(String, Object...)
358 * @see Logger#info(Marker, String, Object...)
359 * @see Logger#info(String, Object...)
360 * @see Logger#debug(Marker, String, Object...)
361 * @see Logger#debug(String, Object...)
362 * @see Logger#trace(Marker, String, Object...)
363 * @see Logger#trace(String, Object...)
364 * @see StringFormatterMessageFactory
365 */
366 public static Logger getFormatterLogger(final Object value) {
367 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2),
368 StringFormatterMessageFactory.INSTANCE);
369 }
370
371 /**
372 * Returns a formatter Logger with the specified name.
373 * <p>
374 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
375 * </p>
376 * <p>
377 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
378 * </p>
379 *
380 * @param name The logger name. If null it will default to the name of the calling class.
381 * @return The Logger, created with a {@link StringFormatterMessageFactory}
382 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
383 * @see Logger#fatal(Marker, String, Object...)
384 * @see Logger#fatal(String, Object...)
385 * @see Logger#error(Marker, String, Object...)
386 * @see Logger#error(String, Object...)
387 * @see Logger#warn(Marker, String, Object...)
388 * @see Logger#warn(String, Object...)
389 * @see Logger#info(Marker, String, Object...)
390 * @see Logger#info(String, Object...)
391 * @see Logger#debug(Marker, String, Object...)
392 * @see Logger#debug(String, Object...)
393 * @see Logger#trace(Marker, String, Object...)
394 * @see Logger#trace(String, Object...)
395 * @see StringFormatterMessageFactory
396 */
397 public static Logger getFormatterLogger(final String name) {
398 return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name,
399 StringFormatterMessageFactory.INSTANCE);
400 }
401
402 /**
403 * Returns a Logger with the name of the calling class.
404 * @return The Logger for the calling class.
405 * @throws UnsupportedOperationException if the calling class cannot be determined.
406 */
407 public static Logger getLogger() {
408 return getLogger(ReflectionUtil.getCallerClass(2));
409 }
410
411 /**
412 * Returns a Logger using the fully qualified name of the Class as the Logger name.
413 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
414 * class.
415 * @return The Logger.
416 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
417 */
418 public static Logger getLogger(final Class<?> clazz) {
419 if (clazz == null) {
420 final Class<?> candidate = ReflectionUtil.getCallerClass(2);
421 if (candidate == null) {
422 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
423 }
424 return getLogger(candidate);
425 }
426 return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName());
427 }
428
429 /**
430 * Returns a Logger using the fully qualified name of the Class as the Logger name.
431 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
432 * class.
433 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
434 * the logger but will log a warning if mismatched.
435 * @return The Logger.
436 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
437 */
438 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
439 if (clazz == null) {
440 final Class<?> candidate = ReflectionUtil.getCallerClass(2);
441 if (candidate == null) {
442 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
443 }
444 return getLogger(candidate, messageFactory);
445 }
446 return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory);
447 }
448
449 /**
450 * Returns a Logger with the name of the calling class.
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 for the calling class.
454 * @throws UnsupportedOperationException if the calling class cannot be determined.
455 */
456 public static Logger getLogger(final MessageFactory messageFactory) {
457 return getLogger(ReflectionUtil.getCallerClass(2), messageFactory);
458 }
459
460 /**
461 * Returns a Logger using the fully qualified class name of the value as the Logger name.
462 * @param value The value whose class name should be used as the Logger name. If null the name of the calling
463 * class will be used as the logger name.
464 * @return The Logger.
465 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
466 */
467 public static Logger getLogger(final Object value) {
468 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2));
469 }
470
471 /**
472 * Returns a Logger using the fully qualified class name of the value as the Logger name.
473 * @param value The value whose class name should be used as the Logger name. If null the name of the calling
474 * class will be used as the logger name.
475 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
476 * the logger but will log a warning if mismatched.
477 * @return The Logger.
478 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
479 */
480 public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
481 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory);
482 }
483
484 /**
485 * Returns a Logger with the specified name.
486 *
487 * @param name The logger name. If null the name of the calling class will be used.
488 * @return The Logger.
489 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
490 */
491 public static Logger getLogger(final String name) {
492 return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2));
493 }
494
495 /**
496 * Returns a Logger with the specified name.
497 *
498 * @param name The logger name. If null the name of the calling class will be used.
499 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
500 * the logger but will log a warning if mismatched.
501 * @return The Logger.
502 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
503 */
504 public static Logger getLogger(final String name, final MessageFactory messageFactory) {
505 return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
506 ReflectionUtil.getCallerClass(2), messageFactory);
507 }
508
509 /**
510 * Returns a Logger with the specified name.
511 *
512 * @param fqcn The fully qualified class name of the class that this method is a member of.
513 * @param name The logger name.
514 * @return The Logger.
515 */
516 protected static Logger getLogger(final String fqcn, final String name) {
517 return factory.getContext(fqcn, null, null, false).getLogger(name);
518 }
519
520 /**
521 * Returns the root logger.
522 *
523 * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
524 */
525 public static Logger getRootLogger() {
526 return getLogger(ROOT_LOGGER_NAME);
527 }
528
529 /**
530 * Prevents instantiation
531 */
532 protected LogManager() {
533 }
534
535 }