Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 699   Methods: 49
NCLOC: 465   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
Log.java 20.3% 27.4% 36.7% 26.3%
coverage coverage
 1   
 package abbot;
 2   
 
 3   
 import java.io.*;
 4   
 import java.lang.reflect.InvocationTargetException;
 5   
 import java.util.*;
 6   
 
 7   
 import javax.swing.SwingUtilities;
 8   
 
 9   
 /**
 10   
    Various logging, assertion, and debug routines.  Typical usage is to
 11   
    include the following code
 12   
    <blockquote><code><pre>
 13   
    public static void main(String[] args) {
 14   
 &nbsp; args = Log.init(args)
 15   
 &nbsp; ...
 16   
    }
 17   
    </pre></code></blockquote>
 18   
    at an application's main entry point.  This way the Log class can remove its
 19   
    options from the full set passed into the application.  See the
 20   
    {@link #init(String[]) Log.init} method for initialization
 21   
    options. <p>
 22   
 
 23   
    General usage notes on public functions:<p>
 24   
    
 25   
    <ul>
 26   
    <li><b>warn</b><br>
 27   
    Programmer warnings; things that you think shouldn't be happening or
 28   
    indicate something might be wrong.  Warnings typically mean "Something
 29   
    happened that I didn't expect would happen".<p>
 30   
    <li><b>log</b><br>
 31   
    Important information that might be needed for later reference; things the
 32   
    user or debugger might be interested in.  By default, all messages go
 33   
    here.  Logs are made available so that the customer may provide us with an 
 34   
    accurate record of software activity.<br>
 35   
    All warnings and failed assertions are written to the log.  Debug
 36   
    statements are also written to log in non-release code.<p>
 37   
    <li><b>debug</b><br> 
 38   
    Any messages which might be useful for debugging (non-release code
 39   
    only).<p> 
 40   
    <li><b>assertTrue</b><br> 
 41   
    Assumed preconditions for proper execution, also referred to as
 42   
    invariants.<p>
 43   
    </ul>
 44   
    <p>
 45   
    
 46   
    Per-class stack trace depth can be specified when adding a class, e.g.
 47   
    classname[:stack-depth].<p>
 48   
    
 49   
    @author      twall
 50   
    @version     $Revision: 1.26 $
 51   
 */
 52   
 
 53   
 final public class Log {
 54   
     /** No instantiations. */
 55  0
     private Log() { }
 56   
     /** Global final to determine whether debugging code is generated.  This
 57   
         should be changed to false to build production code. */ 
 58   
     public static final boolean DEBUG_BUILD = true;
 59   
     /** Mnemonic to print all lines of a stack trace. */
 60   
     public static final int FULL_STACK = 0;
 61   
     /** Mnemonic to print the default number of lines of stack trace. */
 62   
     private static final int CLASS_STACK_DEPTH = -1;
 63   
     /** Basic warning categories.  FIXME use these. 
 64   
     public static final int ERROR   = 0x0001;
 65   
     public static final int WARNING = 0x0002;
 66   
     public static final int DEBUG   = 0x0004;
 67   
     public static final int INFO    = 0x0008;*/
 68   
 
 69   
     private static class LogSynchronizer extends Object { }
 70   
     /** Synchronize message output. */
 71   
     private static LogSynchronizer synchronizer = new LogSynchronizer();
 72   
     /** Whether any debugging output is enabled. */
 73   
     public static boolean expectDebugOutput;
 74   
     /** Enable assert checks. */
 75   
     private static boolean assertChecks = DEBUG_BUILD;
 76   
     /** Whether to terminate on assertion failures. */
 77   
     private static boolean exitOnAssertionFailure = false;
 78   
     /** Whether to log messages. Default on so that we capture output until
 79   
      * the log file has been set or not set in {@link #init(String[])}.
 80   
      */
 81   
     private static boolean logMessages = true;
 82   
     /** Whether to print programmer warnings. */
 83   
     private static boolean printConsoleWarnings = DEBUG_BUILD;
 84   
     /** Whether to show threads in debug output. */
 85   
     private static boolean showThreads;
 86   
     /** Default number of lines of stack trace to print. */
 87   
     private static int debugStackDepth = 1;
 88   
     /** Default number of lines of exception stack trace to print. */
 89   
     private static int excStackDepth = FULL_STACK;
 90   
     /** Show timestamps in the log? */
 91   
     private static boolean showTimestamp = true;
 92   
     private static java.text.DateFormat timestampFormat =
 93   
         new java.text.SimpleDateFormat("yyMMdd HH:mm:ss:SSS ");
 94   
 
 95   
     /** Strip this out of output, since it doesn't add information to see it
 96   
         repeatedly.  Some projects have <i>really</i> long prefixes. */
 97   
     private static final String COMMON_PREFIX = null;
 98   
     /** Store which classes we want to see debug info for.  FIXME make it a
 99   
         map and make the value the debug level. */
 100   
     private static HashMap debugged = new HashMap();
 101   
     /** Store which classes we don't want to see debug info for */
 102   
     private static HashSet notdebugged = new HashSet();
 103   
     /** Debug all classes? */
 104   
     private static boolean debugAll;
 105   
     /** Treat inner/anonymous classes as outer class? */
 106   
     private static boolean debugInner = true;
 107   
 
 108   
     private static final String DEFAULT_LOGFILE_NAME = "abbot.log";
 109   
     private static ByteArrayOutputStream preInitLog =
 110   
         new ByteArrayOutputStream();
 111   
     private static PrintStream log = new PrintStream(preInitLog);
 112   
 
 113   
     // Save these for future use
 114   
     static PrintStream systemOut = System.out;
 115   
     static PrintStream systemErr = System.err;
 116   
 
 117   
     /** Debug/log initialization, presumably from the command line. 
 118   
         <br>Recognized options:
 119   
         <pre>
 120   
         --debug all | className[:depth] | *.partialClassName[:depth]
 121   
         --no-debug className | *.partialClassName
 122   
         --log <log file name>
 123   
         --no-timestamp
 124   
         --enable-warnings
 125   
         --show-threads
 126   
         --stack-depth <depth>
 127   
         --exception-depth <depth>
 128   
         </pre>
 129   
      */
 130  3
     public static String[] init(String[] args){
 131   
 
 132  3
         if (!DEBUG_BUILD) {
 133   
         // Redirect System.err & System.out
 134  0
         PrintStream nullStream = new PrintStream(new OutputStream() {
 135  0
         public void write(int b) {            
 136   
         }
 137   
         });
 138  0
         System.setErr(nullStream);
 139  0
         System.setOut(nullStream);
 140   
     }
 141  3
         Vector newArgs = new Vector(args.length);
 142  3
         for (int i=0;i < args.length;i++){
 143  9
             if (args[i].equals("--enable-warnings")){
 144  0
                 printConsoleWarnings = true;
 145   
             }
 146  9
             else if (args[i].equals("--no-timestamp")) {
 147  0
                 showTimestamp = false;
 148   
             }
 149  9
             else if (args[i].equals("--show-threads")){
 150  0
                 showThreads = true;
 151   
             }
 152  9
             else if (args[i].equals("--stack-depth")) {
 153  0
                 if (++i < args.length) {
 154  0
                     try {
 155  0
                         debugStackDepth = Integer.parseInt(args[i]);
 156   
                     }
 157   
                     catch(Exception exc) {
 158   
                     }
 159   
                 }
 160   
                 else {
 161  0
                     internalWarn("Ignoring --stack-depth with no argument");
 162   
                 }
 163   
             }
 164  9
             else if (args[i].equals("--exception-depth")) {
 165  0
                 if (++i < args.length) {
 166  0
                     try {
 167  0
                         excStackDepth = Integer.parseInt(args[i]);
 168   
                     }
 169   
                     catch(Exception exc) {
 170   
                     }
 171   
                 }
 172   
                 else {
 173  0
                     internalWarn("Ignoring --exception-depth with no argument");
 174   
                 }
 175   
             }
 176  9
             else if (args[i].equals("--debug")
 177   
                      || args[i].equals("--no-debug")) {
 178   
                 // since we're enabling some debugging, set the other settings
 179   
                 // to debug defaults...
 180  0
                 boolean exclude = args[i].startsWith("--no");
 181   
 
 182   
                 // Re-enable stdout/stderr if they were removed
 183  0
                 if (!DEBUG_BUILD) {
 184  0
                     System.setOut(systemOut);
 185  0
                     System.setErr(systemErr);
 186   
                 }
 187  0
                 if (++i < args.length) {
 188  0
                     if (exclude)
 189  0
                         removeDebugClass(args[i]);
 190   
                     else
 191  0
                         addDebugClass(args[i]);
 192   
                 }
 193   
                 else {
 194  0
                     internalWarn("Ignoring " + args[i-1] 
 195   
                                  + " with no argument");
 196   
                 }
 197   
             }
 198  9
             else if (args[i].equals("--log")){
 199  0
                 String filename = DEFAULT_LOGFILE_NAME;
 200  0
                 if (++i < args.length)
 201  0
                     filename = args[i];
 202  0
                 enableLogging(filename);
 203   
             }
 204   
             else {
 205  9
                 newArgs.addElement(args[i]);
 206   
             }
 207   
         }
 208  3
         String[] result = new String[newArgs.size()];
 209  3
         for (int i=0;i < result.length;i++){
 210  9
             result[i] = (String)newArgs.elementAt(i);
 211   
         }
 212  3
         return result;
 213   
     }
 214   
 
 215   
     /** Is log output enabled? */
 216  5556
     public static boolean loggingEnabled() { 
 217  5556
         return logMessages && log != null; 
 218   
     }
 219   
 
 220   
     /** Enable log output to the given file.  If the filename given is "-",
 221   
      * stdout is used instead of a file.
 222   
      */
 223  0
     public static void enableLogging(String filename) {
 224  0
         logMessages = true;
 225  0
         try {
 226  0
             if (filename.equals("-"))
 227  0
                 log = systemOut;
 228   
             else
 229  0
                 log = new PrintStream(new FileOutputStream(filename), 
 230   
                                       true);
 231  0
             String hostname = "127.0.0.1";
 232  0
             try { 
 233  0
                 hostname = java.net.InetAddress.
 234   
                     getLocalHost().getHostName(); 
 235   
             }
 236   
             catch(java.net.UnknownHostException uhe) { 
 237  0
                 internalWarn("Can't get hostname, using " + hostname);
 238   
             }
 239  0
             log("Log started on " + hostname);
 240  0
             if (!DEBUG_BUILD) {
 241   
                 // Prefer output to go to the log on a release build, since
 242   
                 // there may not be any console.
 243  0
                 if (expectDebugOutput) {
 244  0
                     systemOut.println("Output redirected.  See the log "
 245   
                                       + "file for debug output.");
 246   
                 }
 247  0
                 System.setErr(log);
 248  0
                 System.setOut(log);
 249   
             }
 250   
             // Make sure the log gets closed on System.exit
 251  0
             Runtime.getRuntime().addShutdownHook(new Thread("Log shutdown hook") {
 252  0
                 public void run() { close(); }
 253   
             });
 254   
         }
 255   
         catch (FileNotFoundException exc){
 256  0
             internalWarn("Can't open log file " + filename);
 257   
         }
 258   
     }
 259   
 
 260   
     /** Sets the debug stack depth to the given amount */
 261  0
     public static void setDebugStackDepth(int depth) {
 262  0
         debugStackDepth = depth;
 263   
     }
 264   
 
 265   
     /** Indicate the class name to exclude from debug output. */
 266  0
     public static void removeDebugClass(String id) {
 267  0
         setDebugClass(id, false);
 268   
     }
 269   
 
 270   
     /** Indicate that the given class should NOT be debugged 
 271   
         (assuming --debug all) */
 272  0
     public static void removeDebugClass(Class c) {
 273  0
         notdebugged.add(c);
 274  0
         debugged.remove(c);
 275  0
         Log.debug("Debugging disabled for " + c);
 276  0
         expectDebugOutput = debugged.size() > 0 || debugAll;
 277   
     }
 278   
 
 279   
     /** Indicate the class name[:depth] to add to debug output. */
 280  0
     public static void addDebugClass(String id) {
 281  0
         setDebugClass(id, true);
 282   
     }
 283   
 
 284   
     /** Indicate the class to add to debug output. */
 285  0
     public static void addDebugClass(Class c) {
 286  0
         addDebugClass(c, CLASS_STACK_DEPTH);
 287   
     }
 288   
 
 289   
     /** Indicate that debug messages should be output for the given class. */
 290  0
     public static void addDebugClass(Class c, int depth){
 291  0
         expectDebugOutput = true;
 292  0
         debugged.put(c, new Integer(depth));
 293  0
         notdebugged.remove(c);
 294  0
         Log.debug("Debugging enabled for " + c);
 295   
     }
 296   
 
 297   
     /** Parse the given string, which may should be of the format
 298   
         "class[:depth]" */
 299  0
     private static void setDebugClass(String id, boolean enable) {
 300  0
         if (id.equals("all")) {
 301  0
             debugAll = enable;
 302  0
             if (enable) {
 303  0
                 notdebugged.clear();
 304  0
                 expectDebugOutput = true;
 305   
             }
 306   
             else {
 307  0
                 debugged.clear();
 308  0
                 expectDebugOutput = false;
 309   
             }
 310   
         }
 311   
         else {
 312  0
             int colon = id.indexOf(":");
 313  0
             String className = colon == -1 ? id : id.substring(0, colon);
 314  0
             try {
 315  0
                 Class c = getClassFromDescriptor(className);
 316  0
                 try {
 317  0
                     int depth = colon == -1 
 318   
                         ? debugStackDepth 
 319   
                         : Integer.parseInt(id.substring(colon+1));
 320  0
                     if (enable)
 321  0
                         addDebugClass(c, depth);
 322   
                     else
 323  0
                         removeDebugClass(c);
 324   
                 }
 325   
                 catch (NumberFormatException nfe) {
 326  0
                     if (enable)
 327  0
                         addDebugClass(c);
 328   
                     else 
 329  0
                         removeDebugClass(c);
 330   
                 }
 331   
             }
 332   
             catch (ClassNotFoundException exc){
 333  0
                 internalWarn("Class '" + className + "' not found");
 334   
             }
 335   
         }
 336   
     }
 337   
 
 338   
     /** Returns class from given name/descriptor.  Descriptor can be either a
 339   
         fully qualified classname, or a classname beginning with *. with
 340   
         client-specific package and classname following.
 341   
     */
 342  0
     private static Class getClassFromDescriptor(String className)
 343   
         throws ClassNotFoundException {
 344  0
         if (COMMON_PREFIX != null && className.startsWith("*.")) {
 345  0
             className = COMMON_PREFIX + className.substring(1);
 346   
         }
 347  0
         return Class.forName(className);
 348   
     }
 349   
 
 350   
     /** Return the requested number of levels of stack trace, not including
 351   
         this call.   Returns the full stack trace if LINES is FULL_STACK.
 352   
         Skip the first POP frames of the trace, which is for excluding the
 353   
         innermost stack frames when debug functions make nested calls.
 354   
         The outermost call of getStackTrace itself is always removed from the
 355   
         trace.  */ 
 356  185
     private static String getStackTrace(int pop, int lines) {
 357  185
         return getStackTrace(pop, lines, new Throwable("--debug--"));
 358   
     }
 359   
 
 360   
     /** Return the requested number of levels of stack trace, not including
 361   
         this call.   Returns the full stack trace if LINES is FULL_STACK.
 362   
         Skip the first POP frames of the trace, which is for excluding the
 363   
         innermost stack frames when debug functions make nested calls.
 364   
         The outermost call of getStackTrace itself is always removed from the
 365   
         trace.   Provide an exception to use for the stack trace,
 366   
         rather than using the current program location.
 367   
     */
 368  202
     private static String getStackTrace(int pop, int lines, Throwable thr) {
 369  202
         String stack = getStackTrace(pop, thr);
 370  202
         if (lines == FULL_STACK) 
 371  195
             return stack;
 372  7
         return trimStackTrace(stack, lines);
 373   
     }
 374   
 
 375   
     /** Return the stack trace contained in the given Throwable.
 376   
         Skip the first POP frames of the trace, which is for excluding the
 377   
         innermost stack frames when debug functions make nested calls.
 378   
         The outermost call of getStackTrace itself is always removed from the
 379   
         trace.   
 380   
     */
 381  202
     private static String getStackTrace(int pop, Throwable thr){
 382