/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id$ */ /* * * ConsoleLogger.java * */ package org.apache.qetest; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; /** * Logger that prints human-readable output to System.out. * As an experiment, the ConsoleLogger supports an independent * loggingLevel that can be more restrictive than a loggingLevel * set in any enclosing Reporter. * Note this isn't quite as well architected as I would like, * but it does address what seems to be the * most common usage case: where you're running tests automatically, * and likely will be using some file-based output for results * analysis. This allows you to set loggingLevel for your Reporter * high, so that most/all output is sent to the file, but set this * ConsoleLogger's loggingLevel low, so only critical problems * are displayed on the screen (since most of the time users will * never be watching the console in this situation). * @author Shane_Curcuru@lotus.com * @version $Id$ */ public class ConsoleLogger implements Logger { //----------------------------------------------------- //-------- Class members -------- //----------------------------------------------------- /** Our output stream - currently hard-coded to System.out. */ protected PrintStream outStream = System.out; /** If we're ready to start outputting yet. */ protected boolean ready = false; /** If we should indent sub-results or not. */ protected boolean indent = true; /** Level (number of spaces?) to indent sub-results. */ protected StringBuffer sIndent = new StringBuffer(); /** Generic properties for this Logger; sort-of replaces instance variables. */ protected Properties loggerProps = null; /** * Special LoggingLevel for just this instance. * May be set from "ConsoleLogger.loggingLevel" property; * defaults to 100 which should be larger than any other * loggingLevels in use currently. * Note that different levels here will even restrict output * from control messages like testCaseInit; see individual * javadocs for what controls what. This may affect the level * of indenting you see as well; I traded off a little speed * (don't calc indent if not using that message) for prettiness. */ protected int consoleLoggingLevel = 100; //----------------------------------------------------- //-------- Control and utility routines -------- //----------------------------------------------------- /** Simple constructor, does not perform initialization. */ public ConsoleLogger() { /* no-op */ } /** * Constructor calls initialize(p). * @param p Properties block to initialize us with. */ public ConsoleLogger(Properties p) { ready = initialize(p); } /** * Return a description of what this Logger does. * @return "reports results to System.out". */ public String getDescription() { return ("org.apache.qetest.ConsoleLogger - reports results to System.out."); } /** * Returns information about the Property name=value pairs that * are understood by this Logger/Reporter. * @return same as {@link java.applet.Applet.getParameterInfo}. */ public String[][] getParameterInfo() { String pinfo[][] = { { OPT_INDENT, "boolean", "If reporter should indent sub-results" }, { "ConsoleLogger.loggingLevel", "String", "loggingLevel for just ConsoleLogger; only if more restrictive than other loggingLevels" } }; return pinfo; } /** * Accessor methods for our properties block. * * NEEDSDOC ($objectName$) @return */ public Properties getProperties() { return loggerProps; } /** * Accessor methods for our properties block. * @param p Properties to set (is cloned). */ public void setProperties(Properties p) { if (p != null) { loggerProps = (Properties) p.clone(); } } /** * Call once to initialize this Logger/Reporter from Properties. * @param Properties block to initialize from. * @param status, true if OK, false if an error occoured. * * @param p Properties block to initialize from * @return true if OK; currently always returns true */ public boolean initialize(Properties p) { setProperties(p); String i = loggerProps.getProperty(OPT_INDENT); if (i != null) { if (i.toLowerCase().equals("no") || i.toLowerCase().equals("false")) indent = false; else if (i.toLowerCase().equals("yes") || i.toLowerCase().equals("true")) indent = true; } // Grab our specific loggingLevel and set if needed String logLvl = loggerProps.getProperty("ConsoleLogger.loggingLevel"); if (logLvl != null) { // Note: if present, we'll attempt to set it // It doesn't really make much sense to set it if // this value is larger than an enclosing Reporter's // loggingLevel, but it won't hurt either try { consoleLoggingLevel = Integer.parseInt(logLvl); } catch (NumberFormatException numEx) { /* no-op */ } } ready = true; return true; } /** * Is this Logger/Reporter ready to log results? * @return status - true if it's ready to report, false otherwise */ public boolean isReady() { return ready; } /** * Flush this Logger/Reporter - no-op for ConsoleLogger. */ public void flush() { /* no-op */ } /** * Close this Logger/Reporter - essentially no-op for ConsoleLogger. */ public void close() { flush(); ready = false; } /** Simplistic indenting - two spaces. */ protected void indent() { if (indent) sIndent.append(" "); } /** Simplistic outdenting - two spaces. */ protected void outdent() { if ((indent) && (sIndent.length() >= 2)) sIndent.setLength(sIndent.length() - 2); } //----------------------------------------------------- //-------- Testfile / Testcase start and stop routines -------- //----------------------------------------------------- /** * Report that a testfile has started. * Output only when ConsoleLogger.loggingLevel >= ERRORMSG * * @param name file name or tag specifying the test. * @param comment comment about the test. */ public void testFileInit(String name, String comment) { if (consoleLoggingLevel < ERRORMSG) return; outStream.println(sIndent + "TestFileInit " + name + ":" + comment); indent(); } /** * Report that a testfile has finished, and report it's result. * Output only when ConsoleLogger.loggingLevel >= ERRORMSG * * @param msg message or name of test to log out * @param result result of testfile */ public void testFileClose(String msg, String result) { if (consoleLoggingLevel < ERRORMSG) return; outdent(); outStream.println(sIndent + "TestFileClose(" + result + ") " + msg); } /** * Report that a testcase has started. * Output only when ConsoleLogger.loggingLevel >= WARNINGMSG * * @param comment short description of this test case's objective. */ public void testCaseInit(String comment) { if (consoleLoggingLevel < WARNINGMSG) return; outStream.println(sIndent + "TestCaseInit " + comment); indent(); } /** * Report that a testcase has finished, and report it's result. * Output only when ConsoleLogger.loggingLevel >= WARNINGMSG * * @param msg message of name of test case to log out * @param result result of testfile */ public void testCaseClose(String msg, String result) { if (consoleLoggingLevel < WARNINGMSG) return; outdent(); outStream.println(sIndent + "TestCaseClose(" + result + ") " + msg); } //----------------------------------------------------- //-------- Test results logging routines -------- //----------------------------------------------------- /** * Report a comment to result file with specified severity. * Output only when ConsoleLogger.loggingLevel >= level * * @param level severity or class of message. * @param msg comment to log out. */ public void logMsg(int level, String msg) { if (consoleLoggingLevel < level) return; outStream.println(sIndent + msg); } /** * Report an arbitrary String to result file with specified severity. * Log out the String provided exactly as-is. * Output only when ConsoleLogger.loggingLevel >= level * * @param level severity or class of message. * @param msg arbitrary String to log out. */ public void logArbitrary(int level, String msg) { if (consoleLoggingLevel < level) return; outStream.println(msg); } /** * Logs out statistics to result file with specified severity. * Output only when ConsoleLogger.loggingLevel >= level * * @param level severity of message. * @param lVal statistic in long format. * @param dVal statistic in double format. * @param msg comment to log out. */ public void logStatistic(int level, long lVal, double dVal, String msg) { if (consoleLoggingLevel < level) return; outStream.println(sIndent + msg + " l: " + lVal + " d: " + dVal); } /** * Logs out Throwable.toString() and a stack trace of the * Throwable with the specified severity. * @author Shane_Curcuru@lotus.com * @param level severity of message. * @param throwable throwable/exception to log out. * @param msg description of the throwable. */ public void logThrowable(int level, Throwable throwable, String msg) { if (consoleLoggingLevel < level) return; StringWriter sWriter = new StringWriter(); sWriter.write(msg + "\n"); sWriter.write(throwable.toString() + "\n"); PrintWriter pWriter = new PrintWriter(sWriter); throwable.printStackTrace(pWriter); outStream.println(sWriter.toString()); } /** * Logs out a element to results with specified severity. * Simply indents and dumps output as string like so: *
* element * attr1=value1 * ... * msg.toString() ** Output only when ConsoleLogger.loggingLevel >= level * * @param level severity of message. * @param element name of enclosing element * @param attrs hash of name=value attributes * @param msg Object to log out; up to reporters to handle * processing of this; usually logs just .toString(). */ public void logElement(int level, String element, Hashtable attrs, Object msg) { if (consoleLoggingLevel < level) return; if ((element == null) || (attrs == null)) { // Bail if either element name or attr list is null // Note: we should really handle this case more elegantly return; } indent(); outStream.println(sIndent + element); indent(); for (Enumeration keys = attrs.keys(); keys.hasMoreElements(); /* no increment portion */ ) { Object key = keys.nextElement(); outStream.println(sIndent + key.toString() + "=" + attrs.get(key).toString()); } outdent(); if (msg != null) outStream.println(sIndent + msg.toString()); outdent(); } /** * Logs out contents of a Hashtable with specified severity. * Output only when ConsoleLogger.loggingLevel >= level * * @param level severity or class of message. * @param hash Hashtable to log the contents of. * @param msg decription of the Hashtable. */ public void logHashtable(int level, Hashtable hash, String msg) { if (consoleLoggingLevel < level) return; indent(); outStream.println(sIndent + "HASHTABLE: " + msg); indent(); if (hash == null) { outStream.println(sIndent + "hash == null, no data"); } else { try { // Fake the Properties-like output for (Enumeration keys = hash.keys(); keys.hasMoreElements(); /* no increment portion */ ) { Object key = keys.nextElement(); outStream.println(sIndent + key.toString() + "=" + hash.get(key).toString()); } } catch (Exception e) { // No-op: should ensure we have clean output } } outdent(); outdent(); } //----------------------------------------------------- //-------- Test results reporting check* routines -------- //----------------------------------------------------- /** * Writes out a Pass record with comment. * Output only when ConsoleLogger.loggingLevel > FAILSONLY * * @param comment comment to log with the pass record. */ public void checkPass(String comment) { // Note <=, since FAILSONLY is a special level if (consoleLoggingLevel <= FAILSONLY) return; outStream.println(sIndent + "PASS! " + comment); } /** * Writes out an ambiguous record with comment. * Output only when ConsoleLogger.loggingLevel > FAILSONLY * * @param comment comment to log with the ambg record. */ public void checkAmbiguous(String comment) { // Note <=, since FAILSONLY is a special level if (consoleLoggingLevel <= FAILSONLY) return; outStream.println(sIndent + "AMBG " + comment); } /** * Writes out a Fail record with comment. * Output only when ConsoleLogger.loggingLevel >= FAILSONLY * * @param comment comment to log with the fail record. */ public void checkFail(String comment) { if (consoleLoggingLevel < FAILSONLY) return; outStream.println(sIndent + "FAIL " + comment); } /** * Writes out a Error record with comment. * Output only when ConsoleLogger.loggingLevel >= ERRORMSG * * @param comment comment to log with the error record. */ public void checkErr(String comment) { if (consoleLoggingLevel < ERRORMSG) return; outStream.println(sIndent + "ERROR " + comment); } /* EXPERIMENTAL: have duplicate set of check*() methods that all output some form of ID as well as comment. Leave the non-ID taking forms for both simplicity to the end user who doesn't care about IDs as well as for backwards compatibility. */ /** * Writes out a Pass record with comment and ID. * Output only when ConsoleLogger.loggingLevel > FAILSONLY * * @param comment comment to log with the pass record. * @param ID token to log with the pass record. */ public void checkPass(String comment, String id) { // Note <=, since FAILSONLY is a special level if (consoleLoggingLevel <= FAILSONLY) return; if (id != null) outStream.println(sIndent + "PASS! (" + id + ") " + comment); else outStream.println(sIndent + "PASS! " + comment); } /** * Writes out an ambiguous record with comment and ID. * Output only when ConsoleLogger.loggingLevel > FAILSONLY * * @param comment to log with the ambg record. * @param ID token to log with the pass record. */ public void checkAmbiguous(String comment, String id) { // Note <=, since FAILSONLY is a special level if (consoleLoggingLevel <= FAILSONLY) return; if (id != null) outStream.println(sIndent + "AMBG (" + id + ") " + comment); else outStream.println(sIndent + "AMBG " + comment); } /** * Writes out a Fail record with comment and ID. * Output only when ConsoleLogger.loggingLevel >= FAILSONLY * * @param comment comment to log with the fail record. * @param ID token to log with the pass record. */ public void checkFail(String comment, String id) { if (consoleLoggingLevel < FAILSONLY) return; if (id != null) outStream.println(sIndent + "FAIL! (" + id + ") " + comment); else outStream.println(sIndent + "FAIL! " + comment); } /** * Writes out an Error record with comment and ID. * Output only when ConsoleLogger.loggingLevel >= ERRORMSG * * @param comment comment to log with the error record. * @param ID token to log with the pass record. */ public void checkErr(String comment, String id) { if (consoleLoggingLevel < ERRORMSG) return; if (id != null) outStream.println(sIndent + "ERROR (" + id + ") " + comment); else outStream.println(sIndent + "ERROR " + comment); } } // end of class ConsoleLogger