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 */
017package org.apache.log4j;
018
019import java.util.Enumeration;
020import java.util.Map;
021import java.util.ResourceBundle;
022import java.util.WeakHashMap;
023import java.util.concurrent.ConcurrentHashMap;
024import java.util.concurrent.ConcurrentMap;
025
026import org.apache.log4j.helpers.NullEnumeration;
027import org.apache.log4j.legacy.core.CategoryUtil;
028import org.apache.log4j.or.ObjectRenderer;
029import org.apache.log4j.or.RendererSupport;
030import org.apache.log4j.spi.LoggerFactory;
031import org.apache.log4j.spi.LoggingEvent;
032import org.apache.logging.log4j.message.MapMessage;
033import org.apache.logging.log4j.message.SimpleMessage;
034import org.apache.logging.log4j.spi.ExtendedLogger;
035import org.apache.logging.log4j.spi.LoggerContext;
036import org.apache.logging.log4j.message.LocalizedMessage;
037import org.apache.logging.log4j.message.Message;
038import org.apache.logging.log4j.message.ObjectMessage;
039import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
040import org.apache.logging.log4j.util.Strings;
041
042
043/**
044 * Implementation of the Category class for compatibility, despite it having been deprecated a long, long time ago.
045 */
046public class Category {
047
048    private static PrivateAdapter adapter = new PrivateAdapter();
049
050    private static final Map<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP =
051        new WeakHashMap<>();
052
053    private static final String FQCN = Category.class.getName();
054
055    private static final boolean isCoreAvailable;
056
057    private final Map<Class<?>, ObjectRenderer> rendererMap;
058
059    static {
060        boolean available;
061
062        try {
063            available = Class.forName("org.apache.logging.log4j.core.Logger") != null;
064        } catch (Exception ex) {
065            available = false;
066        }
067        isCoreAvailable = available;
068    }
069
070    /**
071     * Resource bundle for localized messages.
072     */
073    protected ResourceBundle bundle = null;
074
075    private final org.apache.logging.log4j.Logger logger;
076
077    /**
078     * Constructor used by Logger to specify a LoggerContext.
079     * @param context The LoggerContext.
080     * @param name The name of the Logger.
081     */
082    protected Category(final LoggerContext context, final String name) {
083        this.logger = context.getLogger(name);
084        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
085    }
086
087    /**
088     * Constructor exposed by Log4j 1.2.
089     * @param name The name of the Logger.
090     */
091    protected Category(final String name) {
092        this(PrivateManager.getContext(), name);
093    }
094
095    private Category(final org.apache.logging.log4j.Logger logger) {
096        this.logger = logger;
097        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
098    }
099
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}