1 /**
2  * Logback: the reliable, generic, fast and flexible logging framework.
3  * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4  *
5  * This program and the accompanying materials are dual-licensed under
6  * either the terms of the Eclipse Public License v1.0 as published by
7  * the Eclipse Foundation
8  *
9  *   or (per the licensee's choosing)
10  *
11  * under the terms of the GNU Lesser General Public License version 2.1
12  * as published by the Free Software Foundation.
13  */

14 package ch.qos.logback.classic;
15
16 import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ScheduledFuture;
26
27 import org.slf4j.ILoggerFactory;
28 import org.slf4j.Marker;
29
30 import ch.qos.logback.classic.spi.LoggerComparator;
31 import ch.qos.logback.classic.spi.LoggerContextListener;
32 import ch.qos.logback.classic.spi.LoggerContextVO;
33 import ch.qos.logback.classic.spi.TurboFilterList;
34 import ch.qos.logback.classic.turbo.TurboFilter;
35 import ch.qos.logback.classic.util.LoggerNameUtil;
36 import ch.qos.logback.core.ContextBase;
37 import ch.qos.logback.core.boolex.EventEvaluator;
38 import ch.qos.logback.core.spi.FilterReply;
39 import ch.qos.logback.core.spi.LifeCycle;
40 import ch.qos.logback.core.status.StatusListener;
41 import ch.qos.logback.core.status.StatusManager;
42 import ch.qos.logback.core.status.WarnStatus;
43
44 /**
45  * LoggerContext glues many of the logback-classic components together. In
46  * principle, every logback-classic component instance is attached either
47  * directly or indirectly to a LoggerContext instance. Just as importantly
48  * LoggerContext implements the {@link ILoggerFactory} acting as the
49  * manufacturing source of {@link Logger} instances.
50  *
51  * @author Ceki Gulcu
52  */

53 public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
54
55     /** Default setting of packaging data in stack traces */
56     public static final boolean DEFAULT_PACKAGING_DATA = false;
57
58     final Logger root;
59     private int size;
60     private int noAppenderWarning = 0;
61     final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
62
63     private Map<String, Logger> loggerCache;
64
65     private LoggerContextVO loggerContextRemoteView;
66     private final TurboFilterList turboFilterList = new TurboFilterList();
67     private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;
68
69     private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
70
71     int resetCount = 0;
72     private List<String> frameworkPackages;
73
74     public LoggerContext() {
75         super();
76         this.loggerCache = new ConcurrentHashMap<String, Logger>();
77
78         this.loggerContextRemoteView = new LoggerContextVO(this);
79         this.root = new Logger(Logger.ROOT_LOGGER_NAME, nullthis);
80         this.root.setLevel(Level.DEBUG);
81         loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
82         initEvaluatorMap();
83         size = 1;
84         this.frameworkPackages = new ArrayList<String>();
85     }
86
87     void initEvaluatorMap() {
88         putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>());
89     }
90
91     /**
92      * A new instance of LoggerContextRemoteView needs to be created each time the
93      * name or propertyMap (including keys or values) changes.
94      */

95     private void updateLoggerContextVO() {
96         loggerContextRemoteView = new LoggerContextVO(this);
97     }
98
99     @Override
100     public void putProperty(String key, String val) {
101         super.putProperty(key, val);
102         updateLoggerContextVO();
103     }
104
105     @Override
106     public void setName(String name) {
107         super.setName(name);
108         updateLoggerContextVO();
109     }
110
111     public final Logger getLogger(final Class<?> clazz) {
112         return getLogger(clazz.getName());
113     }
114
115     @Override
116     public final Logger getLogger(final String name) {
117
118         if (name == null) {
119             throw new IllegalArgumentException("name argument cannot be null");
120         }
121
122         // if we are asking for the root logger, then let us return it without
123         // wasting time
124         if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
125             return root;
126         }
127
128         int i = 0;
129         Logger logger = root;
130
131         // check if the desired logger exists, if it does, return it
132         // without further ado.
133         Logger childLogger = (Logger) loggerCache.get(name);
134         // if we have the child, then let us return it without wasting time
135         if (childLogger != null) {
136             return childLogger;
137         }
138
139         // if the desired logger does not exist, them create all the loggers
140         // in between as well (if they don't already exist)
141         String childName;
142         while (true) {
143             int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
144             if (h == -1) {
145                 childName = name;
146             } else {
147                 childName = name.substring(0, h);
148             }
149             // move i left of the last point
150             i = h + 1;
151             synchronized (logger) {
152                 childLogger = logger.getChildByName(childName);
153                 if (childLogger == null) {
154                     childLogger = logger.createChildByName(childName);
155                     loggerCache.put(childName, childLogger);
156                     incSize();
157                 }
158             }
159             logger = childLogger;
160             if (h == -1) {
161                 return childLogger;
162             }
163         }
164     }
165
166     private void incSize() {
167         size++;
168     }
169
170     int size() {
171         return size;
172     }
173
174     /**
175      * Check if the named logger exists in the hierarchy. If so return its
176      * reference, otherwise returns <code>null</code>.
177      *
178      * @param name the name of the logger to search for.
179      */

