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.spi.LoggerFactory;
029import org.apache.log4j.spi.LoggingEvent;
030import org.apache.logging.log4j.spi.ExtendedLogger;
031import org.apache.logging.log4j.spi.LoggerContext;
032import org.apache.logging.log4j.message.LocalizedMessage;
033import org.apache.logging.log4j.message.Message;
034import org.apache.logging.log4j.message.ObjectMessage;
035import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
036import org.apache.logging.log4j.util.Strings;
037
038
039/**
040 * Implementation of the Category class for compatibility, despite it having been deprecated a long, long time ago.
041 */
042public class Category {
043
044    private static PrivateAdapter adapter = new PrivateAdapter();
045
046    private static final Map<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP =
047        new WeakHashMap<>();
048
049    private static final String FQCN = Category.class.getName();
050
051    private static final boolean isCoreAvailable;
052
053
054    static {
055        boolean available;
056
057        try {
058            available = Class.forName("org.apache.logging.log4j.core.Logger") != null;
059        } catch (Exception ex) {
060            available = false;
061        }
062        isCoreAvailable = available;
063    }
064
065    /**
066     * Resource bundle for localized messages.
067     */
068    protected ResourceBundle bundle = null;
069
070    private final org.apache.logging.log4j.Logger logger;
071
072    /**
073     * Constructor used by Logger to specify a LoggerContext.
074     * @param context The LoggerContext.
075     * @param name The name of the Logger.
076     */
077    protected Category(final LoggerContext context, final String name) {
078        this.logger = context.getLogger(name);
079    }
080
081    /**
082     * Constructor exposed by Log4j 1.2.
083     * @param name The name of the Logger.
084     */
085    protected Category(final String name) {
086        this(PrivateManager.getContext(), name);
087    }
088
089    private Category(final org.apache.logging.log4j.Logger logger) {
090        this.logger = logger;
091    }
092
093    public static Category getInstance(final String name) {
094        return getInstance(PrivateManager.getContext(), name, adapter);
095    }
096
097    static Logger getInstance(final LoggerContext context, final String name) {
098        return getInstance(context, name, adapter);
099    }
100
101    static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
102        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
103        Logger logger = loggers.get(name);
104        if (logger != null) {
105            return logger;
106        }
107        logger = factory.makeNewLoggerInstance(name);
108        final Logger prev = loggers.putIfAbsent(name, logger);
109        return prev == null ? logger : prev;
110    }
111
112    static Logger getInstance(final LoggerContext context, final String name, final PrivateAdapter factory) {
113        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
114        Logger logger = loggers.get(name);
115        if (logger != null) {
116            return logger;
117        }
118        logger = factory.newLogger(name, context);
119        final Logger prev = loggers.putIfAbsent(name, logger);
120        return prev == null ? logger : prev;
121    }
122
123    public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
124        return getInstance(clazz.getName());
125    }
126
127    static Logger getInstance(final LoggerContext context, @SuppressWarnings("rawtypes") final Class clazz) {
128        return getInstance(context, clazz.getName());
129    }
130
131    public final String getName() {
132        return logger.getName();
133    }
134
135    org.apache.logging.log4j.Logger getLogger() {
136        return logger;
137    }
138
139    public final Category getParent() {
140        if (!isCoreAvailable) {
141            return null;
142        }
143        org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
144        LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
145        if (parent == null || loggerContext == null) {
146            return null;
147        }
148        final ConcurrentMap<String, Logger> loggers = getLoggersMap(loggerContext);
149        final Logger l = loggers.get(parent.getName());
150        return l == null ? new Category(parent) : l;
151    }
152
153    public static Category getRoot() {
154        return getInstance(Strings.EMPTY);
155    }
156
157    static Logger getRoot(final LoggerContext context) {
158        return getInstance(context, Strings.EMPTY);
159    }
160
161    private static ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
162        synchronized (CONTEXT_MAP) {
163            ConcurrentMap<String, Logger> map = CONTEXT_MAP.get(context);
164            if (map == null) {
165                map = new ConcurrentHashMap<>();
166                CONTEXT_MAP.put(context, map);
167            }
168            return map;
169        }
170    }
171
172    /**
173     Returns all the currently defined categories in the default
174     hierarchy as an {@link java.util.Enumeration Enumeration}.
175
176     <p>The root category is <em>not</em> included in the returned
177     {@link Enumeration}.
178     @return and Enumeration of the Categories.
179
180     @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
181     */
182    @SuppressWarnings("rawtypes")
183    @Deprecated
184    public static Enumeration getCurrentCategories() {
185        return LogManager.getCurrentLoggers();
186    }
187
188    public final Level getEffectiveLevel() {
189        switch (logger.getLevel().getStandardLevel()) {
190        case ALL:
191            return Level.ALL;
192        case TRACE:
193            return Level.TRACE;
194        case DEBUG:
195            return Level.DEBUG;
196        case INFO:
197            return Level.INFO;
198        case WARN:
199            return Level.WARN;
200        case ERROR:
201            return Level.ERROR;
202        case FATAL:
203            return Level.FATAL;
204        default:
205            // TODO Should this be an IllegalStateException?
206            return Level.OFF;
207        }
208    }
209
210    public final Priority getChainedPriority() {
211        return getEffectiveLevel();
212    }
213
214    public final Level getLevel() {
215        return getEffectiveLevel();
216    }
217
218    public void setLevel(final Level level) {
219        setLevel(level.levelStr);
220    }
221
222    public final Level getPriority() {
223        return getEffectiveLevel();
224    }
225
226    public void setPriority(final Priority priority) {
227        setLevel(priority.levelStr);
228    }
229
230    private void setLevel(final String levelStr) {
231        if (isCoreAvailable) {
232            CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
233        }
234    }
235
236    public void debug(final Object message) {
237        maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, null);
238    }
239
240    public void debug(final Object message, final Throwable t) {
241        maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
242    }
243
244    public boolean isDebugEnabled() {
245        return logger.isDebugEnabled();
246    }
247
248    public void error(final Object message) {
249        maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
250    }
251
252    public void error(final Object message, final Throwable t) {
253        maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
254    }
255
256    public boolean isErrorEnabled() {
257        return logger.isErrorEnabled();
258    }
259
260    public void warn(final Object message) {
261        maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
262    }
263
264    public void warn(final Object message, final Throwable t) {
265        maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
266    }
267
268    public boolean isWarnEnabled() {
269        return logger.isWarnEnabled();
270    }
271
272    public void fatal(final Object message) {
273        maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
274    }
275
276    public void fatal(final Object message, final Throwable t) {
277        maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
278    }
279
280    public boolean isFatalEnabled() {
281        return logger.isFatalEnabled();
282    }
283
284    public void info(final Object message) {
285        maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
286    }
287
288    public void info(final Object message, final Throwable t) {
289        maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
290    }
291
292    public boolean isInfoEnabled() {
293        return logger.isInfoEnabled();
294    }
295
296    public void trace(final Object message) {
297        maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
298    }
299
300    public void trace(final Object message, final Throwable t) {
301        maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
302    }
303
304    public boolean isTraceEnabled() {
305        return logger.isTraceEnabled();
306    }
307
308    public boolean isEnabledFor(final Priority level) {
309        final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
310        return isEnabledFor(lvl);
311    }
312
313    /**
314     * No-op implementation.
315     * @param appender The Appender to add.
316     */
317    public void addAppender(final Appender appender) {
318    }
319
320    /**
321     * No-op implementation.
322     * @param event The logging event.
323     */
324    public void callAppenders(final LoggingEvent event) {
325    }
326
327    @SuppressWarnings("rawtypes")
328    public Enumeration getAllAppenders() {
329        return NullEnumeration.getInstance();
330    }
331
332    /**
333     * No-op implementation.
334     * @param name The name of the Appender.
335     * @return null.
336     */
337    public Appender getAppender(final String name) {
338        return null;
339    }
340
341    /**
342     Is the appender passed as parameter attached to this category?
343     * @param appender The Appender to add.
344     * @return true if the appender is attached.
345     */
346    public boolean isAttached(final Appender appender) {
347        return false;
348    }
349
350    /**
351     * No-op implementation.
352     */
353    public void removeAllAppenders() {
354    }
355
356    /**
357     * No-op implementation.
358     * @param appender The Appender to remove.
359     */
360    public void removeAppender(final Appender appender) {
361    }
362
363    /**
364     * No-op implementation.
365     * @param name The Appender to remove.
366     */
367    public void removeAppender(final String name) {
368    }
369
370    /**
371     * No-op implementation.
372     */
373    public static void shutdown() {
374    }
375
376
377    public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
378        final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
379        final Message msg = message instanceof Message ? (Message) message : new ObjectMessage(message);
380        if (logger instanceof ExtendedLogger) {
381            ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, new ObjectMessage(message), t);
382        } else {
383            logger.log(lvl, msg, t);
384        }
385    }
386
387    public boolean exists(final String name) {
388        return PrivateManager.getContext().hasLogger(name);
389    }
390
391    public boolean getAdditivity() {
392        return isCoreAvailable ? CategoryUtil.isAdditive(logger) : false;
393    }
394
395    public void setAdditivity(final boolean additivity) {
396        if (isCoreAvailable) {
397            CategoryUtil.setAdditivity(logger, additivity);
398        }
399    }
400
401    public void setResourceBundle(final ResourceBundle bundle) {
402        this.bundle = bundle;
403    }
404
405    public ResourceBundle getResourceBundle() {
406        if (bundle != null) {
407            return bundle;
408        }
409        String name = logger.getName();
410        if (isCoreAvailable) {
411            LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
412            if (ctx != null) {
413                final ConcurrentMap<String, Logger> loggers = getLoggersMap(ctx);
414                while ((name = getSubName(name)) != null) {
415                    final Logger subLogger = loggers.get(name);
416                    if (subLogger != null) {
417                        final ResourceBundle rb = subLogger.bundle;
418                        if (rb != null) {
419                            return rb;
420                        }
421                    }
422                }
423            }
424        }
425        return null;
426    }
427
428    private static  String getSubName(final String name) {
429        if (Strings.isEmpty(name)) {
430            return null;
431        }
432        final int i = name.lastIndexOf('.');
433        return i > 0 ? name.substring(0, i) : Strings.EMPTY;
434    }
435
436    /**
437     If <code>assertion</code> parameter is {@code false}, then
438     logs <code>msg</code> as an {@link #error(Object) error} statement.
439
440     <p>The <code>assert</code> method has been renamed to
441     <code>assertLog</code> because <code>assert</code> is a language
442     reserved word in JDK 1.4.
443
444     @param assertion The assertion.
445     @param msg The message to print if <code>assertion</code> is
446     false.
447
448     @since 1.2
449     */
450    public void assertLog(final boolean assertion, final String msg) {
451        if (!assertion) {
452            this.error(msg);
453        }
454    }
455
456    public void l7dlog(final Priority priority, final String key, final Throwable t) {
457        if (isEnabledFor(priority)) {
458            final Message msg = new LocalizedMessage(bundle, key, null);
459            forcedLog(FQCN, priority, msg, t);
460        }
461    }
462
463    public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
464        if (isEnabledFor(priority)) {
465            final Message msg = new LocalizedMessage(bundle, key, params);
466            forcedLog(FQCN, priority, msg, t);
467        }
468    }
469
470    public void log(final Priority priority, final Object message, final Throwable t) {
471        if (isEnabledFor(priority)) {
472            final Message msg = new ObjectMessage(message);
473            forcedLog(FQCN, priority, msg, t);
474        }
475    }
476
477    public void log(final Priority priority, final Object message) {
478        if (isEnabledFor(priority)) {
479            final Message msg = new ObjectMessage(message);
480            forcedLog(FQCN, priority, msg, null);
481        }
482    }
483
484    public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
485        if (isEnabledFor(priority)) {
486            final Message msg = new ObjectMessage(message);
487            forcedLog(fqcn, priority, msg, t);
488        }
489    }
490
491    private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
492            final Object message, final Throwable throwable) {
493        if (logger.isEnabled(level)) {
494            if (logger instanceof ExtendedLogger) {
495                ((ExtendedLogger) logger).logMessage(fqcn, level, null, new ObjectMessage(message), throwable);
496            } else {
497                logger.log(level, message, throwable);
498            }
499        }
500    }
501
502    private static class PrivateAdapter extends AbstractLoggerAdapter<Logger> {
503
504        @Override
505        protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
506            return new Logger(context, name);
507        }
508
509        @Override
510        protected org.apache.logging.log4j.spi.LoggerContext getContext() {
511            return PrivateManager.getContext();
512        }
513    }
514
515    /**
516     * Private LogManager.
517     */
518    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
519        private static final String FQCN = Category.class.getName();
520
521        public static LoggerContext getContext() {
522            return getContext(FQCN, false);
523        }
524
525        public static org.apache.logging.log4j.Logger getLogger(final String name) {
526            return getLogger(FQCN, name);
527        }
528    }
529
530    private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
531        return logger.isEnabled(level);
532    }
533
534}