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