1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 /* 23 * 24 * ConsoleLogger.java 25 * 26 */ 27 package org.apache.qetest; 28 29 import java.io.PrintStream; 30 import java.io.PrintWriter; 31 import java.io.StringWriter; 32 import java.util.Enumeration; 33 import java.util.Hashtable; 34 import java.util.Properties; 35 36 /** 37 * Logger that prints human-readable output to System.out. 38 * As an experiment, the ConsoleLogger supports an independent 39 * loggingLevel that can be more restrictive than a loggingLevel 40 * set in any enclosing Reporter. 41 * Note this isn't quite as well architected as I would like, 42 * but it does address what seems to be the 43 * most common usage case: where you're running tests automatically, 44 * and likely will be using some file-based output for results 45 * analysis. This allows you to set loggingLevel for your Reporter 46 * high, so that most/all output is sent to the file, but set this 47 * ConsoleLogger's loggingLevel low, so only critical problems 48 * are displayed on the screen (since most of the time users will 49 * never be watching the console in this situation). 50 * @author Shane_Curcuru@lotus.com 51 * @version $Id$ 52 */ 53 public class ConsoleLogger implements Logger 54 { 55 56 //----------------------------------------------------- 57 //-------- Class members -------- 58 //----------------------------------------------------- 59 60 /** Our output stream - currently hard-coded to System.out. */ 61 protected PrintStream outStream = System.out; 62 63 /** If we're ready to start outputting yet. */ 64 protected boolean ready = false; 65 66 /** If we should indent sub-results or not. */ 67 protected boolean indent = true; 68 69 /** Level (number of spaces?) to indent sub-results. */ 70 protected StringBuffer sIndent = new StringBuffer(); 71 72 /** Generic properties for this Logger; sort-of replaces instance variables. */ 73 protected Properties loggerProps = null; 74 75 /** 76 * Special LoggingLevel for just this instance. 77 * May be set from "ConsoleLogger.loggingLevel" property; 78 * defaults to 100 which should be larger than any other 79 * loggingLevels in use currently. 80 * Note that different levels here will even restrict output 81 * from control messages like testCaseInit; see individual 82 * javadocs for what controls what. This may affect the level 83 * of indenting you see as well; I traded off a little speed 84 * (don't calc indent if not using that message) for prettiness. 85 */ 86 protected int consoleLoggingLevel = 100; 87 88 //----------------------------------------------------- 89 //-------- Control and utility routines -------- 90 //----------------------------------------------------- 91 92 /** Simple constructor, does not perform initialization. */ ConsoleLogger()93 public ConsoleLogger() 94 { /* no-op */ 95 } 96 97 /** 98 * Constructor calls initialize(p). 99 * @param p Properties block to initialize us with. 100 */ ConsoleLogger(Properties p)101 public ConsoleLogger(Properties p) 102 { 103 ready = initialize(p); 104 } 105 106 /** 107 * Return a description of what this Logger does. 108 * @return "reports results to System.out". 109 */ getDescription()110 public String getDescription() 111 { 112 return ("org.apache.qetest.ConsoleLogger - reports results to System.out."); 113 } 114 115 /** 116 * Returns information about the Property name=value pairs that 117 * are understood by this Logger/Reporter. 118 * @return same as {@link java.applet.Applet.getParameterInfo}. 119 */ getParameterInfo()120 public String[][] getParameterInfo() 121 { 122 123 String pinfo[][] = 124 { 125 { OPT_INDENT, "boolean", "If reporter should indent sub-results" }, 126 { "ConsoleLogger.loggingLevel", "String", "loggingLevel for just ConsoleLogger; only if more restrictive than other loggingLevels" } 127 }; 128 129 return pinfo; 130 } 131 132 /** 133 * Accessor methods for our properties block. 134 * 135 * NEEDSDOC ($objectName$) @return 136 */ getProperties()137 public Properties getProperties() 138 { 139 return loggerProps; 140 } 141 142 /** 143 * Accessor methods for our properties block. 144 * @param p Properties to set (is cloned). 145 */ setProperties(Properties p)146 public void setProperties(Properties p) 147 { 148 149 if (p != null) 150 { 151 loggerProps = (Properties) p.clone(); 152 } 153 } 154 155 /** 156 * Call once to initialize this Logger/Reporter from Properties. 157 * @param Properties block to initialize from. 158 * @param status, true if OK, false if an error occoured. 159 * 160 * @param p Properties block to initialize from 161 * @return true if OK; currently always returns true 162 */ initialize(Properties p)163 public boolean initialize(Properties p) 164 { 165 166 setProperties(p); 167 168 String i = loggerProps.getProperty(OPT_INDENT); 169 170 if (i != null) 171 { 172 if (i.toLowerCase().equals("no") 173 || i.toLowerCase().equals("false")) 174 indent = false; 175 else if (i.toLowerCase().equals("yes") 176 || i.toLowerCase().equals("true")) 177 indent = true; 178 } 179 180 // Grab our specific loggingLevel and set if needed 181 String logLvl = loggerProps.getProperty("ConsoleLogger.loggingLevel"); 182 if (logLvl != null) 183 { 184 // Note: if present, we'll attempt to set it 185 // It doesn't really make much sense to set it if 186 // this value is larger than an enclosing Reporter's 187 // loggingLevel, but it won't hurt either 188 try 189 { 190 consoleLoggingLevel = Integer.parseInt(logLvl); 191 } 192 catch (NumberFormatException numEx) 193 { /* no-op */ 194 } 195 } 196 197 ready = true; 198 199 return true; 200 } 201 202 /** 203 * Is this Logger/Reporter ready to log results? 204 * @return status - true if it's ready to report, false otherwise 205 */ isReady()206 public boolean isReady() 207 { 208 return ready; 209 } 210 211 /** 212 * Flush this Logger/Reporter - no-op for ConsoleLogger. 213 */ flush()214 public void flush() 215 { /* no-op */ 216 } 217 218 /** 219 * Close this Logger/Reporter - essentially no-op for ConsoleLogger. 220 */ close()221 public void close() 222 { 223 224 flush(); 225 226 ready = false; 227 } 228 229 /** Simplistic indenting - two spaces. */ indent()230 protected void indent() 231 { 232 if (indent) 233 sIndent.append(" "); 234 } 235 236 /** Simplistic outdenting - two spaces. */ outdent()237 protected void outdent() 238 { 239 if ((indent) && (sIndent.length() >= 2)) 240 sIndent.setLength(sIndent.length() - 2); 241 } 242 243 //----------------------------------------------------- 244 //-------- Testfile / Testcase start and stop routines -------- 245 //----------------------------------------------------- 246 247 /** 248 * Report that a testfile has started. 249 * Output only when ConsoleLogger.loggingLevel >= ERRORMSG 250 * 251 * @param name file name or tag specifying the test. 252 * @param comment comment about the test. 253 */ testFileInit(String name, String comment)254 public void testFileInit(String name, String comment) 255 { 256 if (consoleLoggingLevel < ERRORMSG) 257 return; 258 259 outStream.println(sIndent + "TestFileInit " + name + ":" + comment); 260 indent(); 261 } 262 263 /** 264 * Report that a testfile has finished, and report it's result. 265 * Output only when ConsoleLogger.loggingLevel >= ERRORMSG 266 * 267 * @param msg message or name of test to log out 268 * @param result result of testfile 269 */ testFileClose(String msg, String result)270 public void testFileClose(String msg, String result) 271 { 272 if (consoleLoggingLevel < ERRORMSG) 273 return; 274 275 outdent(); 276 outStream.println(sIndent + "TestFileClose(" + result + ") " + msg); 277 } 278 279 /** 280 * Report that a testcase has started. 281 * Output only when ConsoleLogger.loggingLevel >= WARNINGMSG 282 * 283 * @param comment short description of this test case's objective. 284 */ testCaseInit(String comment)285 public void testCaseInit(String comment) 286 { 287 if (consoleLoggingLevel < WARNINGMSG) 288 return; 289 290 outStream.println(sIndent + "TestCaseInit " + comment); 291 indent(); 292 } 293 294 /** 295 * Report that a testcase has finished, and report it's result. 296 * Output only when ConsoleLogger.loggingLevel >= WARNINGMSG 297 * 298 * @param msg message of name of test case to log out 299 * @param result result of testfile 300 */ testCaseClose(String msg, String result)301 public void testCaseClose(String msg, String result) 302 { 303 if (consoleLoggingLevel < WARNINGMSG) 304 return; 305 306 outdent(); 307 outStream.println(sIndent + "TestCaseClose(" + result + ") " + msg); 308 } 309 310 //----------------------------------------------------- 311 //-------- Test results logging routines -------- 312 //----------------------------------------------------- 313 314 /** 315 * Report a comment to result file with specified severity. 316 * Output only when ConsoleLogger.loggingLevel >= level 317 * 318 * @param level severity or class of message. 319 * @param msg comment to log out. 320 */ logMsg(int level, String msg)321 public void logMsg(int level, String msg) 322 { 323 if (consoleLoggingLevel < level) 324 return; 325 326 outStream.println(sIndent + msg); 327 } 328 329 /** 330 * Report an arbitrary String to result file with specified severity. 331 * Log out the String provided exactly as-is. 332 * Output only when ConsoleLogger.loggingLevel >= level 333 * 334 * @param level severity or class of message. 335 * @param msg arbitrary String to log out. 336 */ logArbitrary(int level, String msg)337 public void logArbitrary(int level, String msg) 338 { 339 if (consoleLoggingLevel < level) 340 return; 341 342 outStream.println(msg); 343 } 344 345 /** 346 * Logs out statistics to result file with specified severity. 347 * Output only when ConsoleLogger.loggingLevel >= level 348 * 349 * @param level severity of message. 350 * @param lVal statistic in long format. 351 * @param dVal statistic in double format. 352 * @param msg comment to log out. 353 */ logStatistic(int level, long lVal, double dVal, String msg)354 public void logStatistic(int level, long lVal, double dVal, String msg) 355 { 356 if (consoleLoggingLevel < level) 357 return; 358 359 outStream.println(sIndent + msg + " l: " + lVal + " d: " + dVal); 360 } 361 362 /** 363 * Logs out Throwable.toString() and a stack trace of the 364 * Throwable with the specified severity. 365 * @author Shane_Curcuru@lotus.com 366 * @param level severity of message. 367 * @param throwable throwable/exception to log out. 368 * @param msg description of the throwable. 369 */ logThrowable(int level, Throwable throwable, String msg)370 public void logThrowable(int level, Throwable throwable, String msg) 371 { 372 if (consoleLoggingLevel < level) 373 return; 374 375 StringWriter sWriter = new StringWriter(); 376 377 sWriter.write(msg + "\n"); 378 sWriter.write(throwable.toString() + "\n"); 379 380 PrintWriter pWriter = new PrintWriter(sWriter); 381 throwable.printStackTrace(pWriter); 382 383 outStream.println(sWriter.toString()); 384 } 385 386 /** 387 * Logs out a element to results with specified severity. 388 * Simply indents and dumps output as string like so: 389 * <pre> 390 * element 391 * attr1=value1 392 * ... 393 * msg.toString() 394 * </pre> 395 * Output only when ConsoleLogger.loggingLevel >= level 396 * 397 * @param level severity of message. 398 * @param element name of enclosing element 399 * @param attrs hash of name=value attributes 400 * @param msg Object to log out; up to reporters to handle 401 * processing of this; usually logs just .toString(). 402 */ logElement(int level, String element, Hashtable attrs, Object msg)403 public void logElement(int level, String element, Hashtable attrs, 404 Object msg) 405 { 406 if (consoleLoggingLevel < level) 407 return; 408 409 if ((element == null) 410 || (attrs == null)) 411 { 412 // Bail if either element name or attr list is null 413 // Note: we should really handle this case more elegantly 414 return; 415 } 416 417 indent(); 418 outStream.println(sIndent + element); 419 indent(); 420 421 for (Enumeration keys = attrs.keys(); 422 keys.hasMoreElements(); /* no increment portion */ ) 423 { 424 Object key = keys.nextElement(); 425 426 outStream.println(sIndent + key.toString() + "=" 427 + attrs.get(key).toString()); 428 } 429 430 outdent(); 431 if (msg != null) 432 outStream.println(sIndent + msg.toString()); 433 outdent(); 434 } 435 436 /** 437 * Logs out contents of a Hashtable with specified severity. 438 * Output only when ConsoleLogger.loggingLevel >= level 439 * 440 * @param level severity or class of message. 441 * @param hash Hashtable to log the contents of. 442 * @param msg decription of the Hashtable. 443 */ logHashtable(int level, Hashtable hash, String msg)444 public void logHashtable(int level, Hashtable hash, String msg) 445 { 446 if (consoleLoggingLevel < level) 447 return; 448 449 indent(); 450 outStream.println(sIndent + "HASHTABLE: " + msg); 451 indent(); 452 453 if (hash == null) 454 { 455 outStream.println(sIndent + "hash == null, no data"); 456 } 457 else 458 { 459 try 460 { 461 462 // Fake the Properties-like output 463 for (Enumeration keys = hash.keys(); 464 keys.hasMoreElements(); /* no increment portion */ ) 465 { 466 Object key = keys.nextElement(); 467 468 outStream.println(sIndent + key.toString() + "=" 469 + hash.get(key).toString()); 470 } 471 } 472 catch (Exception e) 473 { 474 475 // No-op: should ensure we have clean output 476 } 477 } 478 479 outdent(); 480 outdent(); 481 } 482 483 //----------------------------------------------------- 484 //-------- Test results reporting check* routines -------- 485 //----------------------------------------------------- 486 487 /** 488 * Writes out a Pass record with comment. 489 * Output only when ConsoleLogger.loggingLevel > FAILSONLY 490 * 491 * @param comment comment to log with the pass record. 492 */ checkPass(String comment)493 public void checkPass(String comment) 494 { 495 // Note <=, since FAILSONLY is a special level 496 if (consoleLoggingLevel <= FAILSONLY) 497 return; 498 499 outStream.println(sIndent + "PASS! " + comment); 500 } 501 502 /** 503 * Writes out an ambiguous record with comment. 504 * Output only when ConsoleLogger.loggingLevel > FAILSONLY 505 * 506 * @param comment comment to log with the ambg record. 507 */ checkAmbiguous(String comment)508 public void checkAmbiguous(String comment) 509 { 510 // Note <=, since FAILSONLY is a special level 511 if (consoleLoggingLevel <= FAILSONLY) 512 return; 513 514 outStream.println(sIndent + "AMBG " + comment); 515 } 516 517 /** 518 * Writes out a Fail record with comment. 519 * Output only when ConsoleLogger.loggingLevel >= FAILSONLY 520 * 521 * @param comment comment to log with the fail record. 522 */ checkFail(String comment)523 public void checkFail(String comment) 524 { 525 if (consoleLoggingLevel < FAILSONLY) 526 return; 527 528 outStream.println(sIndent + "FAIL " + comment); 529 } 530 531 /** 532 * Writes out a Error record with comment. 533 * Output only when ConsoleLogger.loggingLevel >= ERRORMSG 534 * 535 * @param comment comment to log with the error record. 536 */ checkErr(String comment)537 public void checkErr(String comment) 538 { 539 if (consoleLoggingLevel < ERRORMSG) 540 return; 541 542 outStream.println(sIndent + "ERROR " + comment); 543 } 544 545 /* EXPERIMENTAL: have duplicate set of check*() methods 546 that all output some form of ID as well as comment. 547 Leave the non-ID taking forms for both simplicity to the 548 end user who doesn't care about IDs as well as for 549 backwards compatibility. 550 */ 551 552 /** 553 * Writes out a Pass record with comment and ID. 554 * Output only when ConsoleLogger.loggingLevel > FAILSONLY 555 * 556 * @param comment comment to log with the pass record. 557 * @param ID token to log with the pass record. 558 */ checkPass(String comment, String id)559 public void checkPass(String comment, String id) 560 { 561 // Note <=, since FAILSONLY is a special level 562 if (consoleLoggingLevel <= FAILSONLY) 563 return; 564 565 if (id != null) 566 outStream.println(sIndent + "PASS! (" + id + ") " + comment); 567 else 568 outStream.println(sIndent + "PASS! " + comment); 569 } 570 571 /** 572 * Writes out an ambiguous record with comment and ID. 573 * Output only when ConsoleLogger.loggingLevel > FAILSONLY 574 * 575 * @param comment to log with the ambg record. 576 * @param ID token to log with the pass record. 577 */ checkAmbiguous(String comment, String id)578 public void checkAmbiguous(String comment, String id) 579 { 580 // Note <=, since FAILSONLY is a special level 581 if (consoleLoggingLevel <= FAILSONLY) 582 return; 583 584 if (id != null) 585 outStream.println(sIndent + "AMBG (" + id + ") " + comment); 586 else 587 outStream.println(sIndent + "AMBG " + comment); 588 } 589 590 /** 591 * Writes out a Fail record with comment and ID. 592 * Output only when ConsoleLogger.loggingLevel >= FAILSONLY 593 * 594 * @param comment comment to log with the fail record. 595 * @param ID token to log with the pass record. 596 */ checkFail(String comment, String id)597 public void checkFail(String comment, String id) 598 { 599 if (consoleLoggingLevel < FAILSONLY) 600 return; 601 602 if (id != null) 603 outStream.println(sIndent + "FAIL! (" + id + ") " + comment); 604 else 605 outStream.println(sIndent + "FAIL! " + comment); 606 } 607 608 /** 609 * Writes out an Error record with comment and ID. 610 * Output only when ConsoleLogger.loggingLevel >= ERRORMSG 611 * 612 * @param comment comment to log with the error record. 613 * @param ID token to log with the pass record. 614 */ checkErr(String comment, String id)615 public void checkErr(String comment, String id) 616 { 617 if (consoleLoggingLevel < ERRORMSG) 618 return; 619 620 if (id != null) 621 outStream.println(sIndent + "ERROR (" + id + ") " + comment); 622 else 623 outStream.println(sIndent + "ERROR " + comment); 624 } 625 } // end of class ConsoleLogger 626 627