1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.trace;
39
40
41
42
43
44
45
46
47
48 import com.gargoylesoftware.base.util.DetailedNullPointerException;
49 import java.io.PrintWriter;
50 import java.io.StringWriter;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.StringTokenizer;
54
55 /***
56 * A class that provides a mechanism for logging diagnostic messages. This
57 * mechanism is significantly faster then calling System.out.println directly
58 * as the printing is done on a background thread which does not impact the
59 * performance of the application threads.
60 * <p>
61 * The methods for tracing are print(String), println(String) and
62 * printStackTrace(Throwable). The basic idea is that we want the trace
63 * methods to return to the working threads as quickly as possible so we
64 * put the thing to be traced on a queue. The queue is then processed by
65 * a second thread which performs all the formatting and actual output.<p>
66 *
67 * Trace can be configured using the {@link TraceController} class - see
68 * {@link #getController()}. It is possible to redirect System.out and
69 * System.err so that calling System.out.println("foo") will be the same
70 * as calling Trace.println("foo")
71 * <pre>
72 * Trace.getController().setOutRedirected(true);
73 * Trace.getController().setErrRedirected(true);
74 * </pre>
75 *
76 * Calls to any of the print methods will print to a {@link TraceChannel}
77 * which in turn will print using whatever {@link TraceWriter}s have been
78 * added to it. Print methods that do not take a {@link TraceChannel}
79 * will use the default channel - see
80 * {@link TraceController#setDefaultChannel(TraceChannel)}<p>
81 *
82 * <b>If you are using this code in a JVM prior to 1.3 then you need to read this:</b>
83 * <br />Because trace lines are being processed on a second thread, there
84 * might still be trace messages in the queue when the VM exits. To solve
85 * this problem we introduce the method {@link #exit(int)} which will shut
86 * down the second thread and then call System.exit(). If you are running on
87 * a java 1.3 VM or higher then it is not neccessary to call {@link #exit(int)}
88 * as Trace will automatically install a shutdown hook which will do the
89 * neccessary cleanup.<p>
90 *
91 * @version $Revision: 1.7 $
92 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
93 */
94 public class Trace {
95 /*** An exception used to determine where the code is at any point in time. */
96 public static class WhereAmIException extends Exception {
97 private static final long serialVersionUID = 6876505080685244104L;
98
99 /*** Instantiate one */
100 public WhereAmIException() {
101 super("?? Where am I ??");
102 }
103 }
104
105 /***
106 * The equivilent of "standard out"
107 */
108 public static final TraceChannel out = new TraceChannel("STDOUT");
109 /***
110 * The equivilent of "standard error"
111 */
112 public static final TraceChannel err = new TraceChannel("STDERR");
113
114
115 private static final String LINE_SEPARATOR = System.getProperty("line.separator");
116
117 private static final TraceItemDispatcher TRACE_ITEM_DISPATCHER = new TraceItemDispatcher();
118 private static final TraceController TRACE_CONTROLLER = new TraceController();
119
120
121 /***
122 * Private constructor to prevent instantiation of this class.
123 */
124 private Trace() {
125 }
126
127 /***
128 * Print a line to the specified channel.
129 * If the channel is null then nothing will be printed.
130 * @param channel The trace channel to use
131 * @param string The text string to write.
132 */
133 public static void print( final TraceChannel channel, String string ) {
134 assertNotNull("channel", channel);
135 if( string == null ) {
136 string = "<null>";
137 }
138
139 final TraceItemDispatcher dispatcher = getDispatcher();
140 final TraceItem item = dispatcher.getNewTraceItem();
141
142 item.setMessage(string);
143 item.setChannel(channel);
144 dispatcher.dispatch(item);
145 }
146
147 /***
148 * Print a line to the default channel
149 * If the channel is null then nothing will be printed.
150 * @param string The text string to write.
151 */
152 public static void print( final String string ) {
153 print( getController().getDefaultChannel(), string );
154 }
155
156 /***
157 * Print the line to the specified channel with a new line at the end.
158 * If the channel is null then nothing will be printed.
159 * @param channel The trace channel to use
160 * @param string the string to write.
161 */
162 public static void println( final TraceChannel channel, final String string ) {
163 if( channel == null ) {
164 return;
165 }
166 print( channel, string + LINE_SEPARATOR );
167 }
168
169 /***
170 * Print the line to the default channel with a new line at the end.
171 *
172 * @param string the string to write.
173 */
174 public static void println( final String string ) {
175 print( getController().getDefaultChannel(), string + LINE_SEPARATOR );
176 }
177
178 /***
179 * Print the stack trace to the specified channel.
180 * If the channel is null then nothing will be printed.
181 *
182 * @param channel The trace channel to use
183 * @param throwable The exception to print
184 */
185 public static void printStackTrace( final TraceChannel channel,
186 final Throwable throwable ) {
187
188 if( channel == null ) {
189 return;
190 }
191 if( throwable == null ) {
192 println( channel, "<null exception>" );
193 return;
194 }
195
196 final TraceItemDispatcher dispatcher = getDispatcher();
197 final TraceItem item = dispatcher.getNewTraceItem();
198
199 item.setThrowable(throwable);
200 item.setChannel(channel);
201 dispatcher.dispatch(item);
202 }
203
204 /***
205 * Print the stack trace to the default channel.
206 * @param throwable The exception to print.
207 */
208 public static void printStackTrace( final Throwable throwable ) {
209 printStackTrace( getController().getDefaultChannel(), throwable );
210 }
211
212 /***
213 * Print the specified lines to the default trace channel.
214 * @param lines The lines to print.
215 */
216 public static void printLines( final String lines[] ) {
217 printLines( getController().getDefaultChannel(), lines );
218 }
219
220 /***
221 * Print the specified lines to the trace channel.
222 * If the channel is null then nothing will be printed.
223 *
224 * @param channel The trace channel to use
225 * @param lines the lines to print.
226 */
227 public static void printLines( final TraceChannel channel, final String lines[] ) {
228 if( channel == null ) {
229 return;
230 }
231 if( lines == null ) {
232 println( channel, "<null lines>" );
233 return;
234 }
235
236
237
238 int i;
239 for( i=0; i<lines.length; i++ ) {
240 println( channel, lines[i] );
241 }
242 }
243
244 /***
245 * Print a stack trace to show where we came from. It will be printed to the default
246 * channel.
247 */
248 public static void whereAmI() {
249 whereAmI( getController().getDefaultChannel() );
250 }
251
252 /***
253 * Print a stack trace to show where we came from. If the channel is null then
254 * nothing will be printed.
255 * @param channel The trace channel to use.
256 */
257 public static void whereAmI( final TraceChannel channel ) {
258 if( channel == null ) {
259 return;
260 }
261
262 printStackTrace( channel, new WhereAmIException() );
263 }
264
265 /***
266 * Dump the stack trace from the specified throwable into an array
267 * of strings where each line in the trace is a separate string.
268 * Additionally, expand all tabs to spaces.
269 *
270 * @param t The exception.
271 * @return The resulting string.
272 */
273 public static String[] throwableToStringArray( final Throwable t ) {
274 assertNotNull("t", t);
275
276 String token;
277 char c;
278 StringBuffer buffer;
279 int length;
280 int i;
281
282 final List vector = new ArrayList();
283 final StringTokenizer tokenizer
284 = new StringTokenizer( throwableToString(t), "\n");
285
286 while( tokenizer.hasMoreTokens() ) {
287 token = tokenizer.nextToken();
288
289
290 length = token.length();
291 buffer = new StringBuffer(length*2);
292 for( i=0; i<length; i++ ) {
293 c = token.charAt(i);
294 if( c == '\t' ) {
295 buffer.append(" ");
296 }
297 else {
298 buffer.append(c);
299 }
300 }
301 vector.add(buffer.toString());
302 }
303
304 final String array[] = new String[vector.size()];
305 vector.toArray(array);
306 return array;
307 }
308
309 /***
310 * Dump the stack trace from the specified throwable into a String.
311 *
312 * @param t The exception.
313 * @return The resulting string.
314 */
315 public static String throwableToString( final Throwable t ) {
316 assertNotNull("t", t);
317
318 final StringWriter writer = new StringWriter();
319 final PrintWriter printWriter = new PrintWriter(writer);
320 t.printStackTrace(printWriter);
321 printWriter.close();
322 return writer.toString();
323 }
324
325 /***
326 * Return the controller object for the debugging stuff
327 *
328 * @return The controller in use.
329 */
330 public static TraceController getController() {
331 return TRACE_CONTROLLER;
332 }
333
334 /*** @return The dispatcher */
335 static TraceItemDispatcher getDispatcher() {
336 return TRACE_ITEM_DISPATCHER;
337 }
338
339 /***
340 * Flush the trace queue.
341 */
342 public static void flush() {
343 getDispatcher().flush();
344 }
345
346 /***
347 * Shutdown all buffering and exit the VM with the specified
348 * status code.
349 * <p>
350 * <b>Note</b> This is no longer needed if you are running JDK1.3 or higher as
351 * we now register a shutdown hook to disable buffering before the VM exits.
352 * @param statusCode The status code returned when the application exits.
353 */
354 public static void exit( final int statusCode ) {
355 getController().setBufferingEnabled(false);
356 System.exit(statusCode);
357 }
358
359
360 /***
361 * Verify that the specified value is not null. If it is then throw an exception
362 *
363 * @param fieldName The name of the field to check
364 * @param fieldValue The value of the field to check
365 * @exception DetailedNullPointerException If fieldValue is null
366 */
367 protected static final void assertNotNull( final String fieldName, final Object fieldValue )
368 throws DetailedNullPointerException {
369
370 if( fieldValue == null ) {
371 throw new DetailedNullPointerException(fieldName);
372 }
373 }
374 }
375