PageRenderTime 81ms CodeModel.GetById 64ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/util/Log.java

#
Java | 496 lines | 315 code | 44 blank | 137 comment | 43 complexity | 85a081bfa92e5a48117e2706fbe78ca1 MD5 | raw file
  1/*
  2 * Log.java - A class for logging events
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2003 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.util;
 24
 25import java.io.*;
 26import java.util.*;
 27import javax.swing.*;
 28import javax.swing.event.*;
 29
 30/**
 31 * This class provides methods for logging events. In terms of functionality,
 32 * it is somewhere in between <code>System.out.println()</code> and
 33 * full-blown logging packages such as log4j.<p>
 34 *
 35 * All events are logged to an in-memory buffer and optionally a stream,
 36 * and those with a high urgency (warnings and errors) are also printed
 37 * to standard output.<p>
 38 *
 39 * Logging of exception tracebacks is supported.<p>
 40 *
 41 * This class can also optionally redirect standard output and error to the log.
 42 *
 43 * @author Slava Pestov
 44 * @version $Id: Log.java 5004 2004-03-28 00:07:27Z spestov $
 45 */
 46public class Log
 47{
 48	//{{{ Constants
 49	/**
 50	 * The maximum number of log messages that will be kept in memory.
 51	 * @since jEdit 2.6pre5
 52	 */
 53	public static final int MAXLINES = 500;
 54
 55	/**
 56	 * Debugging message urgency. Should be used for messages only
 57	 * useful when debugging a problem.
 58	 * @since jEdit 2.2pre2
 59	 */
 60	public static final int DEBUG = 1;
 61
 62	/**
 63	 * Message urgency. Should be used for messages which give more
 64	 * detail than notices.
 65	 * @since jEdit 2.2pre2
 66	 */
 67	public static final int MESSAGE = 3;
 68
 69	/**
 70	 * Notice urgency. Should be used for messages that directly
 71	 * affect the user.
 72	 * @since jEdit 2.2pre2
 73	 */
 74	public static final int NOTICE = 5;
 75
 76	/**
 77	 * Warning urgency. Should be used for messages that warrant
 78	 * attention.
 79	 * @since jEdit 2.2pre2
 80	 */
 81	public static final int WARNING = 7;
 82
 83	/**
 84	 * Error urgency. Should be used for messages that signal a
 85	 * failure.
 86	 * @since jEdit 2.2pre2
 87	 */
 88	public static final int ERROR = 9;
 89	//}}}
 90
 91	//{{{ init() method
 92	/**
 93	 * Initializes the log.
 94	 * @param stdio If true, standard output and error will be
 95	 * sent to the log
 96	 * @param level Messages with this log level or higher will
 97	 * be printed to the system console
 98	 * @since jEdit 3.2pre4
 99	 */
100	public static void init(boolean stdio, int level)
101	{
102		if(stdio)
103		{
104			if(System.out == realOut && System.err == realErr)
105			{
106				System.setOut(createPrintStream(NOTICE,null));
107				System.setErr(createPrintStream(ERROR,null));
108			}
109		}
110
111		Log.level = level;
112
113		// Log some stuff
114		log(MESSAGE,Log.class,"When reporting bugs, please"
115			+ " include the following information:");
116		String[] props = {
117			"java.version", "java.vm.version", "java.runtime.version",
118			"java.vendor", "java.compiler", "os.name", "os.version",
119			"os.arch", "user.home", "java.home",
120			"java.class.path",
121			};
122		for(int i = 0; i < props.length; i++)
123		{
124			log(MESSAGE,Log.class,
125				props[i] + "=" + System.getProperty(props[i]));
126		}
127	} //}}}
128
129	//{{{ setLogWriter() method
130	/**
131	 * Writes all currently logged messages to this stream if there was no
132	 * stream set previously, and sets the stream to write future log
133	 * messages to.
134	 * @param stream The writer
135	 * @since jEdit 3.2pre4
136	 */
137	public static void setLogWriter(Writer stream)
138	{
139		if(Log.stream == null && stream != null)
140		{
141			try
142			{
143				if(wrap)
144				{
145					for(int i = logLineCount; i < log.length; i++)
146					{
147						stream.write(log[i]);
148						stream.write(lineSep);
149					}
150				}
151				for(int i = 0; i < logLineCount; i++)
152				{
153					stream.write(log[i]);
154					stream.write(lineSep);
155				}
156
157				stream.flush();
158			}
159			catch(Exception e)
160			{
161				// do nothing, who cares
162			}
163		}
164
165		Log.stream = stream;
166	} //}}}
167
168	//{{{ flushStream() method
169	/**
170	 * Flushes the log stream.
171	 * @since jEdit 2.6pre5
172	 */
173	public static void flushStream()
174	{
175		if(stream != null)
176		{
177			try
178			{
179				stream.flush();
180			}
181			catch(IOException io)
182			{
183				io.printStackTrace(realErr);
184			}
185		}
186	} //}}}
187
188	//{{{ closeStream() method
189	/**
190	 * Closes the log stream. Should be done before your program exits.
191	 * @since jEdit 2.6pre5
192	 */
193	public static void closeStream()
194	{
195		if(stream != null)
196		{
197			try
198			{
199				stream.close();
200				stream = null;
201			}
202			catch(IOException io)
203			{
204				io.printStackTrace(realErr);
205			}
206		}
207	} //}}}
208
209	//{{{ getLogListModel() method
210	/**
211	 * Returns the list model for viewing the log contents.
212	 * @since jEdit 4.2pre1
213	 */
214	public static ListModel getLogListModel()
215	{
216		return listModel;
217	} //}}}
218
219	//{{{ log() method
220	/**
221	 * Logs a message. This method is thread-safe.<p>
222	 *
223	 * The following code sends a typical debugging message to the activity
224	 * log:
225	 * <pre>Log.log(Log.DEBUG,this,"counter = " + counter);</pre>
226	 * The corresponding activity log entry might read as follows:
227	 * <pre>[debug] JavaParser: counter = 15</pre>
228	 *
229	 * @param urgency The urgency; can be one of
230	 * <code>Log.DEBUG</code>, <code>Log.MESSAGE</code>,
231	 * <code>Log.NOTICE</code>, <code>Log.WARNING</code>, or
232	 * <code>Log.ERROR</code>.
233	 * @param source The source of the message, either an object or a
234	 * class instance. When writing log messages from macros, set
235	 * this parameter to <code>BeanShell.class</code> to make macro
236	 * errors easier to spot in the activity log.
237	 * @param message The message. This can either be a string or
238	 * an exception
239	 *
240	 * @since jEdit 2.2pre2
241	 */
242	public static void log(int urgency, Object source, Object message)
243	{
244		String _source;
245		if(source == null)
246		{
247			_source = Thread.currentThread().getName();
248			if(_source == null)
249			{
250				_source = Thread.currentThread().getClass().getName();
251			}
252		}
253		else if(source instanceof Class)
254			_source = ((Class)source).getName();
255		else
256			_source = source.getClass().getName();
257		int index = _source.lastIndexOf('.');
258		if(index != -1)
259			_source = _source.substring(index+1);
260
261		if(message instanceof Throwable)
262		{
263			_logException(urgency,source,(Throwable)message);
264		}
265		else
266		{
267			String _message = String.valueOf(message);
268			// If multiple threads log stuff, we don't want
269			// the output to get mixed up
270			synchronized(LOCK)
271			{
272				StringTokenizer st = new StringTokenizer(
273					_message,"\r\n");
274				int lineCount = 0;
275				boolean oldWrap = wrap;
276				while(st.hasMoreTokens())
277				{
278					lineCount++;
279					_log(urgency,_source,st.nextToken()
280						.replace('\t',' '));
281				}
282				listModel.update(lineCount,oldWrap);
283			}
284		}
285	} //}}}
286
287	//{{{ Private members
288
289	//{{{ Instance variables
290	private static Object LOCK = new Object();
291	private static String[] log;
292	private static int logLineCount;
293	private static boolean wrap;
294	private static int level = WARNING;
295	private static Writer stream;
296	private static String lineSep;
297	private static PrintStream realOut;
298	private static PrintStream realErr;
299	private static LogListModel listModel;
300	//}}}
301
302	//{{{ Class initializer
303	static
304	{
305		level = WARNING;
306
307		realOut = System.out;
308		realErr = System.err;
309
310		log = new String[MAXLINES];
311		lineSep = System.getProperty("line.separator");
312		listModel = new LogListModel();
313	} //}}}
314
315	//{{{ createPrintStream() method
316	private static PrintStream createPrintStream(final int urgency,
317		final Object source)
318	{
319		return new PrintStream(new OutputStream() {
320			public void write(int b)
321			{
322				byte[] barray = { (byte)b };
323				write(barray,0,1);
324			}
325
326			public void write(byte[] b, int off, int len)
327			{
328				String str = new String(b,off,len);
329				log(urgency,source,str);
330			}
331		});
332	} //}}}
333
334	//{{{ _logException() method
335	private static void _logException(final int urgency,
336		final Object source,
337		final Throwable message)
338	{
339		PrintStream out = createPrintStream(urgency,source);
340
341		synchronized(LOCK)
342		{
343			message.printStackTrace(out);
344		}
345	} //}}}
346
347	//{{{ _log() method
348	private static void _log(int urgency, String source, String message)
349	{
350		String fullMessage = "[" + urgencyToString(urgency) + "] " + source
351			+ ": " + message;
352
353		try
354		{
355			log[logLineCount] = fullMessage;
356			if(++logLineCount >= log.length)
357			{
358				wrap = true;
359				logLineCount = 0;
360			}
361
362			if(stream != null)
363			{
364				stream.write(fullMessage);
365				stream.write(lineSep);
366			}
367		}
368		catch(Exception e)
369		{
370			e.printStackTrace(realErr);
371		}
372
373		if(urgency >= level)
374		{
375			if(urgency == ERROR)
376				realErr.println(fullMessage);
377			else
378				realOut.println(fullMessage);
379		}
380	} //}}}
381
382	//{{{ urgencyToString() method
383	private static String urgencyToString(int urgency)
384	{
385		switch(urgency)
386		{
387		case DEBUG:
388			return "debug";
389		case MESSAGE:
390			return "message";
391		case NOTICE:
392			return "notice";
393		case WARNING:
394			return "warning";
395		case ERROR:
396			return "error";
397		}
398
399		throw new IllegalArgumentException("Invalid urgency: " + urgency);
400	} //}}}
401
402	//}}}
403
404	//{{{ LogListModel class
405	static class LogListModel implements ListModel
406	{
407		Vector listeners = new Vector();
408
409		private void fireIntervalAdded(int index1, int index2)
410		{
411			for(int i = 0; i < listeners.size(); i++)
412			{
413				ListDataListener listener = (ListDataListener)
414					listeners.elementAt(i);
415				listener.intervalAdded(new ListDataEvent(this,
416					ListDataEvent.INTERVAL_ADDED,
417					index1,index2));
418			}
419		}
420
421		private void fireIntervalRemoved(int index1, int index2)
422		{
423			for(int i = 0; i < listeners.size(); i++)
424			{
425				ListDataListener listener = (ListDataListener)
426					listeners.elementAt(i);
427				listener.intervalRemoved(new ListDataEvent(this,
428					ListDataEvent.INTERVAL_REMOVED,
429					index1,index2));
430			}
431		}
432
433		public void addListDataListener(ListDataListener listener)
434		{
435			listeners.addElement(listener);
436		}
437
438		public void removeListDataListener(ListDataListener listener)
439		{
440			listeners.removeElement(listener);
441		}
442
443		public Object getElementAt(int index)
444		{
445			if(wrap)
446			{
447				if(index < MAXLINES - logLineCount)
448					return log[index + logLineCount];
449				else
450					return log[index - MAXLINES + logLineCount];
451			}
452			else
453				return log[index];
454		}
455
456		public int getSize()
457		{
458			if(wrap)
459				return MAXLINES;
460			else
461				return logLineCount;
462		}
463
464		void update(final int lineCount, final boolean oldWrap)
465		{
466			if(lineCount == 0 || listeners.size() == 0)
467				return;
468
469			SwingUtilities.invokeLater(new Runnable()
470			{
471				public void run()
472				{
473					if(wrap)
474					{
475						if(oldWrap)
476							fireIntervalRemoved(0,lineCount - 1);
477						else
478						{
479							fireIntervalRemoved(0,
480								logLineCount);
481						}
482						fireIntervalAdded(
483							MAXLINES - lineCount + 1,
484							MAXLINES);
485					}
486					else
487					{
488						fireIntervalAdded(
489							logLineCount - lineCount + 1,
490							logLineCount);
491					}
492				}
493			});
494		}
495	} //}}}
496}