View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  
11  package ch.qos.logback.classic;
12  
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.Collections;
16  import java.util.HashMap;
17  import java.util.Hashtable;
18  import java.util.List;
19  
20  import org.slf4j.ILoggerFactory;
21  import org.slf4j.Marker;
22  
23  import ch.qos.logback.classic.spi.LoggerComparator;
24  import ch.qos.logback.classic.spi.LoggerContextListener;
25  import ch.qos.logback.classic.spi.LoggerContextRemoteView;
26  import ch.qos.logback.classic.spi.TurboFilterList;
27  import ch.qos.logback.classic.turbo.TurboFilter;
28  import ch.qos.logback.core.ContextBase;
29  import ch.qos.logback.core.CoreConstants;
30  import ch.qos.logback.core.spi.FilterReply;
31  import ch.qos.logback.core.spi.LifeCycle;
32  import ch.qos.logback.core.status.StatusListener;
33  import ch.qos.logback.core.status.StatusManager;
34  import ch.qos.logback.core.status.WarnStatus;
35  
36  /**
37   * LoggerContext glues many of the logback-classic components together. In
38   * principle, every logback-classic component instance is attached either
39   * directly or indirecty to a LoggerContext instance. Just as importantly
40   * LoggerContext implements the {@link ILoggerFactory} acting as the
41   * manufacturing source of {@link Logger} instances.
42   * 
43   * @author Ceki Gulcu
44   */
45  public class LoggerContext extends ContextBase implements ILoggerFactory,
46      LifeCycle {
47  
48    public static final String ROOT_NAME = "root";
49  
50    final Logger root;
51    private int size;
52    private int noAppenderWarning = 0;
53    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();
54  
55    // We want loggerCache to be synchronized so Hashtable is a good choice. In
56    // practice, it performs a little faster than the map returned by
57    // Collections.synchronizedMap at the cost of a very slightly higher memory
58    // footprint.
59    private Hashtable<String, Logger> loggerCache;
60  
61    private LoggerContextRemoteView loggerContextRemoteView;
62    private final TurboFilterList turboFilterList = new TurboFilterList();
63  
64    boolean started = false;
65  
66    public LoggerContext() {
67      super();
68      this.loggerCache = new Hashtable<String, Logger>();
69      this.loggerContextRemoteView = new LoggerContextRemoteView(this);
70      this.root = new Logger(ROOT_NAME, null, this);
71      this.root.setLevel(Level.DEBUG);
72      loggerCache.put(ROOT_NAME, root);
73      putObject(CoreConstants.EVALUATOR_MAP, new HashMap());
74      size = 1;
75    }
76  
77    /**
78     * A new instance of LoggerContextRemoteView needs to be created each time the
79     * name or propertyMap (including keys or values) changes.
80     */
81    private void syncRemoteView() {
82      loggerContextRemoteView = new LoggerContextRemoteView(this);
83      for (Logger logger : loggerCache.values()) {
84        logger.buildRemoteView();
85      }
86    }
87  
88    @Override
89    public void putProperty(String key, String val) {
90      super.putProperty(key, val);
91      syncRemoteView();
92    }
93  
94    @Override
95    public void setName(String name) {
96      super.setName(name);
97      syncRemoteView();
98    }
99  
100   public final Logger getLogger(final Class clazz) {
101     return getLogger(clazz.getName());
102   }
103 
104   public final Logger getLogger(final String name) {
105 
106     if (name == null) {
107       throw new IllegalArgumentException("name argument cannot be null");
108     }
109 
110     // if we are asking for the root logger, then let us return it without
111     // wasting time
112     if (ROOT_NAME.equalsIgnoreCase(name)) {
113       return root;
114     }
115 
116     int i = 0;
117     Logger logger = root;
118 
119     // check if the desired logger exists, if it does, return it
120     // without further ado.
121     Logger childLogger = (Logger) loggerCache.get(name);
122     // if we have the child, then let us return it without wasting time
123     if (childLogger != null) {
124       return childLogger;
125     }
126 
127     // if the desired logger does not exist, them create all the loggers
128     // in between as well (if they don't already exist)
129     String childName;
130     while (true) {
131       int h = name.indexOf(ClassicGlobal.LOGGER_SEPARATOR, i);
132       if (h == -1) {
133         childName = name;
134       } else {
135         childName = name.substring(0, h);
136       }
137       // move i left of the last point
138       i = h + 1;
139       synchronized (logger) {
140         childLogger = logger.getChildByName(childName);
141         if (childLogger == null) {
142           childLogger = logger.createChildByName(childName);
143           loggerCache.put(childName, childLogger);
144           incSize();
145         }
146       }
147       logger = childLogger;
148       if (h == -1) {
149         return childLogger;
150       }
151     }
152   }
153 
154   private void incSize() {
155     size++;
156   }
157 
158   int size() {
159     return size;
160   }
161 
162   /**
163    * Check if the named logger exists in the hierarchy. If so return its
164    * reference, otherwise returns <code>null</code>.
165    * 
166    * @param name
167    *                the name of the logger to search for.
168    */
169   public Logger exists(String name) {
170     return (Logger) loggerCache.get(name);
171   }
172 
173   final void noAppenderDefinedWarning(final Logger logger) {
174     if (noAppenderWarning++ == 0) {
175       getStatusManager().add(
176           new WarnStatus("No appenders present in context [" + getName()
177               + "] for logger [" + logger.getName() + "].", logger));
178     }
179   }
180 
181   public List<Logger> getLoggerList() {
182     Collection<Logger> collection = loggerCache.values();
183     List<Logger> loggerList = new ArrayList<Logger>(collection);
184     Collections.sort(loggerList, new LoggerComparator());
185     return loggerList;
186   }
187 
188   public LoggerContextRemoteView getLoggerContextRemoteView() {
189     return loggerContextRemoteView;
190   }
191 
192   /**
193    * This method closes all appenders,
194    */
195   public void reset() {
196     root.recursiveReset();
197     resetTurboFilterList();
198     fireOnReset();
199     resetListenersExceptResetResistant();
200     resetStatusListeners();
201   }
202 
203   private void resetStatusListeners() {
204     StatusManager sm = getStatusManager();
205     for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
206       sm.remove(sl);
207     }
208   }
209 
210   public TurboFilterList getTurboFilterList() {
211     return turboFilterList;
212   }
213 
214   public void addTurboFilter(TurboFilter newFilter) {
215     turboFilterList.add(newFilter);
216   }
217 
218   /**
219    * First stop all registered turbo filters and then clear the registration
220    * list.
221    */
222   public void resetTurboFilterList() {
223     for (TurboFilter tf : turboFilterList) {
224       tf.stop();
225     }
226     turboFilterList.clear();
227   }
228 
229   final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker,
230       final Logger logger, final Level level, final String format,
231       final Object[] params, final Throwable t) {
232     if (turboFilterList.size() == 0) {
233       return FilterReply.NEUTRAL;
234     }
235     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
236         format, params, t);
237   }
238 
239   final FilterReply getTurboFilterChainDecision_1(final Marker marker,
240       final Logger logger, final Level level, final String format,
241       final Object param, final Throwable t) {
242     if (turboFilterList.size() == 0) {
243       return FilterReply.NEUTRAL;
244     }
245     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
246         format, new Object[] { param }, t);
247   }
248 
249   final FilterReply getTurboFilterChainDecision_2(final Marker marker,
250       final Logger logger, final Level level, final String format,
251       final Object param1, final Object param2, final Throwable t) {
252     if (turboFilterList.size() == 0) {
253       return FilterReply.NEUTRAL;
254     }
255     return turboFilterList.getTurboFilterChainDecision(marker, logger, level,
256         format, new Object[] { param1, param2 }, t);
257   }
258 
259   // === start listeners ==============================================
260   public void addListener(LoggerContextListener listener) {
261     loggerContextListenerList.add(listener);
262   }
263 
264   public void removeListener(LoggerContextListener listener) {
265     loggerContextListenerList.remove(listener);
266   }
267 
268   private void resetListenersExceptResetResistant() {
269     List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
270 
271     for (LoggerContextListener lcl : loggerContextListenerList) {
272       if (lcl.isResetResistant()) {
273         toRetain.add(lcl);
274       }
275     }
276     loggerContextListenerList.retainAll(toRetain);
277   }
278 
279   private void resetAllListeners() {
280     loggerContextListenerList.clear();
281   }
282 
283   public List<LoggerContextListener> getCopyOfListenerList() {
284     return new ArrayList<LoggerContextListener>(loggerContextListenerList);
285   }
286 
287   private void fireOnReset() {
288     for (LoggerContextListener listener : loggerContextListenerList) {
289       listener.onReset(this);
290     }
291   }
292 
293   private void fireOnStart() {
294     for (LoggerContextListener listener : loggerContextListenerList) {
295       listener.onStart(this);
296     }
297   }
298 
299   private void fireOnStop() {
300     for (LoggerContextListener listener : loggerContextListenerList) {
301       listener.onStop(this);
302     }
303   }
304 
305   // === end listeners ==============================================
306 
307   public boolean isStarted() {
308     return started;
309   }
310 
311   public void start() {
312     started = true;
313     fireOnStart();
314   }
315 
316   public void stop() {
317     reset();
318     fireOnStop();
319     resetAllListeners();
320     started = false;
321   }
322 
323   @Override
324   public String toString() {
325     return this.getClass().getName() + "[" + getName() + "]";
326   }
327 }