View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, 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 indirecty 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, closes all appenders, removes
212    * any turboFilters, fires an OnReset event, removes all status listeners,
213    * removes all context listeners (except those which are reset resistant).
214    */
215   @Override
216   public void reset() {
217     resetCount++;
218     super.reset();
219     initEvaluatorMap();
220     root.recursiveReset();
221     resetTurboFilterList();
222     fireOnReset();
223     resetListenersExceptResetResistant();
224     resetStatusListeners();
225   }
226 
227   private void resetStatusListeners() {
228     StatusManager sm = getStatusManager();
229     for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
230       sm.remove(sl);
231     }
232   }
233 
234   public TurboFilterList getTurboFilterList() {
235     return turboFilterList;
236   }
237 
238   public void addTurboFilter(TurboFilter newFilter) {
239     turboFilterList.add(newFilter);
240   }
241 
242   /**
243    * First stop all registered turbo filters and then clear the registration
244    * list.
245    */
246   public void resetTurboFilterList() {
247     for (TurboFilter tf : turboFilterList) {
248       tf.stop();
249     }
250     turboFilterList.clear();
251   }
252 
253   final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker,
254       final Logger logger, final Level level, final String format,
255       final Object[] params, final Throwable t) {
256     if (turboFilterList.size() == 0) {
257       return FilterReply.NEUTRAL;
258     }
259     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
260         format, params, t);
261   }
262 
263   final FilterReply getTurboFilterChainDecision_1(final Marker marker,
264       final Logger logger, final Level level, final String format,
265       final Object param, final Throwable t) {
266     if (turboFilterList.size() == 0) {
267       return FilterReply.NEUTRAL;
268     }
269     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
270         format, new Object[] { param }, t);
271   }
272 
273   final FilterReply getTurboFilterChainDecision_2(final Marker marker,
274       final Logger logger, final Level level, final String format,
275       final Object param1, final Object param2, final Throwable t) {
276     if (turboFilterList.size() == 0) {
277       return FilterReply.NEUTRAL;
278     }
279     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
280         format, new Object[] { param1, param2 }, t);
281   }
282 
283   // === start listeners ==============================================
284   public void addListener(LoggerContextListener listener) {
285     loggerContextListenerList.add(listener);
286   }
287 
288   public void removeListener(LoggerContextListener listener) {
289     loggerContextListenerList.remove(listener);
290   }
291 
292   private void resetListenersExceptResetResistant() {
293     List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
294 
295     for (LoggerContextListener lcl : loggerContextListenerList) {
296       if (lcl.isResetResistant()) {
297         toRetain.add(lcl);
298       }
299     }
300     loggerContextListenerList.retainAll(toRetain);
301   }
302 
303   private void resetAllListeners() {
304     loggerContextListenerList.clear();
305   }
306 
307   public List<LoggerContextListener> getCopyOfListenerList() {
308     return new ArrayList<LoggerContextListener>(loggerContextListenerList);
309   }
310 
311   private void fireOnReset() {
312     for (LoggerContextListener listener : loggerContextListenerList) {
313       listener.onReset(this);
314     }
315   }
316 
317   private void fireOnStart() {
318     for (LoggerContextListener listener : loggerContextListenerList) {
319       listener.onStart(this);
320     }
321   }
322 
323   private void fireOnStop() {
324     for (LoggerContextListener listener : loggerContextListenerList) {
325       listener.onStop(this);
326     }
327   }
328 
329   // === end listeners ==============================================
330 
331   public boolean isStarted() {
332     return started;
333   }
334 
335   public void start() {
336     started = true;
337     fireOnStart();
338   }
339 
340   public void stop() {
341     reset();
342     fireOnStop();
343     resetAllListeners();
344     started = false;
345   }
346 
347   @Override
348   public String toString() {
349     return this.getClass().getName() + "[" + getName() + "]";
350   }
351 
352   public int getMaxCallerDataDepth() {
353     return maxCallerDataDepth;
354   }
355 
356   public void setMaxCallerDataDepth(int maxCallerDataDepth) {
357     this.maxCallerDataDepth = maxCallerDataDepth;
358   }
359 }