1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    * <p>
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    * <p>
9    * or (per the licensee's choosing)
10   * <p>
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  import java.util.concurrent.locks.ReentrantLock;
27  
28  import ch.qos.logback.classic.util.LogbackMDCAdapter;
29  import ch.qos.logback.core.status.ErrorStatus;
30  import ch.qos.logback.core.status.InfoStatus;
31  import org.slf4j.ILoggerFactory;
32  import org.slf4j.Marker;
33  
34  import ch.qos.logback.classic.spi.LoggerComparator;
35  import ch.qos.logback.classic.spi.LoggerContextListener;
36  import ch.qos.logback.classic.spi.LoggerContextVO;
37  import ch.qos.logback.classic.spi.TurboFilterList;
38  import ch.qos.logback.classic.turbo.TurboFilter;
39  import ch.qos.logback.classic.util.LoggerNameUtil;
40  import ch.qos.logback.core.ContextBase;
41  import ch.qos.logback.core.boolex.EventEvaluator;
42  import ch.qos.logback.core.spi.FilterReply;
43  import ch.qos.logback.core.spi.LifeCycle;
44  import ch.qos.logback.core.spi.SequenceNumberGenerator;
45  import ch.qos.logback.core.status.StatusListener;
46  import ch.qos.logback.core.status.StatusManager;
47  import ch.qos.logback.core.status.WarnStatus;
48  import org.slf4j.spi.MDCAdapter;
49  
50  /**
51   * LoggerContext glues many of the logback-classic components together. In
52   * principle, every logback-classic component instance is attached either
53   * directly or indirectly to a LoggerContext instance. Just as importantly
54   * LoggerContext implements the {@link ILoggerFactory} acting as the
55   * manufacturing source of {@link Logger} instances.
56   *
57   * @author Ceki Gulcu
58   */
59  public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
60  
61      /**
62       * Default setting of packaging data in stack traces
63       */
64      public static final boolean DEFAULT_PACKAGING_DATA = false;
65  
66      final Logger root;
67      private int size;
68      private int noAppenderWarning = 0;
69      final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
70  
71      private Map<String, Logger> loggerCache;
72  
73      private LoggerContextVO loggerContextRemoteView;
74      private final TurboFilterList turboFilterList = new TurboFilterList();
75      private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;
76      SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator
77  
78      MDCAdapter mdcAdapter;
79  
80      private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
81  
82      int resetCount = 0;
83      private List<String> frameworkPackages;
84  
85      public LoggerContext() {
86          super();
87          this.loggerCache = new ConcurrentHashMap<String, Logger>();
88  
89          this.loggerContextRemoteView = new LoggerContextVO(this);
90          this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
91          this.root.setLevel(Level.DEBUG);
92          loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
93          initEvaluatorMap();
94          size = 1;
95          this.frameworkPackages = new ArrayList<String>();
96          // In 1.5.7, the stop() method assumes that at some point the context has been started
97          // since earlier versions of logback did not mandate calling the start method
98          // we need to call in the constructor
99          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 }