View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, 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 java.util.ArrayList;
17  import java.util.Collection;
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.Hashtable;
21  import java.util.List;
22  
23  import org.slf4j.ILoggerFactory;
24  import org.slf4j.Marker;
25  
26  import ch.qos.logback.classic.spi.LoggerComparator;
27  import ch.qos.logback.classic.spi.LoggerContextListener;
28  import ch.qos.logback.classic.spi.LoggerContextVO;
29  import ch.qos.logback.classic.spi.TurboFilterList;
30  import ch.qos.logback.classic.turbo.TurboFilter;
31  import ch.qos.logback.core.ContextBase;
32  import ch.qos.logback.core.CoreConstants;
33  import ch.qos.logback.core.spi.FilterReply;
34  import ch.qos.logback.core.spi.LifeCycle;
35  import ch.qos.logback.core.status.StatusListener;
36  import ch.qos.logback.core.status.StatusManager;
37  import ch.qos.logback.core.status.WarnStatus;
38  
39  /**
40   * LoggerContext glues many of the logback-classic components together. In
41   * principle, every logback-classic component instance is attached either
42   * directly or indirectly to a LoggerContext instance. Just as importantly
43   * LoggerContext implements the {@link ILoggerFactory} acting as the
44   * manufacturing source of {@link Logger} instances.
45   * 
46   * @author Ceki Gulcu
47   */
48  public class LoggerContext extends ContextBase implements ILoggerFactory,
49      LifeCycle {
50  
51    final Logger root;
52    private int size;
53    private int noAppenderWarning = 0;
54    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
55  
56    // We want loggerCache to be synchronized so Hashtable is a good choice. In
57    // practice, it performs a little faster than the map returned by
58    // Collections.synchronizedMap at the cost of a very slightly higher memory
59    // footprint.
60    private Hashtable<String, Logger> loggerCache;
61  
62    private LoggerContextVO loggerContextRemoteView;
63    private final TurboFilterList turboFilterList = new TurboFilterList();
64    private boolean packagingDataEnabled = true;
65  
66    private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
67  
68    boolean started = false;
69  
70    int resetCount = 0;
71  
72    public LoggerContext() {
73      super();
74      this.loggerCache = new Hashtable<String, Logger>();
75      this.loggerContextRemoteView = new LoggerContextVO(this);
76      this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
77      this.root.setLevel(Level.DEBUG);
78      loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
79      initEvaluatorMap();
80      size = 1;
81    }
82  
83    void initEvaluatorMap() {
84      putObject(CoreConstants.EVALUATOR_MAP, new HashMap());
85    }
86    
87    /**
88     * A new instance of LoggerContextRemoteView needs to be created each time the
89     * name or propertyMap (including keys or values) changes.
90     */
91    private void syncRemoteView() {
92      loggerContextRemoteView = new LoggerContextVO(this);
93      for (Logger logger : loggerCache.values()) {
94        logger.buildRemoteView();
95      }
96    }
97  
98    @Override
99    public void putProperty(String key, String val) {
100     super.putProperty(key, val);
101     syncRemoteView();
102   }
103 
104   @Override
105   public void setName(String name) {
106     super.setName(name);
107     syncRemoteView();
108   }
109 
110   public final Logger getLogger(final Class clazz) {
111     return getLogger(clazz.getName());
112   }
113 
114   public final Logger getLogger(final String name) {
115 
116     if (name == null) {
117       throw new IllegalArgumentException("name argument cannot be null");
118     }
119 
120     // if we are asking for the root logger, then let us return it without
121     // wasting time
122     if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
123       return root;
124     }
125 
126     int i = 0;
127     Logger logger = root;
128 
129     // check if the desired logger exists, if it does, return it
130     // without further ado.
131     Logger childLogger = (Logger) loggerCache.get(name);
132     // if we have the child, then let us return it without wasting time
133     if (childLogger != null) {
134       return childLogger;
135     }
136 
137     // if the desired logger does not exist, them create all the loggers
138     // in between as well (if they don't already exist)
139     String childName;
140     while (true) {
141       int h = Logger.getSeparatorIndexOf(name, i);
142       if (h == -1) {
143         childName = name;
144       } else {
145         childName = name.substring(0, h);
146       }
147       // move i left of the last point
148       i = h + 1;
149       synchronized (logger) {
150         childLogger = logger.getChildByName(childName);
151         if (childLogger == null) {
152           childLogger = logger.createChildByName(childName);
153           loggerCache.put(childName, childLogger);
154           incSize();
155         }
156       }
157       logger = childLogger;
158       if (h == -1) {
159         return childLogger;
160       }
161     }
162   }
163 
164   private void incSize() {
165     size++;
166   }
167 
168   int size() {
169     return size;
170   }
171 
172   /**
173    * Check if the named logger exists in the hierarchy. If so return its
174    * reference, otherwise returns <code>null</code>.
175    * 
176    * @param name
177    *          the name of the logger to search for.
178    */
179   public Logger exists(String name) {
180     return (Logger) loggerCache.get(name);
181   }
182 
183   final void noAppenderDefinedWarning(final Logger logger) {
184     if (noAppenderWarning++ == 0) {
185       getStatusManager().add(
186           new WarnStatus("No appenders present in context [" + getName()
187               + "] for logger [" + logger.getName() + "].", logger));
188     }
189   }
190 
191   public List<Logger> getLoggerList() {
192     Collection<Logger> collection = loggerCache.values();
193     List<Logger> loggerList = new ArrayList<Logger>(collection);
194     Collections.sort(loggerList, new LoggerComparator());
195     return loggerList;
196   }
197 
198   public LoggerContextVO getLoggerContextRemoteView() {
199     return loggerContextRemoteView;
200   }
201 
202   public void setPackagingDataEnabled(boolean packagingDataEnabled) {
203     this.packagingDataEnabled = packagingDataEnabled;
204   }
205 
206   public boolean isPackagingDataEnabled() {
207     return packagingDataEnabled;
208   }
209 
210   /**
211    * This method clears all internal properties, except internal status messages,
212    * closes all appenders, removes any turboFilters, fires an OnReset event,
213    * removes all status listeners, removes all context listeners
214    * (except those which are reset resistant).
215    * <p>
216    * As mentioned above, internal status messages survive resets.
217    * */
218   @Override
219   public void reset() {
220     resetCount++;
221     super.reset();
222     initEvaluatorMap();
223     root.recursiveReset();
224     resetTurboFilterList();
225     fireOnReset();
226     resetListenersExceptResetResistant();
227     resetStatusListeners();
228   }
229 
230   private void resetStatusListeners() {
231     StatusManager sm = getStatusManager();
232     for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
233       sm.remove(sl);
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 stop all registered turbo filters and then clear the registration
247    * 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,
257       final Logger logger, final Level level, final String format,
258       final Object[] params, final Throwable t) {
259     if (turboFilterList.size() == 0) {
260       return FilterReply.NEUTRAL;
261     }
262     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
263         format, params, t);
264   }
265 
266   final FilterReply getTurboFilterChainDecision_1(final Marker marker,
267       final Logger logger, final Level level, final String format,
268       final Object param, final Throwable t) {
269     if (turboFilterList.size() == 0) {
270       return FilterReply.NEUTRAL;
271     }
272     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
273         format, new Object[] { param }, t);
274   }
275 
276   final FilterReply getTurboFilterChainDecision_2(final Marker marker,
277       final Logger logger, final Level level, final String format,
278       final Object param1, final Object param2, final Throwable t) {
279     if (turboFilterList.size() == 0) {
280       return FilterReply.NEUTRAL;
281     }
282     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
283         format, new Object[] { param1, param2 }, t);
284   }
285 
286   // === start listeners ==============================================
287   public void addListener(LoggerContextListener listener) {
288     loggerContextListenerList.add(listener);
289   }
290 
291   public void removeListener(LoggerContextListener listener) {
292     loggerContextListenerList.remove(listener);
293   }
294 
295   private void resetListenersExceptResetResistant() {
296     List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
297 
298     for (LoggerContextListener lcl : loggerContextListenerList) {
299       if (lcl.isResetResistant()) {
300         toRetain.add(lcl);
301       }
302     }
303     loggerContextListenerList.retainAll(toRetain);
304   }
305 
306   private void resetAllListeners() {
307     loggerContextListenerList.clear();
308   }
309 
310   public List<LoggerContextListener> getCopyOfListenerList() {
311     return new ArrayList<LoggerContextListener>(loggerContextListenerList);
312   }
313 
314   void fireOnLevelChange(Logger logger, Level level) {
315     for (LoggerContextListener listener : loggerContextListenerList) {
316       listener.onLevelChange(logger, level);
317     }
318   }
319 
320   private void fireOnReset() {
321     for (LoggerContextListener listener : loggerContextListenerList) {
322       listener.onReset(this);
323     }
324   }
325 
326   private void fireOnStart() {
327     for (LoggerContextListener listener : loggerContextListenerList) {
328       listener.onStart(this);
329     }
330   }
331 
332   private void fireOnStop() {
333     for (LoggerContextListener listener : loggerContextListenerList) {
334       listener.onStop(this);
335     }
336   }
337 
338   // === end listeners ==============================================
339 
340   public boolean isStarted() {
341     return started;
342   }
343 
344   public void start() {
345     started = true;
346     fireOnStart();
347   }
348 
349   public void stop() {
350     reset();
351     fireOnStop();
352     resetAllListeners();
353     started = false;
354   }
355 
356   @Override
357   public String toString() {
358     return this.getClass().getName() + "[" + getName() + "]";
359   }
360 
361   public int getMaxCallerDataDepth() {
362     return maxCallerDataDepth;
363   }
364 
365   public void setMaxCallerDataDepth(int maxCallerDataDepth) {
366     this.maxCallerDataDepth = maxCallerDataDepth;
367   }
368 }