View Javadoc

1   /*
2    * $HeadURL: $
3    * $Date: $
4    * $Revision: $
5    * $Author: $
6    * 
7    * Copyright (c) 2005 MindTree Consulting Ltd. 
8    * 
9    * This file is part of Insight.
10   * 
11   * Insight is free software: you can redistribute it and/or modify it under the 
12   * terms of the GNU General Public License as published by the Free Software 
13   * Foundation, either version 3 of the License, or (at your option) any later 
14   * version.
15   * 
16   * Insight is distributed in the hope that it will be useful, but 
17   * WITHOUT ANY WARRANTY; without even the implied warranty of 
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
19   * Public License for more details.
20   * 
21   * You should have received a copy of the GNU General Public License along with 
22   * Insight.  If not, see <http://www.gnu.org/licenses/>.
23   */
24  
25  package com.mindtree.techworks.insight.appender;
26  
27  import java.util.StringTokenizer;
28  import java.util.regex.Pattern;
29  
30  import org.apache.log4j.Appender;
31  import org.apache.log4j.Layout;
32  import org.apache.log4j.helpers.OnlyOnceErrorHandler;
33  import org.apache.log4j.spi.ErrorHandler;
34  import org.apache.log4j.spi.Filter;
35  import org.apache.log4j.spi.LoggerRepository;
36  import org.apache.log4j.spi.LoggingEvent;
37  
38  import com.mindtree.techworks.insight.Controller;
39  import com.mindtree.techworks.insight.model.LogEventModel;
40  import com.mindtree.techworks.insight.pagination.IPage;
41  import com.mindtree.techworks.insight.pagination.PageSet;
42  import com.mindtree.techworks.insight.receiver.RuntimeNamespaceContainer;
43  import com.mindtree.techworks.insight.spi.LogEvent;
44  import com.mindtree.techworks.insight.spi.LogLevel;
45  
46  /**
47  *
48  * The <code>ParsedEventAppender</code> class implements the Apache Appender interface.
49  * This implementation caches the received parsed LoggingEvent instance and passes it
50  * on to the Controller instance that this Appender was instantiated with.
51  * Many methods in this class donot have an implementation. May be added if need be. 
52  *
53  * @author  Regunath B
54  * @version 1.0, 04/10/25
55  * @see     org.apache.log4j.Appender
56  */
57  
58  public class ParsedEventAppender extends LogEventModel implements Appender {
59  	
60  	/**
61  	 * Used for object serialization
62  	 */
63  	private static final long serialVersionUID = 217646441758807591L;
64  
65  	/*
66  	 * Useful constant for the LogEvent sequence start number
67  	 */
68  	private static final long LOG_EVENT_SEQUENCE_START = 1L;
69  
70      /*
71       * Useful constants to identify the Exception string in stack traces
72       */
73      public static final String EXCEPTION_STRING = "EXCEPTION";
74      private static final int EXCEPTION_STRING_LENGTH = EXCEPTION_STRING.length();
75  	
76  	/*
77  	 * Useful regex for permissible chars in Exception class name
78  	 */
79      private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9.]");
80  	
81  	/*
82  	 * Default name for this Appender
83  	 */
84  	private static final String NAME = "Parsed Event Appender";
85  	
86  	/*
87  	 * Singleton instance of this Appender
88  	 */
89  	private static ParsedEventAppender instance;
90  
91  	/*
92  	 * The ErrorHandler instance that all Appender implementations need to support
93  	 */
94  	private ErrorHandler errorHandler;
95  
96  	/*
97  	 * Filter for data handled by this Appender
98  	 */
99  	private Filter filter;
100 
101 	/*
102 	 * Layout for this Appender
103 	 */
104 	private Layout layout;
105 
106 	/*
107 	 * Stores the name of this Appender
108 	 */
109 	private String name = NAME;
110 	
111 	/*
112 	 * Variable that holds the next sequence number for LogEvent instances
113 	 */
114 	private long nextLogEventSequenceNumber = LOG_EVENT_SEQUENCE_START;
115 
116 	/**
117 	 * LogEventModel Name
118 	 */
119 	private static final String LOG_EVENT_MODEL_NAME = "R";
120 	
121 	/**
122 	 * Private Constructor for this class to prevent multiple instances
123 	 * @param controller the Controller instance that gets notified when this Appender receives events
124 	 * @see #getInstance(Controller)
125 	 */
126 	private ParsedEventAppender(Controller controller) {
127 		super(controller);
128 		this.errorHandler = new OnlyOnceErrorHandler();
129 		this.controller.setRootLogEventModel(this);
130 		setLogEventModelName(LOG_EVENT_MODEL_NAME);
131 	}
132 	
133 	/**
134 	 * Accessor method to get the singleton instance of this class.
135 	 * @param controller the Controller that will be notified of parsed events. 
136 	 * @return the sungleton instance of this class
137 	 */
138 	public static final synchronized ParsedEventAppender getInstance(Controller controller) {
139 		if (instance == null) {
140 			instance = new ParsedEventAppender(controller);
141 		}
142 		return instance;
143 	}
144 
145 	/**
146 	 * Appender interface implementation.
147 	 * Contains no significant implementation.
148 	 * @param newFilter the Filter instance to add
149 	 * 
150 	 */
151 	public void addFilter(Filter newFilter) {
152 		this.filter = newFilter;
153 	}
154 
155 	/**
156 	 * Appender interface implementation.
157 	 * Contains no significant implementation.
158 	 * @return null or the Filter instance for this Appender
159 	 * 
160 	 */
161 	public Filter getFilter() {
162 		return this.filter;
163 	}
164 
165 
166 	/**
167 	 * Appender interface implementation.
168 	 * Contains no significant implementation.
169 	 * Sets the Filter reference of this class to null 
170 	 */
171 	public void clearFilters() {
172 		this.filter = null;
173 	}
174 
175 	/**
176 	 * Appender interface implementation.
177 	 * Contains no significant implementation.
178 	 */
179 	public void close() {
180 		// un-register listeners if any.
181 	}
182 
183 	/**
184 	 * Appender interface implementation. 
185 	 * @param loggingEvent the LoggingEvent instance that has been received
186 	 */
187 	public void doAppend(LoggingEvent loggingEvent) {
188 		appendEvent(loggingEvent instanceof LogEvent ? (LogEvent)loggingEvent : new LogEvent(loggingEvent));
189 	}
190 	
191 	/**
192 	 * Appends the specified LogEvent.
193 	 * @param event the LogEvent instance that has been received
194 	 */
195 	public synchronized void appendEvent(LogEvent event) {
196 		event.setSequenceNumber(this.nextLogEventSequenceNumber);
197 		this.nextLogEventSequenceNumber += 1;
198 		event.setExceptionName(getExceptionClassName(event));
199 		IPage page = pageSet.getLastPage();
200 		page.addLogEvent(event);
201 		super.fireLogEventAdded(event);
202 	}
203 
204 	/**
205 	 * Appender interface implementation.
206 	 * Contains no significant implementation. Returns the name of this Appender
207 	 * @return the name of this Appender
208 	 * 
209 	 */
210 	public String getName() {
211 		return this.name;
212 	}
213 
214 	/**
215 	 * Appender interface implementation.
216 	 * Contains no significant implementation. Sets the specified ErrorHandler
217 	 * for this Appender
218 	 * @param handler the ErrorHandler for this Appender
219 	 * 
220 	 */
221 	public void setErrorHandler(ErrorHandler handler) {
222 		this.errorHandler = handler;
223 	}
224 
225 	/**
226 	 * Appender interface implementation.
227 	 * Contains no significant implementation. Returns the ErrorHandler of this Appender
228 	 * @return the ErrorHandler for this Appender
229 	 * 
230 	 */
231 	public ErrorHandler getErrorHandler() {
232 		return this.errorHandler;
233 	}
234 
235 	/**
236 	 * Appender interface implementation.
237 	 * Contains no significant implementation. 
238 	 * @param layout the Layout for this Appender
239 	 * 
240 	 */
241 	public void setLayout(Layout layout) {
242 		this.layout = layout;
243 	}
244 
245 	/**
246 	 * Appender interface implementation.
247 	 * Contains no significant implementation. Returns the Layout of this Appender
248 	 * 
249 	 */
250 	public Layout getLayout() {
251 		return this.layout;
252 	}
253 
254 	/**
255 	 * Appender interface implementation.
256 	 * Contains no significant implementation. Sets the name of this Appender
257 	 * @param name the name of this Appender
258 	 * 
259 	 */
260 	public void setName(String name) {
261 		this.name = name;
262 	}
263 
264 	/**
265 	 * Appender interface implementation.
266 	 * Contains no significant implementation. Returns false
267 	 */
268 	public boolean requiresLayout() {
269 		return false;
270 	}
271 	
272 	/**
273 	 * Helper method that return the Exception name if the LogEvent
274 	 * is of ERROR or FATAL or WARN priority
275      * @param event the LogEvent that potentially contains an Exception class name
276      * @return empty string or the Exception class name, if found
277 	 */
278     private final String getExceptionClassName(LogEvent event) {
279     	String exceptionName = "";
280 		int levelInt = event.getLevel().toInt();
281 		if ((levelInt == LogLevel.ERROR_INT) || (levelInt == LogLevel.FATAL_INT) || (levelInt == LogLevel.WARN_INT)) {
282 			// try to get exception name from stack trace
283 			String[] throwableInfo = event.getThrowableStrRep();
284 			for (int i = 0; i < throwableInfo.length; i++) {
285 				if (throwableInfo[i].toUpperCase().indexOf(EXCEPTION_STRING) > -1) {
286 					exceptionName = getExceptionNameFromString(throwableInfo[i]);
287 					break;
288 				}
289 			}
290 			if (exceptionName.length() == 0) {
291 				// try to get Exception name from message string
292 				exceptionName =  getExceptionNameFromString(event.getMessage().toString());
293 			}
294 		}
295 		return exceptionName;
296     }
297     
298     /**
299      * Helper method that gets the Exception class name from the specified string
300      * @param line the input line that potentially contains the Exception class name
301      * @return empty string or the Exception class name, if found
302      */
303     private final String getExceptionNameFromString(String line) {
304     	String exceptionClassName = "";
305     	StringTokenizer tokenizer = new StringTokenizer(line);
306     	while(tokenizer.hasMoreTokens()) {
307     		String token = tokenizer.nextToken();
308     		int exceptionIndex = token.toUpperCase().lastIndexOf(EXCEPTION_STRING);
309     		if (exceptionIndex > -1) {
310     			int startIndex = exceptionIndex;
311     			int prevIndex = startIndex - 1;
312     			while(prevIndex >= 0) {
313     				char[] prevChar = new char[] {token.charAt(prevIndex)};
314     				if (CLASS_NAME_PATTERN.matcher(new String(prevChar)).matches()) { // proceed only for class/package name chars permitted by Java
315         				startIndex = prevIndex;
316         				prevIndex -= 1;
317     				} else {
318     					break;
319     				}
320     			}
321     			exceptionClassName = token.substring(startIndex,exceptionIndex + EXCEPTION_STRING_LENGTH);
322     			break;
323     		}
324     	}
325     	return exceptionClassName;
326     }
327     
328     /**
329      * Clears the loaded namespaces
330      *  
331      * @see com.mindtree.techworks.insight.model.LogEventModel#clear()
332      */
333     public void clear(){
334     	super.clear();
335     	// Initialize the page set because the root model always needs an active pageset
336     	this.initializePageSet();
337     	this.nextLogEventSequenceNumber = LOG_EVENT_SEQUENCE_START;
338     	RuntimeNamespaceContainer.clearLoadedNamespaces();
339     }
340     
341     /*
342      * Helper method to initialize the page set for this LogEventModel
343      */
344     private void initializePageSet() {
345 		if(null == pageSet){
346 			pageSet = new PageSet();
347 			setPageSet(pageSet);
348 		}
349     }
350 
351 	public void setLoggerRepository(LoggerRepository arg0) {
352 		// TODO Auto-generated method stub
353 		
354 	}
355 }