001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 * <p>
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 * <p>
009 * or (per the licensee's choosing)
010 * <p>
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic;
015
016import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.ScheduledFuture;
026import java.util.concurrent.locks.ReentrantLock;
027
028import ch.qos.logback.classic.util.LogbackMDCAdapter;
029import ch.qos.logback.core.status.ErrorStatus;
030import ch.qos.logback.core.status.InfoStatus;
031import org.slf4j.ILoggerFactory;
032import org.slf4j.Marker;
033
034import ch.qos.logback.classic.spi.LoggerComparator;
035import ch.qos.logback.classic.spi.LoggerContextListener;
036import ch.qos.logback.classic.spi.LoggerContextVO;
037import ch.qos.logback.classic.spi.TurboFilterList;
038import ch.qos.logback.classic.turbo.TurboFilter;
039import ch.qos.logback.classic.util.LoggerNameUtil;
040import ch.qos.logback.core.ContextBase;
041import ch.qos.logback.core.boolex.EventEvaluator;
042import ch.qos.logback.core.spi.FilterReply;
043import ch.qos.logback.core.spi.LifeCycle;
044import ch.qos.logback.core.spi.SequenceNumberGenerator;
045import ch.qos.logback.core.status.StatusListener;
046import ch.qos.logback.core.status.StatusManager;
047import ch.qos.logback.core.status.WarnStatus;
048import org.slf4j.spi.MDCAdapter;
049
050/**
051 * LoggerContext glues many of the logback-classic components together. In
052 * principle, every logback-classic component instance is attached either
053 * directly or indirectly to a LoggerContext instance. Just as importantly
054 * LoggerContext implements the {@link ILoggerFactory} acting as the
055 * manufacturing source of {@link Logger} instances.
056 *
057 * @author Ceki Gulcu
058 */
059public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
060
061    /**
062     * Default setting of packaging data in stack traces
063     */
064    public static final boolean DEFAULT_PACKAGING_DATA = false;
065
066    final Logger root;
067    private int size;
068    private int noAppenderWarning = 0;
069    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
070
071    private Map<String, Logger> loggerCache;
072
073    private LoggerContextVO loggerContextRemoteView;
074    private final TurboFilterList turboFilterList = new TurboFilterList();
075    private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;
076    SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator
077
078    MDCAdapter mdcAdapter;
079
080    private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
081
082    int resetCount = 0;
083    private List<String> frameworkPackages;
084
085    public LoggerContext() {
086        super();
087        this.loggerCache = new ConcurrentHashMap<String, Logger>();
088
089        this.loggerContextRemoteView = new LoggerContextVO(this);
090        this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
091        this.root.setLevel(Level.DEBUG);
092        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
093        initEvaluatorMap();
094        size = 1;
095        this.frameworkPackages = new ArrayList<String>();
096        // In 1.5.7, the stop() method assumes that at some point the context has been started
097        // since earlier versions of logback did not mandate calling the start method
098        // we need to call in the constructor
099        this.start();
100    }
101
102    void initEvaluatorMap() {
103        putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>());
104    }
105
106    /**
107     * A new instance of LoggerContextRemoteView needs to be created each time the
108     * name or propertyMap (including keys or values) changes.
109     */
110    private void updateLoggerContextVO() {
111        loggerContextRemoteView = new LoggerContextVO(this);
112    }
113
114    @Override
115    public void putProperty(String key, String val) {
116        super.putProperty(key, val);
117        updateLoggerContextVO();
118    }
119
120    @Override
121    public void setName(String name) {
122        super.setName(name);
123        updateLoggerContextVO();
124    }
125
126    public final Logger getLogger(final Class<?> clazz) {
127        return getLogger(clazz.getName());
128    }
129
130    @Override
131    public Logger getLogger(final String name) {
132
133        if (name == null) {
134            throw new IllegalArgumentException("name argument cannot be null");
135        }
136
137        // if we are asking for the root logger, then let us return it without
138        // wasting time
139        if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
140            return root;
141        }
142
143        int i = 0;
144        Logger logger = root;
145
146        // check if the desired logger exists, if it does, return it
147        // without further ado.
148        Logger childLogger = (Logger) loggerCache.get(name);
149        // if we have the child, then let us return it without wasting time
150        if (childLogger != null) {
151            return childLogger;
152        }
153
154        // if the desired logger does not exist, them create all the loggers
155        // in between as well (if they don't already exist)
156        String childName;
157        while (true) {
158            int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
159            if (h == -1) {
160                childName = name;
161            } else {
162                childName = name.substring(0, h);
163            }
164            // move i left of the last point
165            i = h + 1;
166            synchronized (logger) {
167                childLogger = logger.getChildByName(childName);
168                if (childLogger == null) {
169                    childLogger = logger.createChildByName(childName);
170                    loggerCache.put(childName, childLogger);
171                    incSize();
172                }
173            }
174            logger = childLogger;
175            if (h == -1) {
176                return childLogger;
177            }
178        }
179    }
180
181    private void incSize() {
182        size++;
183    }
184
185    int size() {
186        return size;
187    }
188
189    /**
190     * Check if the named logger exists in the hierarchy. If so return its
191     * reference, otherwise returns <code>null</code>.
192     *
193     * @param name the name of the logger to search for.
194     */
195    public Logger exists(String name) {
196        return (Logger) loggerCache.get(name);
197    }
198
199    final void noAppenderDefinedWarning(final Logger logger) {
200        if (noAppenderWarning++ == 0) {
201            getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger));
202        }
203    }
204
205    public List<Logger> getLoggerList() {
206        Collection<Logger> collection = loggerCache.values();
207        List<Logger> loggerList = new ArrayList<Logger>(collection);
208        Collections.sort(loggerList, new LoggerComparator());
209        return loggerList;
210    }
211
212    public LoggerContextVO getLoggerContextRemoteView() {
213        return loggerContextRemoteView;
214    }
215
216    public void setPackagingDataEnabled(boolean packagingDataEnabled) {
217        this.packagingDataEnabled = packagingDataEnabled;
218    }
219
220    public boolean isPackagingDataEnabled() {
221        return packagingDataEnabled;
222    }
223
224    void cancelScheduledTasks() {
225
226        try {
227            configurationLock.lock();
228
229            for (ScheduledFuture<?> sf : scheduledFutures) {
230                sf.cancel(false);
231            }
232            scheduledFutures.clear();
233        } finally {
234            configurationLock.unlock();
235        }
236    }
237
238    private void resetStatusListenersExceptResetResistant() {
239        StatusManager sm = getStatusManager();
240        for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
241            if (!sl.isResetResistant()) {
242                sm.remove(sl);
243            }
244        }
245    }
246
247    public TurboFilterList getTurboFilterList() {
248        return turboFilterList;
249    }
250
251    public void addTurboFilter(TurboFilter newFilter) {
252        turboFilterList.add(newFilter);
253    }
254
255    /**
256     * First processPriorToRemoval all registered turbo filters and then clear the
257     * registration list.
258     */
259    public void resetTurboFilterList() {
260        for (TurboFilter tf : turboFilterList) {
261            tf.stop();
262        }
263        turboFilterList.clear();
264    }
265
266    final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
267                    final Object[] params, final Throwable t) {
268        if (turboFilterList.size() == 0) {
269            return FilterReply.NEUTRAL;
270        }
271        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
272    }
273
274    final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logger logger, final Level level, final String format, final Object param,
275                    final Throwable t) {
276        if (turboFilterList.size() == 0) {
277            return FilterReply.NEUTRAL;
278        }
279        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param }, t);
280    }
281
282    final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1,
283                    final Object param2, final Throwable t) {
284        if (turboFilterList.size() == 0) {
285            return FilterReply.NEUTRAL;
286        }
287        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t);
288    }
289
290    // === start listeners ==============================================
291    public void addListener(LoggerContextListener listener) {
292        loggerContextListenerList.add(listener);
293    }
294
295    public void removeListener(LoggerContextListener listener) {
296        loggerContextListenerList.remove(listener);
297    }
298
299    private void resetListenersExceptResetResistant() {
300        List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
301
302        for (LoggerContextListener lcl : loggerContextListenerList) {
303            if (lcl.isResetResistant()) {
304                toRetain.add(lcl);
305            }
306        }
307        loggerContextListenerList.retainAll(toRetain);
308    }
309
310    private void resetAllListeners() {
311        loggerContextListenerList.clear();
312    }
313
314    public List<LoggerContextListener> getCopyOfListenerList() {
315        return new ArrayList<LoggerContextListener>(loggerContextListenerList);
316    }
317
318    void fireOnLevelChange(Logger logger, Level level) {
319        for (LoggerContextListener listener : loggerContextListenerList) {
320            listener.onLevelChange(logger, level);
321        }
322    }
323
324    private void fireOnReset() {
325        for (LoggerContextListener listener : loggerContextListenerList) {
326            listener.onReset(this);
327        }
328    }
329
330    private void fireOnStart() {
331        for (LoggerContextListener listener : loggerContextListenerList) {
332            listener.onStart(this);
333        }
334    }
335
336    private void fireOnStop() {
337        for (LoggerContextListener listener : loggerContextListenerList) {
338            listener.onStop(this);
339        }
340    }
341
342    // === end listeners ==============================================
343
344    @Override
345    public void start() {
346        super.start();
347        fireOnStart();
348    }
349
350    public void stop() {
351        if (!isStarted())
352            return;
353
354        try {
355            configurationLock.lock();
356            if (!isStarted())
357                return;
358
359            reset();
360            fireOnStop();
361            resetAllListeners();
362            super.stop();
363        } finally {
364            configurationLock.unlock();
365        }
366    }
367
368    /**
369     * This method clears all internal properties, except internal status messages,
370     * closes all appenders, removes any turboFilters, fires an OnReset event,
371     * removes all status listeners, removes all context listeners (except those
372     * which are reset resistant).
373     * <p/>
374     * As mentioned above, internal status messages survive resets.
375     */
376    @Override
377    public void reset() {
378        resetCount++;
379        super.reset();
380        initEvaluatorMap();
381        initCollisionMaps();
382        root.recursiveReset();
383        resetTurboFilterList();
384        cancelScheduledTasks();
385        fireOnReset();
386        resetListenersExceptResetResistant();
387        resetStatusListenersExceptResetResistant();
388    }
389
390    @Override
391    public String toString() {
392        return this.getClass().getName() + "[" + getName() + "]";
393    }
394
395    public int getMaxCallerDataDepth() {
396        return maxCallerDataDepth;
397    }
398
399    public void setMaxCallerDataDepth(int maxCallerDataDepth) {
400        this.maxCallerDataDepth = maxCallerDataDepth;
401    }
402
403    /**
404     * List of packages considered part of the logging framework such that they are
405     * never considered as callers of the logging framework. This list used to
406     * compute the caller for logging events.
407     * <p/>
408     * To designate package "com.foo" as well as all its subpackages as being part
409     * of the logging framework, simply add "com.foo" to this list.
410     *
411     * @return list of framework packages
412     */
413    public List<String> getFrameworkPackages() {
414        return frameworkPackages;
415    }
416
417    @Override
418    public void setSequenceNumberGenerator(SequenceNumberGenerator sng) {
419        this.sequenceNumberGenerator = sng;
420    }
421
422    @Override
423    public SequenceNumberGenerator getSequenceNumberGenerator() {
424        return sequenceNumberGenerator;
425    }
426
427    public MDCAdapter getMDCAdapter() {
428        return mdcAdapter;
429    }
430
431    public void setMDCAdapter(MDCAdapter anAdapter) {
432        if (this.mdcAdapter != null) {
433            StatusManager sm = getStatusManager();
434            sm.add(new WarnStatus("mdcAdapter being reset a second time", this));
435        }
436        this.mdcAdapter = anAdapter;
437    }
438}