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 ch.qos.logback.classic.spi.LoggerComparator;
17  import ch.qos.logback.classic.spi.LoggerContextListener;
18  import ch.qos.logback.classic.spi.LoggerContextVO;
19  import ch.qos.logback.classic.spi.TurboFilterList;
20  import ch.qos.logback.classic.turbo.TurboFilter;
21  import ch.qos.logback.classic.util.LoggerNameUtil;
22  import ch.qos.logback.core.ContextBase;
23  import ch.qos.logback.core.boolex.EventEvaluator;
24  import ch.qos.logback.core.spi.FilterReply;
25  import ch.qos.logback.core.spi.LifeCycle;
26  import ch.qos.logback.core.spi.SequenceNumberGenerator;
27  import ch.qos.logback.core.status.StatusListener;
28  import ch.qos.logback.core.status.StatusManager;
29  import ch.qos.logback.core.status.WarnStatus;
30  import org.slf4j.ILoggerFactory;
31  import org.slf4j.Marker;
32  import org.slf4j.spi.MDCAdapter;
33  
34  import java.util.*;
35  import java.util.concurrent.ConcurrentHashMap;
36  import java.util.concurrent.ScheduledFuture;
37  
38  import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP;
39  
40  /**
41   * LoggerContext glues many of the logback-classic components together. In
42   * principle, every logback-classic component instance is attached either
43   * directly or indirectly to a LoggerContext instance. Just as importantly
44   * LoggerContext implements the {@link ILoggerFactory} acting as the
45   * manufacturing source of {@link Logger} instances.
46   *
47   * @author Ceki Gulcu
48   */
49  public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
50  
51      /**
52       * Default setting of packaging data in stack traces
53       */
54      public static final boolean DEFAULT_PACKAGING_DATA = false;
55  
56      final Logger root;
57      private int size;
58      private int noAppenderWarning = 0;
59      final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
60  
61      private Map<String, Logger> loggerCache;
62  
63      private LoggerContextVO loggerContextRemoteView;
64      private final TurboFilterList turboFilterList = new TurboFilterList();
65      private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;
66      SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator
67  
68      MDCAdapter mdcAdapter;
69  
70      private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
71  
72      int resetCount = 0;
73      private List<String> frameworkPackages;
74  
75      public LoggerContext() {
76          super();
77          this.loggerCache = new ConcurrentHashMap<String, Logger>();
78  
79          this.loggerContextRemoteView = new LoggerContextVO(this);
80          this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
81          this.root.setLevel(Level.DEBUG);
82          loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
83          initEvaluatorMap();
84          size = 1;
85          this.frameworkPackages = new ArrayList<String>();
86          // In 1.5.7, the stop() method assumes that at some point the context has been started
87          // since earlier versions of logback did not mandate calling the start method
88          // we need to call in the constructor
89          this.start();
90      }
91  
92      void initEvaluatorMap() {
93          putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>());
94      }
95  
96      /**
97       * A new instance of LoggerContextRemoteView needs to be created each time the
98       * name or propertyMap (including keys or values) changes.
99       */
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 }