View Javadoc
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.log4j;
18  
19  import java.util.Enumeration;
20  import java.util.Map;
21  import java.util.ResourceBundle;
22  import java.util.WeakHashMap;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.util.concurrent.ConcurrentMap;
25  
26  import org.apache.log4j.helpers.NullEnumeration;
27  import org.apache.log4j.legacy.core.CategoryUtil;
28  import org.apache.log4j.or.ObjectRenderer;
29  import org.apache.log4j.or.RendererSupport;
30  import org.apache.log4j.spi.LoggerFactory;
31  import org.apache.log4j.spi.LoggingEvent;
32  import org.apache.logging.log4j.message.MapMessage;
33  import org.apache.logging.log4j.message.SimpleMessage;
34  import org.apache.logging.log4j.spi.ExtendedLogger;
35  import org.apache.logging.log4j.spi.LoggerContext;
36  import org.apache.logging.log4j.message.LocalizedMessage;
37  import org.apache.logging.log4j.message.Message;
38  import org.apache.logging.log4j.message.ObjectMessage;
39  import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
40  import org.apache.logging.log4j.util.Strings;
41  
42  
43  /**
44   * Implementation of the Category class for compatibility, despite it having been deprecated a long, long time ago.
45   */
46  public class Category {
47  
48      private static PrivateAdapter adapter = new PrivateAdapter();
49  
50      private static final Map<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP =
51          new WeakHashMap<>();
52  
53      private static final String FQCN = Category.class.getName();
54  
55      private static final boolean isCoreAvailable;
56  
57      private final Map<Class<?>, ObjectRenderer> rendererMap;
58  
59      static {
60          boolean available;
61  
62          try {
63              available = Class.forName("org.apache.logging.log4j.core.Logger") != null;
64          } catch (Exception ex) {
65              available = false;
66          }
67          isCoreAvailable = available;
68      }
69  
70      /**
71       * Resource bundle for localized messages.
72       */
73      protected ResourceBundle bundle = null;
74  
75      private final org.apache.logging.log4j.Logger logger;
76  
77      /**
78       * Constructor used by Logger to specify a LoggerContext.
79       * @param context The LoggerContext.
80       * @param name The name of the Logger.
81       */
82      protected Category(final LoggerContext context, final String name) {
83          this.logger = context.getLogger(name);
84          rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
85      }
86  
87      /**
88       * Constructor exposed by Log4j 1.2.
89       * @param name The name of the Logger.
90       */
91      protected Category(final String name) {
92          this(PrivateManager.getContext(), name);
93      }
94  
95      private Category(final org.apache.logging.log4j.Logger logger) {
96          this.logger = logger;
97          rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
98      }
99  
100     public static Category getInstance(final String name) {
101         return getInstance(PrivateManager.getContext(), name, adapter);
102     }
103 
104     static Logger getInstance(final LoggerContext context, final String name) {
105         return getInstance(context, name, adapter);
106     }
107 
108     static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
109         final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
110         Logger logger = loggers.get(name);
111         if (logger != null) {
112             return logger;
113         }
114         logger = factory.makeNewLoggerInstance(name);
115         final Logger prev = loggers.putIfAbsent(name, logger);
116         return prev == null ? logger : prev;
117     }
118 
119     static Logger getInstance(final LoggerContext context, final String name, final PrivateAdapter factory) {
120         final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
121         Logger logger = loggers.get(name);
122         if (logger != null) {
123             return logger;
124         }
125         logger = factory.newLogger(name, context);
126         final Logger prev = loggers.putIfAbsent(name, logger);
127         return prev == null ? logger : prev;
128     }
129 
130     public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
131         return getInstance(clazz.getName());
132     }
133 
134     static Logger getInstance(final LoggerContext context, @SuppressWarnings("rawtypes") final Class clazz) {
135         return getInstance(context, clazz.getName());
136     }
137 
138     public final String getName() {
139         return logger.getName();
140     }
141 
142     org.apache.logging.log4j.Logger getLogger() {
143         return logger;
144     }
145 
146     public final Category getParent() {
147         if (!isCoreAvailable) {
148             return null;
149         }
150         org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
151         LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
152         if (parent == null || loggerContext == null) {
153             return null;
154         }
155         final ConcurrentMap<String, Logger> loggers = getLoggersMap(loggerContext);
156         final Logger l = loggers.get(parent.getName());
157         return l == null ? new Category(parent) : l;
158     }
159 
160     public static Category getRoot() {
161         return getInstance(Strings.EMPTY);
162     }
163 
164     static Logger getRoot(final LoggerContext context) {
165         return getInstance(context, Strings.EMPTY);
166     }
167 
168     private static ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
169         synchronized (CONTEXT_MAP) {
170             ConcurrentMap<String, Logger> map = CONTEXT_MAP.get(context);
171             if (map == null) {
172                 map = new ConcurrentHashMap<>();
173                 CONTEXT_MAP.put(context, map);
174             }
175             return map;
176         }
177     }
178 
179     /**
180      * Returns all the currently defined categories in the default hierarchy as an
181      * {@link java.util.Enumeration Enumeration}.
182      * 
183      * <p>
184      * The root category is <em>not</em> included in the returned
185      * {@link Enumeration}.
186      * </p>
187      * 
188      * @return and Enumeration of the Categories.
189      * 
190      * @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
191      */
192     @SuppressWarnings("rawtypes")
193     @Deprecated
194     public static Enumeration getCurrentCategories() {
195         return LogManager.getCurrentLoggers();
196     }
197 
198     public final Level getEffectiveLevel() {
199         switch (logger.getLevel().getStandardLevel()) {
200         case ALL:
201             return Level.ALL;
202         case TRACE:
203             return Level.TRACE;
204         case DEBUG:
205             return Level.DEBUG;
206         case INFO:
207             return Level.INFO;
208         case WARN:
209             return Level.WARN;
210         case ERROR:
211             return Level.ERROR;
212         case FATAL:
213             return Level.FATAL;
214         default:
215             // TODO Should this be an IllegalStateException?
216             return Level.OFF;
217         }
218     }
219 
220     public final Priority getChainedPriority() {
221         return getEffectiveLevel();
222     }
223 
224     public final Level getLevel() {
225         return getEffectiveLevel();
226     }
227 
228     private String getLevelStr(final Priority priority) {
229         return priority == null ? null : priority.levelStr;
230     }
231 
232     public void setLevel(final Level level) {
233         setLevel(getLevelStr(level));
234     }
235 
236     public final Level getPriority() {
237         return getEffectiveLevel();
238     }
239 
240     public void setPriority(final Priority priority) {
241         setLevel(getLevelStr(priority));
242     }
243 
244     private void setLevel(final String levelStr) {
245         if (isCoreAvailable) {
246             CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
247         }
248     }
249 
250     public void debug(final Object message) {
251         maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, null);
252     }
253 
254     public void debug(final Object message, final Throwable t) {
255         maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
256     }
257 
258     public boolean isDebugEnabled() {
259         return logger.isDebugEnabled();
260     }
261 
262     public void error(final Object message) {
263         maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
264     }
265 
266     public void error(final Object message, final Throwable t) {
267         maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
268     }
269 
270     public boolean isErrorEnabled() {
271         return logger.isErrorEnabled();
272     }
273 
274     public void warn(final Object message) {
275         maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
276     }
277 
278     public void warn(final Object message, final Throwable t) {
279         maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
280     }
281 
282     public boolean isWarnEnabled() {
283         return logger.isWarnEnabled();
284     }
285 
286     public void fatal(final Object message) {
287         maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
288     }
289 
290     public void fatal(final Object message, final Throwable t) {
291         maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
292     }
293 
294     public boolean isFatalEnabled() {
295         return logger.isFatalEnabled();
296     }
297 
298     public void info(final Object message) {
299         maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
300     }
301 
302     public void info(final Object message, final Throwable t) {
303         maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
304     }
305 
306     public boolean isInfoEnabled() {
307         return logger.isInfoEnabled();
308     }
309 
310     public void trace(final Object message) {
311         maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
312     }
313 
314     public void trace(final Object message, final Throwable t) {
315         maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
316     }
317 
318     public boolean isTraceEnabled() {
319         return logger.isTraceEnabled();
320     }
321 
322     public boolean isEnabledFor(final Priority level) {
323         final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
324         return isEnabledFor(lvl);
325     }
326 
327     /**
328      * No-op implementation.
329      * @param appender The Appender to add.
330      */
331     public void addAppender(final Appender appender) {
332     }
333 
334     /**
335      * No-op implementation.
336      * @param event The logging event.
337      */
338     public void callAppenders(final LoggingEvent event) {
339     }
340 
341     @SuppressWarnings("rawtypes")
342     public Enumeration getAllAppenders() {
343         return NullEnumeration.getInstance();
344     }
345 
346     /**
347      * No-op implementation.
348      * @param name The name of the Appender.
349      * @return null.
350      */
351     public Appender getAppender(final String name) {
352         return null;
353     }
354 
355     /**
356      Is the appender passed as parameter attached to this category?
357      * @param appender The Appender to add.
358      * @return true if the appender is attached.
359      */
360     public boolean isAttached(final Appender appender) {
361         return false;
362     }
363 
364     /**
365      * No-op implementation.
366      */
367     public void removeAllAppenders() {
368     }
369 
370     /**
371      * No-op implementation.
372      * @param appender The Appender to remove.
373      */
374     public void removeAppender(final Appender appender) {
375     }
376 
377     /**
378      * No-op implementation.
379      * @param name The Appender to remove.
380      */
381     public void removeAppender(final String name) {
382     }
383 
384     /**
385      * No-op implementation.
386      */
387     public static void shutdown() {
388     }
389 
390     public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
391         final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
392         if (logger instanceof ExtendedLogger) {
393             @SuppressWarnings("unchecked")
394             Message msg = message instanceof Message ? (Message) message : message instanceof Map ?
395                     new MapMessage((Map) message) : new ObjectMessage(message);
396             ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
397         } else {
398             ObjectRenderer renderer = get(message.getClass());
399             final Message msg = message instanceof Message ? (Message) message : renderer != null ?
400                     new RenderedMessage(renderer, message) : new ObjectMessage(message);
401             logger.log(lvl, msg, t);
402         }
403     }
404 
405     public boolean exists(final String name) {
406         return PrivateManager.getContext().hasLogger(name);
407     }
408 
409     public boolean getAdditivity() {
410         return isCoreAvailable ? CategoryUtil.isAdditive(logger) : false;
411     }
412 
413     public void setAdditivity(final boolean additivity) {
414         if (isCoreAvailable) {
415             CategoryUtil.setAdditivity(logger, additivity);
416         }
417     }
418 
419     public void setResourceBundle(final ResourceBundle bundle) {
420         this.bundle = bundle;
421     }
422 
423     public ResourceBundle getResourceBundle() {
424         if (bundle != null) {
425             return bundle;
426         }
427         String name = logger.getName();
428         if (isCoreAvailable) {
429             LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
430             if (ctx != null) {
431                 final ConcurrentMap<String, Logger> loggers = getLoggersMap(ctx);
432                 while ((name = getSubName(name)) != null) {
433                     final Logger subLogger = loggers.get(name);
434                     if (subLogger != null) {
435                         final ResourceBundle rb = subLogger.bundle;
436                         if (rb != null) {
437                             return rb;
438                         }
439                     }
440                 }
441             }
442         }
443         return null;
444     }
445 
446     private static  String getSubName(final String name) {
447         if (Strings.isEmpty(name)) {
448             return null;
449         }
450         final int i = name.lastIndexOf('.');
451         return i > 0 ? name.substring(0, i) : Strings.EMPTY;
452     }
453 
454     /**
455      * If <code>assertion</code> parameter is {@code false}, then logs
456      * <code>msg</code> as an {@link #error(Object) error} statement.
457      * 
458      * <p>
459      * The <code>assert</code> method has been renamed to <code>assertLog</code>
460      * because <code>assert</code> is a language reserved word in JDK 1.4.
461      * </p>
462      *
463      * @param assertion The assertion.
464      * @param msg       The message to print if <code>assertion</code> is false.
465      * 
466      * @since 1.2
467      */
468     public void assertLog(final boolean assertion, final String msg) {
469         if (!assertion) {
470             this.error(msg);
471         }
472     }
473 
474     public void l7dlog(final Priority priority, final String key, final Throwable t) {
475         if (isEnabledFor(priority)) {
476             final Message msg = new LocalizedMessage(bundle, key, null);
477             forcedLog(FQCN, priority, msg, t);
478         }
479     }
480 
481     public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
482         if (isEnabledFor(priority)) {
483             final Message msg = new LocalizedMessage(bundle, key, params);
484             forcedLog(FQCN, priority, msg, t);
485         }
486     }
487 
488     public void log(final Priority priority, final Object message, final Throwable t) {
489         if (isEnabledFor(priority)) {
490             @SuppressWarnings("unchecked")
491             final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
492             forcedLog(FQCN, priority, msg, t);
493         }
494     }
495 
496     public void log(final Priority priority, final Object message) {
497         if (isEnabledFor(priority)) {
498             @SuppressWarnings("unchecked")
499             final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
500             forcedLog(FQCN, priority, msg, null);
501         }
502     }
503 
504     public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
505         if (isEnabledFor(priority)) {
506             final Message msg = new ObjectMessage(message);
507             forcedLog(fqcn, priority, msg, t);
508         }
509     }
510 
511     private void maybeLog(
512             final String fqcn,
513             final org.apache.logging.log4j.Level level,
514             final Object message,
515             final Throwable throwable) {
516         if (logger.isEnabled(level)) {
517             final Message msg;
518             if (message instanceof String) {
519                 msg = new SimpleMessage((String) message);
520             }
521             // SimpleMessage treats String and CharSequence differently, hence
522             // this else-if block.
523             else if (message instanceof CharSequence) {
524                 msg = new SimpleMessage((CharSequence) message);
525             } else if (message instanceof Map) {
526                 @SuppressWarnings("unchecked")
527                 final Map<String, ?> map = (Map<String, ?>) message;
528                 msg = new MapMessage<>(map);
529             } else {
530                 msg = new ObjectMessage(message);
531             }
532             if (logger instanceof ExtendedLogger) {
533                 ((ExtendedLogger) logger).logMessage(fqcn, level, null, msg, throwable);
534             } else {
535                 logger.log(level, msg, throwable);
536             }
537         }
538     }
539 
540     private static class PrivateAdapter extends AbstractLoggerAdapter<Logger> {
541 
542         @Override
543         protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
544             return new Logger(context, name);
545         }
546 
547         @Override
548         protected org.apache.logging.log4j.spi.LoggerContext getContext() {
549             return PrivateManager.getContext();
550         }
551     }
552 
553     /**
554      * Private LogManager.
555      */
556     private static class PrivateManager extends org.apache.logging.log4j.LogManager {
557         private static final String FQCN = Category.class.getName();
558 
559         public static LoggerContext getContext() {
560             return getContext(FQCN, false);
561         }
562 
563         public static org.apache.logging.log4j.Logger getLogger(final String name) {
564             return getLogger(FQCN, name);
565         }
566     }
567 
568     private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
569         return logger.isEnabled(level);
570     }
571 
572     private <T> ObjectRenderer get(Class<T> clazz) {
573         ObjectRenderer renderer = null;
574         for (Class<? super T> c = clazz; c != null; c = c.getSuperclass()) {
575             renderer = rendererMap.get(c);
576             if (renderer != null) {
577                 return renderer;
578             }
579             renderer = searchInterfaces(c);
580             if (renderer != null) {
581                 return renderer;
582             }
583         }
584         return null;
585     }
586 
587     ObjectRenderer searchInterfaces(Class<?> c) {
588         ObjectRenderer renderer = rendererMap.get(c);
589         if (renderer != null) {
590             return renderer;
591         }
592         Class<?>[] ia = c.getInterfaces();
593         for (Class<?> clazz : ia) {
594             renderer = searchInterfaces(clazz);
595             if (renderer != null) {
596                 return renderer;
597             }
598         }
599         return null;
600     }
601 
602 }