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