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