View Javadoc
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 ch.qos.logback.classic.util.LogbackMDCAdapter;
28  import ch.qos.logback.core.status.ErrorStatus;
29  import ch.qos.logback.core.status.InfoStatus;
30  import org.slf4j.ILoggerFactory;
31  import org.slf4j.Marker;
32  
33  import ch.qos.logback.classic.spi.LoggerComparator;
34  import ch.qos.logback.classic.spi.LoggerContextListener;
35  import ch.qos.logback.classic.spi.LoggerContextVO;
36  import ch.qos.logback.classic.spi.TurboFilterList;
37  import ch.qos.logback.classic.turbo.TurboFilter;
38  import ch.qos.logback.classic.util.LoggerNameUtil;
39  import ch.qos.logback.core.ContextBase;
40  import ch.qos.logback.core.boolex.EventEvaluator;
41  import ch.qos.logback.core.spi.FilterReply;
42  import ch.qos.logback.core.spi.LifeCycle;
43  import ch.qos.logback.core.spi.SequenceNumberGenerator;
44  import ch.qos.logback.core.status.StatusListener;
45  import ch.qos.logback.core.status.StatusManager;
46  import ch.qos.logback.core.status.WarnStatus;
47  import org.slf4j.spi.MDCAdapter;
48  
49  /**
50   * LoggerContext glues many of the logback-classic components together. In
51   * principle, every logback-classic component instance is attached either
52   * directly or indirectly to a LoggerContext instance. Just as importantly
53   * LoggerContext implements the {@link ILoggerFactory} acting as the
54   * manufacturing source of {@link Logger} instances.
55   *
56   * @author Ceki Gulcu
57   */
58  public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
59  
60      /** Default setting of packaging data in stack traces */
61      public static final boolean DEFAULT_PACKAGING_DATA = false;
62  
63      final Logger root;
64      private int size;
65      private int noAppenderWarning = 0;
66      final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
67  
68      private Map<String, Logger> loggerCache;
69  
70      private LoggerContextVO loggerContextRemoteView;
71      private final TurboFilterList turboFilterList = new TurboFilterList();
72      private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;
73      SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator
74  
75      MDCAdapter mdcAdapter;
76  
77  
78      private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
79  
80      int resetCount = 0;
81      private List<String> frameworkPackages;
82  
83      public LoggerContext() {
84          super();
85          this.loggerCache = new ConcurrentHashMap<String, Logger>();
86  
87          this.loggerContextRemoteView = new LoggerContextVO(this);
88          this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
89          this.root.setLevel(Level.DEBUG);
90          loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
91          initEvaluatorMap();
92          size = 1;
93          this.frameworkPackages = new ArrayList<String>();
94      }
95  
96      void initEvaluatorMap() {
97          putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>());
98      }
99  
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 }