180     public Logger exists(String name) {
181         return (Logger) loggerCache.get(name);
182     }
183
184     final void noAppenderDefinedWarning(final Logger logger) {
185         if (noAppenderWarning++ == 0) {
186             getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger));
187         }
188     }
189
190     public List<Logger> getLoggerList() {
191         Collection<Logger> collection = loggerCache.values();
192         List<Logger> loggerList = new ArrayList<Logger>(collection);
193         Collections.sort(loggerList, new LoggerComparator());
194         return loggerList;
195     }
196
197     public LoggerContextVO getLoggerContextRemoteView() {
198         return loggerContextRemoteView;
199     }
200
201     public void setPackagingDataEnabled(boolean packagingDataEnabled) {
202         this.packagingDataEnabled = packagingDataEnabled;
203     }
204
205     public boolean isPackagingDataEnabled() {
206         return packagingDataEnabled;
207     }
208
209     /**
210      * This method clears all internal properties, except internal status messages,
211      * closes all appenders, removes any turboFilters, fires an OnReset event,
212      * removes all status listeners, removes all context listeners
213      * (except those which are reset resistant).
214      * <p/>
215      * As mentioned above, internal status messages survive resets.
216      */

217     @Override
218     public void reset() {
219         resetCount++;
220         super.reset();
221         initEvaluatorMap();
222         initCollisionMaps();
223         root.recursiveReset();
224         resetTurboFilterList();
225         cancelScheduledTasks();
226         fireOnReset();
227         resetListenersExceptResetResistant();
228         resetStatusListeners();
229     }
230
231     private void cancelScheduledTasks() {
232         for(ScheduledFuture<?> sf: scheduledFutures) {
233             sf.cancel(false);
234         }
235         scheduledFutures.clear();
236     }
237
238     private void resetStatusListeners() {
239         StatusManager sm = getStatusManager();
240         for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
241             sm.remove(sl);
242         }
243     }
244
245     public TurboFilterList getTurboFilterList() {
246         return turboFilterList;
247     }
248
249     public void addTurboFilter(TurboFilter newFilter) {
250         turboFilterList.add(newFilter);
251     }
252
253     /**
254      * First processPriorToRemoval all registered turbo filters and then clear the registration
255      * list.
256      */

257     public void resetTurboFilterList() {
258         for (TurboFilter tf : turboFilterList) {
259             tf.stop();
260         }
261         turboFilterList.clear();
262     }
263
264     final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
265                     final Object[] params, final Throwable t) {
266         if (turboFilterList.size() == 0) {
267             return FilterReply.NEUTRAL;
268         }
269         return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
270     }
271
272     final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logger logger, final Level level, final String format, final Object param,
273                     final Throwable t) {
274         if (turboFilterList.size() == 0) {
275             return FilterReply.NEUTRAL;
276         }
277         return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param }, t);
278     }
279
280     final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1,
281                     final Object param2, final Throwable t) {
282         if (turboFilterList.size() == 0) {
283             return FilterReply.NEUTRAL;
284         }
285         return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t);
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     public void start() {
343         super.start();
344         fireOnStart();
345     }
346
347     public void stop() {
348         reset();
349         fireOnStop();
350         resetAllListeners();
351         super.stop();
352     }
353
354     @Override
355     public String toString() {
356         return this.getClass().getName() + "[" + getName() + "]";
357     }
358
359     public int getMaxCallerDataDepth() {
360         return maxCallerDataDepth;
361     }
362
363     public void setMaxCallerDataDepth(int maxCallerDataDepth) {
364         this.maxCallerDataDepth = maxCallerDataDepth;
365     }
366
367     /**
368      * List of packages considered part of the logging framework such that they are never considered
369      * as callers of the logging framework. This list used to compute the caller for logging events.
370      * <p/>
371      * To designate package "com.foo" as well as all its subpackages as being part of the logging framework, simply add
372      * "com.foo" to this list.
373      *
374      * @return list of framework packages
375      */

376     public List<String> getFrameworkPackages() {
377         return frameworkPackages;
378     }
379 }
380