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