Source code: com/flexstor/common/util/DiagnosticBase.java
1 /*
2 * DiagnosticBase.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:30 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.common.util;
12
13 import java.io.OutputStream;
14 import java.io.PrintWriter;
15 import java.io.StringWriter;
16 import java.io.Writer;
17 import java.text.SimpleDateFormat;
18 import java.util.Date;
19 import java.util.Hashtable;
20 import java.util.Properties;
21 import java.util.StringTokenizer;
22 import java.util.Vector;
23
24 /**
25 * Revision 1.6 2000-09-28 : 22:51:00 Visesh
26 * This class now extends DiagnosticCategoryI instead of DiagnosticCategory
27 * Removed constructors particular to DiagnosticCategory when it was in the hiearchy
28 * Changed all method definations by changing the type of parameters from DiagnosticCategoryI
29 * to DiagnosticCategory
30 *
31 * DiagnosticBase is a utility class for tracing and debugging. It is not intended
32 * for error and exception handling. It provides methods to print, trace, and warn
33 * and also to check for conditions. One or more output streams can be registered
34 * so that output can be saved in a file and shown on the console simultaneously.<P>
35 * This is the base class without dependencies providing the main functionality.
36 * Diagnostic adds the capability of using a properties file for configuration.
37 */
38 public class DiagnosticBase
39 extends DiagnosticCategoryI
40 {
41 protected static final String indent = " ";
42
43 protected static int outLevel = 0;
44 protected static boolean bPropertiesLoaded = false;
45 protected static boolean bOutput = false;
46 protected static boolean bOutputConsole = false;
47 protected static boolean bPrintTime = false;
48 protected static boolean bPrintThreadId = false;
49 protected static boolean bChecking = true;
50 protected static Vector traceCategories = new Vector();
51 protected static Vector fileTraceCategories = new Vector();
52 protected static Properties categoryTraceLevels = new Properties();
53
54 /** Default time format: year-month-day 24hour:minute:second:millisecond */
55 protected static final String COL_SEPARARTOR = " | ";
56 protected static final String DEFAULT_TIME_FORMAT = "yyyy-MM-dd kk:mm:ss:SSS";
57 protected static SimpleDateFormat timeFormat = new SimpleDateFormat(DEFAULT_TIME_FORMAT);
58
59 protected static PrintWriter[] outputDevices = new PrintWriter[0];
60 protected static Hashtable outputDevicesCategory = new Hashtable(); // contains one output writer per category
61 protected static final OutputStream systemOut = System.out; // keep a handle on the original System.out
62
63 /* public DiagnosticBase() { super(); }
64
65 public DiagnosticBase(String name)
66 {
67 super(name);
68 }*/
69
70 /**
71 * Adds an OutputStream as an output device
72 * @param outstream the output stream to write to, e.g. System.out
73 */
74 public static void addOutputDevice(OutputStream outstream)
75 {
76 addOutputDevice(new PrintWriter(outstream));
77 }
78
79 /**
80 * Adds a writer as an output device
81 * @param writer the writer to write to
82 */
83 public static void addOutputDevice(Writer writer)
84 {
85 PrintWriter[] newDevices = null;
86
87 if (outputDevices == null)
88 {
89 outputDevices = new PrintWriter[1];
90 }
91 else
92 {
93 newDevices = new PrintWriter[outputDevices.length+1];
94 System.arraycopy(outputDevices, 0, newDevices, 0, outputDevices.length);
95 }
96
97 newDevices[newDevices.length-1] = new PrintWriter(writer);
98 outputDevices = newDevices;
99 }
100
101 /**
102 * Adds an exclusive OutputStream as an output device for the specified category
103 * @param outstream the output stream to write to, e.g. System.out
104 * @param category a category for this output device
105 */
106 public static void addOutputDevice(DiagnosticCategory category, OutputStream outstream)
107 {
108 addOutputDevice(category, new PrintWriter(outstream));
109 }
110
111 /**
112 * Adds an exclusive writer as an output device for the specified category
113 * @param outstream the output stream to write to, e.g. System.out
114 * @param category a category for this output device
115 */
116 public static void addOutputDevice(DiagnosticCategory category, PrintWriter writer)
117 {
118 outputDevicesCategory.put(category, writer);
119 }
120
121
122 /**
123 * Enables or disables output generation
124 */
125 public static void enableOutput(boolean enable)
126 {
127 bOutput = enable;
128 }
129
130 /**
131 * Enables or disables the checking of conditions in precondition() and check()
132 */
133 public static void enableChecking(boolean enable)
134 {
135 bChecking = enable;
136 }
137
138 /**
139 * Enables or disables the time stamp print
140 */
141 public static void enableTimeStamp(boolean enable)
142 {
143 bPrintTime = enable;
144 }
145
146 /**
147 * Set the minimum threshold for trace and warning output.
148 * 0 is highest level
149 * +maxint is lowest level
150 *
151 * Setting this level to -1 will effectively turn off traces and warnings.
152 */
153 public static void setTraceLevel(int nLevel)
154 {
155 outLevel = nLevel;
156 }
157
158 protected static int getTraceLevel()
159 {
160 return outLevel;
161 }
162
163 public static int getTraceLevel(DiagnosticCategory category)
164 {
165 String sLevel = (String) categoryTraceLevels.get(category);
166 if (sLevel != null)
167 {
168 // convert the value to an int */
169 try
170 {
171 return Integer.parseInt(sLevel);
172 }
173 catch (NumberFormatException nfe)
174 {
175 return outLevel;
176 }
177 }
178 return outLevel;
179 }
180
181 /**
182 * Adds a category for tracing and warnings.
183 * @param category a new category
184 */
185 public static void addTraceCategory(DiagnosticCategory category)
186 {
187 traceCategories.addElement(category);
188 }
189
190 /**
191 * Removes a category for tracing and warnings.
192 * @param category the category to remove from the category list
193 */
194 public static void removeTraceCategory(DiagnosticCategory category)
195 {
196 traceCategories.removeElement(category);
197 }
198
199 /**
200 * Assert a precondition before a method body run. Use this to describe assumed state and
201 * parameter values. A precondition is raised if the given condition is not true.
202 * The check is only performed if checking is enabled.
203 * @param bCondition the condition to check
204 * @param sDescription a text to describe the precondition
205 */
206 public static void precondition(boolean bCondition, String sDescription)
207 throws DiagnosticPreconditionException
208 {
209 //getProperties();
210 if (bChecking && !bCondition)
211 {
212 println("Precondition failed: " + sDescription);
213 throw new DiagnosticPreconditionException(sDescription);
214 }
215 }
216
217 /**
218 * Check a condition within a method body. Use this to describe assumed results and state
219 * after internal operations. A check is raised if the given condition is not true.
220 * @param bCondition the condition to check
221 * @param sDescription a text to describe the condition
222 * @exception DiagnosticCheckException
223 */
224 public static void check(boolean bCondition, String sDescription)
225 throws DiagnosticCheckException
226 {
227 //getProperties();
228 if (bChecking && !bCondition)
229 {
230 println("Check failed: " + sDescription);
231 throw new DiagnosticCheckException(sDescription);
232 }
233 }
234
235 /**
236 * Shorthand for causing an exception if the code reaches an unexpected location
237 * by rethrowing the exception
238 * @param ex the exception to throw
239 * @exception Exception
240 */
241 public static void fail(Exception ex)
242 throws Exception
243 {
244 println(ex.getMessage());
245 printStackTrace(ex);
246 throw(ex);
247 }
248
249 /**
250 * Marks places where an exception is needed.
251 */
252 public static void needException()
253 throws DiagnosticCheckException
254 {
255 check(false, "Need Exception");
256 }
257
258 /**
259 * Output a warning if the threshold level is high enough, and a condition is true, and general
260 * output is enabled
261 * @param nLevel if level is less than or equal to the trace level, AND
262 * @param bCondition if condition is true, output is generated
263 * @param sDescription text to output
264 */
265 public static void warn(int nLevel, boolean bCondition, String sDescription)
266 {
267 //getProperties();
268 if (bCondition && nLevel <= outLevel)
269 println("wn[" + nLevel + "] " + indent.substring(0,nLevel*2) + sDescription);
270 }
271
272 /**
273 * Output a warning if the category is enabled, and a condition is true, and general
274 * output is enabled
275 * @param category if category is registered, AND
276 * @param bCondition if condition is true, output is generated
277 * @param sDescription text to output
278 */
279 public static void warn(DiagnosticCategory category, boolean bCondition, String sDescription)
280 {
281 //getProperties();
282 if (bCondition && (isRegisteredCategory(category) || !bPropertiesLoaded))
283 println(category, "wn[" + category.toString() + "] " + sDescription);
284 }
285
286 /**
287 * Output a warning if the category is enabled, and general
288 * output is enabled
289 * @param category if category is registered, output is generated
290 * @param sDescription text to output
291 */
292 public static void warn(DiagnosticCategory category, String sDescription)
293 {
294 warn(category, true, sDescription);
295 }
296
297 /**
298 * Output a warning if the category is enabled,
299 * the threshold level is high enough,
300 * and a condition is true, and general output is enabled
301 * @param category if category is registered, AND
302 * @param nLevel if level is high enough, AND
303 * @param bCondition if condition is true, output is generated
304 * @param sDescription text to output
305 */
306 public static void warn(DiagnosticCategory category, int nLevel, boolean bCondition, String sDescription)
307 {
308 //getProperties();
309 if (bCondition && ((isOutputLevel(category, nLevel) && isRegisteredCategory(category)) || !bPropertiesLoaded))
310 println(category, "wn[" + category.toString() + "." + nLevel + "] " + sDescription);
311 }
312
313 /**
314 * Output a trace if the threshold nLevel is high enough and general output is enabled
315 * @param nLevel threshold level
316 * @param sDescription text to output
317 */
318 public static void trace(int nLevel, String sDescription)
319 {
320 //getProperties();
321 if (nLevel <= outLevel)
322 println("[" + nLevel + "]" + indent.substring(0,nLevel*2) + sDescription);
323 }
324
325 /**
326 * Output a trace if the category and general output is enabled
327 * @param category a category to trace
328 * @param sDescription text to output
329 */
330 public static void trace(DiagnosticCategory category, String sDescription)
331 {
332 //getProperties();
333 if ( (outLevel > 0 && isRegisteredCategory(category)) || !bPropertiesLoaded )
334 println(category, "[" + category.toString() + "] " + sDescription);
335 }
336
337 /**
338 * Output a trace if the category is enabled and general output is enabled
339 * @param category category to be traced
340 * @param nLevel threshold trace level
341 * @param sDescription text to output
342 */
343 public static void trace(DiagnosticCategory category, int nLevel, String sDescription)
344 {
345 //getProperties();
346 if ( (isOutputLevel(category, nLevel) && isRegisteredCategory(category)) || !bPropertiesLoaded )
347 println(category, "[" + category.toString() + "." + nLevel + "] " + indent.substring(0,nLevel*2) + sDescription);
348 }
349
350 /**
351 * Print a stack trace of the Throwable to one PrintWriter. Used internally.
352 * @param writer a PrintWriter
353 * @param t the Trowable to trace
354 */
355 protected static void printOneStackTrace(PrintWriter writer, String sTime, Throwable t)
356 {
357 byte[] baDelimiters = { 10, 13}; // linefeed, carriage return
358 StringWriter sw = new StringWriter();
359
360 t.printStackTrace(new PrintWriter(sw));
361 StringTokenizer st = new StringTokenizer(sw.toString(), new String(baDelimiters));
362
363 synchronized(writer)
364 {
365 while (st.hasMoreTokens())
366 {
367 printOneLine(writer, sTime, st.nextToken());
368 }
369 }
370 }
371
372 /**
373 * Print a stack trace of the Throwable to the output devices.
374 * @param t the Trowable to trace
375 */
376 public static void printStackTrace(Throwable t)
377 {
378 if (bOutput || !bPropertiesLoaded)
379 {
380 String sTime = timeFormat.format(new Date());
381 for (int i = 0; i < outputDevices.length; i++)
382 {
383 printOneStackTrace(outputDevices[i],sTime, t);
384 }
385 }
386 }
387
388 /**
389 * Print a diagnostic stack trace of the current thread to the output devices.
390 */
391 public static void printStackTrace()
392 {
393 printStackTrace(new Exception("Diagnostic Stack Trace"));
394 }
395
396 /**
397 * Print a stack trace of the Throwable to the output devices
398 * if the category is enabled.
399 * @param category category to be traced
400 * @param t the Trowable to trace
401 */
402 public static void printStackTrace(DiagnosticCategory category, Throwable t)
403 {
404 if ((bOutput && isRegisteredCategory(category)) || !bPropertiesLoaded)
405 {
406 PrintWriter currentWriter = currentWriter = (PrintWriter)outputDevicesCategory.get(category);
407 if (currentWriter == null)
408 {
409 // print to general output
410 printStackTrace(t);
411 }
412 else
413 {
414 String sTime = timeFormat.format(new Date());
415 //print to specific print writer for category
416 printOneStackTrace(currentWriter, sTime, t);
417 // also print to console if flag is set
418 if (bOutputConsole)
419 {
420 printOneStackTrace(new PrintWriter(systemOut), sTime, t);
421 }
422 }
423 } // if
424 }
425
426 /**
427 * Print a stack trace of the Throwable to the output devices
428 * if the trace level is high enough.
429 * @param nLevel threshold trace level
430 * @param t the Trowable to trace
431 */
432 public static void printStackTrace(int nLevel, Throwable t)
433 {
434 //getProperties();
435 if (nLevel <= outLevel)
436 printStackTrace(t);
437 }
438
439 /**
440 * Print a stack trace of the Throwable to the output devices
441 * if the category is enabled and the trace level is high enough.
442 * @param category category to be traced
443 * @param nLevel threshold trace level
444 * @param t the Trowable to trace
445 */
446 public static void printStackTrace(DiagnosticCategory category, int nLevel, Throwable t)
447 {
448 //getProperties();
449 if ((isOutputLevel(category, nLevel) && isRegisteredCategory(category)) || !bPropertiesLoaded)
450 printStackTrace(category, t);
451 }
452
453 /**
454 * Prints text to the output devices with a line feed.
455 * @param sText the text to output
456 */
457 public static void println(String sText)
458 {
459 if (bOutput)
460 {
461 String sTime = timeFormat.format(new Date());
462 for (int i = 0; i < outputDevices.length; i++)
463 {
464 printOneLine(outputDevices[i], sTime, sText);
465 }
466 }
467 else if (!bPropertiesLoaded)
468 System.out.println( sText );
469 }
470
471 /**
472 * Print one line to one PrintWriter. Used internally.
473 * @param writer the writer to output to
474 * @param sText the text to print
475 */
476 protected static void printOneLine(PrintWriter writer, String sTime, String sText)
477 {
478 synchronized(writer)
479 {
480 if (bPrintTime)
481 {
482 writer.print(sTime);
483 writer.print(COL_SEPARARTOR);
484 }
485 if (bPrintThreadId)
486 {
487 writer.print(getThreadId());
488 writer.print(COL_SEPARARTOR);
489 }
490 writer.println(sText);
491 writer.flush();
492 }
493 }
494
495 /**
496 * Checks if an output device is registered for this category,
497 * if so, print to it, otherwise use general output devices.
498 * Prints text to the output devices with a line feed.
499 * @param sText the text to output
500 * @param category println for a category
501 */
502 protected static void println(DiagnosticCategory category, String sText)
503 {
504 if (bOutput)
505 {
506 PrintWriter currentWriter = (PrintWriter)outputDevicesCategory.get(category);
507 if (currentWriter == null)
508 {
509 // print to general output
510 println(sText);
511 }
512 else
513 {
514 String sTime = timeFormat.format(new Date());
515 //print to specific print writer for category
516 printOneLine(currentWriter, sTime, sText);
517 // also print to console if flag is set
518 if (bOutputConsole)
519 {
520 printOneLine(new PrintWriter(systemOut), sTime, sText);
521 }
522 } // else
523 } // if
524 else if (!bPropertiesLoaded)
525 System.out.println( sText );
526 }
527
528 /**
529 * Prints text to the output devices without a line feed.
530 * @param sText the text to output
531 */
532
533 /* made unavailable because it causes problems when multiple threads are printing
534 public static void print(String sText)
535 {
536 if (bOutput)
537 for (int i = 0; i < outputDevices.length; i++)
538 {
539 outputDevices[i].print(sText);
540 //outputDevices[i].flush();
541 }
542 }
543 */
544
545 protected static String getThreadId()
546 {
547 return Thread.currentThread().getName();
548 //return Thread.currentThread().getName() + "(" + Thread.currentThread().getClass().getName();
549 }
550
551 /**
552 * Prints the currently available memory.
553 * @param bRunGc Indicates if garbage collection is to be run
554 */
555 public static void printMemStatus(boolean bRunGc)
556 {
557 Runtime rt = Runtime.getRuntime();
558 println("Free memory : " + rt.freeMemory());
559 if (bRunGc)
560 {
561 rt.gc();
562 try{Thread.sleep(1000);}catch(InterruptedException e){}
563 println(" after gc: " + rt.freeMemory());
564 }
565 }
566
567 protected static boolean isRegisteredCategory(DiagnosticCategory category)
568 {
569 return (fileTraceCategories.contains(category) || traceCategories.contains(category));
570 }
571
572 protected static boolean isOutputLevel(DiagnosticCategory category, int nLevel)
573 {
574 // do we have a trace level specified for this category? */
575 String categoryLevel = (String)categoryTraceLevels.get(category);
576 if (categoryLevel != null)
577 {
578 // convert the value to an int */
579 try
580 {
581 int traceLevel = Integer.parseInt(categoryLevel);
582 // return whether the specified level is <= the category trace level */
583 return (nLevel <= traceLevel);
584 }
585 catch (NumberFormatException e)
586 {
587 // the value wasn't a number - ignore it
588 }
589 }
590 // at this point, we haven't found a valid category trace level, so use the global level. */
591 return (nLevel <= outLevel);
592 }
593
594 public static boolean isOutputEnabled(DiagnosticCategory category)
595 {
596 if ( !isRegisteredCategory(category) )
597 return false;
598 else
599 return bOutput;
600 }
601
602 } // end of class
603