All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.testng.log4testng.Logger Maven / Gradle / Ivy

There is a newer version: 7.10.2
Show newest version
package org.testng.log4testng;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.testng.Assert;
import org.testng.collections.Maps;

/**
 * TestNG support logging via a custom logging framework similar to
 *  Log4j. To control logging, add a
 * resource named "log4testng.properties" to your classpath. The logging levels are
 * TRACE, DEBUG, INFO, WARN, ERROR and FATAL.
 * The Logging framework has the following characteristics:
 *
 * 
    *
  • All logging is done using System.out (for levels < ERROR) or System.err. There * is no way to specify Appenders.
  • *
  • There is no way to control logging programmatically.
  • *
  • The log4testng.properties resource is searched in the classpath on the first * call to the logging API. If it is not present, logging defaults to the WARN * level.
  • *
* * The property file contains lines in the following format: * *

 * # log4testng will log its own behavior (generally used for debugging this package only).
 * log4testng.debug=true
 *
 * # Specifies the root Loggers logging level. Will log DEBUG level and above
 * log4testng.rootLogger=DEBUG
 *
 * # The org.testng.reporters.EmailableReporter Logger will log TRACE level and above
 * log4testng.logger.org.testng.reporters.EmailableReporter=TRACE
 *
 * # All Logger in packages below org.testng will log WARN level and above
 * log4testng.logger.org.testng=WARN
 * 
* * In your source files you will typically instantiate and use loggers this ways: *

 * import org.testng.log4testng.Logger;
 *
 * class ThisClass {
 *     private static final Logger LOGGER = Logger.getLogger(ThisClass.class);
 *
 *     ...
 *     LOGGER.debug("entering myMethod()");
 *     ...
 *     LOGGER.warn("unknown file: " + filename);
 *     ...
 *     LOGGER.error("Unexpected error", exception);
 * 
*/ public class Logger { // Attribute an hierarchical integer value to all levels. private static int i= 0; private static final int TRACE= i++; private static final int DEBUG= i++; private static final int INFO= i++; private static final int WARN= i++; private static final int ERROR= i++; private static final int FATAL= i++; private static final int LEVEL_COUNT= i; /** Standard prefix of all property names in log4testng.properties. */ private static final String PREFIX= "log4testng."; /** Standard prefix of all logger names in log4testng.properties. */ private static final String LOGGER_PREFIX= PREFIX + "logger."; /** Root logger name in log4testng.properties. */ private static final String ROOT_LOGGER= PREFIX + "rootLogger"; /** Debug property name in log4testng.properties. */ private static final String DEBUG_PROPERTY= PREFIX + "debug"; /** The standard error stream (this is allways System.err except for unit tests) */ private static PrintStream err= System.err; /** The standard output stream (this is allways System.out except for unit tests) */ private static PrintStream out= System.out; /** An ordered list of level names. */ private static final String[] levelNames= new String[LEVEL_COUNT]; static { levelNames[TRACE]= "TRACE"; levelNames[DEBUG]= "DEBUG"; levelNames[INFO] = "INFO"; levelNames[WARN] = "WARN"; levelNames[ERROR] = "ERROR"; levelNames[FATAL] = "FATAL"; } /** A map from level name to level integer index (TRACE->0, DEBUG->1 ...) */ private static final Map levelMap= Maps.newHashMap(); static { for(i= 0; i < LEVEL_COUNT; ++i) { levelMap.put(levelNames[i], Integer.valueOf(i)); } } /** true if the Logging system has been initialized. */ private static boolean initialized; /** Map from Logger names to level index (as specified in log4testng.properties) */ private static final Map loggerLevels = Maps.newHashMap(); /** Map of all known loggers. */ private static final Map loggers = Maps.newHashMap(); /** The logging level of the root logger (defaults to warn). */ private static int rootLoggerLevel= WARN; /** Should log4testng log what it is doing (defaults to false). */ private static boolean debug= false; /** The logger's level */ private final int level; /** The logger's name. */ private final Class klass; private final String m_className; /** * Retrieve a logger named according to the value of the pClass.getName() * parameter. If the named logger already exists, then the existing instance * will be returned. Otherwise, a new instance is created. By default, loggers * do not have a set level but inherit it from their nearest ancestor with * a set level. * * @param pClass The class' logger to retrieve. * @return a logger named according to the value of the pClass.getName(). */ public static synchronized Logger getLogger(Class pClass) { initialize(); Logger logger= loggers.get(pClass); if(logger != null) { return logger; } int level= getLevel(pClass); logger= new Logger(pClass, level); loggers.put(pClass, logger); return logger; } /** * Check whether this logger is enabled for the TRACE Level. * @return true if this logger is enabled for level TRACE, false otherwise. */ public boolean isTraceEnabled() { return isLevelEnabled(TRACE); } /** * Log a message object with the TRACE level. This method first checks if this * logger is TRACE enabled. If this logger is TRACE enabled, then it converts * the message object (passed as parameter) to a string by invoking toString(). * WARNING Note that passing a Throwable to this method will print the name of * the Throwable but no stack trace. To print a stack trace use the * trace(Object, Throwable) form instead. * @param message the message object to log. */ public void trace(Object message) { log(TRACE, message, null); } /** * Log a message object with the TRACE level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void trace(Object message, Throwable t) { log(TRACE, message, t); } /** * Check whether this logger is enabled for the DEBUG Level. * @return true if this logger is enabled for level DEBUG, false otherwise. */ public boolean isDebugEnabled() { return isLevelEnabled(DEBUG); } /** * Log a message object with the DEBUG level. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. */ public void debug(Object message) { log(DEBUG, message, null); } /** * Log a message object with the DEBUG level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object, Throwable) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void debug(Object message, Throwable t) { log(DEBUG, message, t); } /** * Check whether this logger is enabled for the INFO Level. * @return true if this logger is enabled for level INFO, false otherwise. */ public boolean isInfoEnabled() { return isLevelEnabled(INFO); } /** * Log a message object with the INFO level. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. */ public void info(Object message) { log(INFO, message, null); } /** * Log a message object with the WARN level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object, Throwable) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void info(Object message, Throwable t) { log(INFO, message, t); } /** * Log a message object with the WARN level. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. */ public void warn(Object message) { log(WARN, message, null); } /** * Log a message object with the ERROR level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object, Throwable) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void warn(Object message, Throwable t) { log(WARN, message, t); } /** * Log a message object with the ERROR level. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. */ public void error(Object message) { log(ERROR, message, null); } /** * Log a message object with the DEBUG level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object, Throwable) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void error(Object message, Throwable t) { log(ERROR, message, t); } /** * Log a message object with the FATAL level. * See Logger.trace(Object) form for more detailed information. * @param message the message object to log. */ public void fatal(Object message) { log(FATAL, message, null); } /** * Log a message object with the FATAL level including the stack trace of the * Throwable t passed as parameter. * See Logger.trace(Object, Throwable) form for more detailed information. * @param message the message object to log. * @param t the exception to log, including its stack trace. */ public void fatal(Object message, Throwable t) { log(FATAL, message, t); } private Logger(Class pClass, int pLevel) { level= pLevel; klass= pClass; m_className= pClass.getName().substring(pClass.getName().lastIndexOf('.') + 1); } private static synchronized void initialize() { if(initialized) { return; } // We flag as initialized right away because if anything goes wrong // We still consider it initialized. TODO Is this OK? initialized= true; InputStream is= Thread.currentThread().getContextClassLoader().getResourceAsStream("log4testng.properties"); if(is == null) { return; } Properties properties= new Properties(); try { properties.load(is); } catch(IOException e) { throw new RuntimeException(e); } checkProperties(properties); } private static void checkProperties(Properties pProperties) { { // See if we want to debug log4testng String debugStr= pProperties.getProperty(DEBUG_PROPERTY); if(debugStr != null) { if(debugStr.equalsIgnoreCase("true")) { debug= true; } else if(debugStr.equalsIgnoreCase("false")) { debug= false; } else { throw new IllegalArgumentException("Unknown " + DEBUG_PROPERTY + " value " + debugStr); } } loglog4testng("log4testng.debug set to " + debug); } { // Set the value of the root logger (if any). String rootLevelStr= pProperties.getProperty(ROOT_LOGGER); if(rootLevelStr != null) { Integer ilevel= levelMap.get(rootLevelStr.toUpperCase()); if(ilevel == null) { throw new IllegalArgumentException("Unknown level for log4testng.rootLogger " + rootLevelStr + " in log4testng.properties"); } rootLoggerLevel= ilevel.intValue(); loglog4testng("Root level logger set to " + rootLevelStr + " level."); } } Iterator it= pProperties.entrySet().iterator(); while(it.hasNext()) { Map.Entry entry= (Entry) it.next(); String logger= (String) entry.getKey(); String level= (String) entry.getValue(); if(!logger.startsWith(PREFIX)) { throw new IllegalArgumentException("Illegal property value: " + logger); } if(logger.equals(DEBUG_PROPERTY)) { // Already handled } else if(logger.equals(ROOT_LOGGER)) { // Already handled } else { if(!logger.startsWith(LOGGER_PREFIX)) { throw new IllegalArgumentException("Illegal property value: " + logger); } Integer ilevel= levelMap.get(level.toUpperCase()); if(ilevel == null) { throw new IllegalArgumentException("Unknown level " + level + " for logger " + logger + " in log4testng.properties"); } loggerLevels.put(logger.substring(LOGGER_PREFIX.length()), ilevel); loglog4testng("logger " + logger + " set to " + ilevel + " level."); } } } /** * Returns the level associated to the current class. The level is obtain by searching * for a logger in the "testng-logging.properties" resource. For example, if class is * "org.testng.TestNG" the the following loggers are searched in this order: *
    *
  1. "org.testng.TestNG"
  2. *
  3. "org.testng"
  4. *
  5. "org"
  6. *
  7. The root level
  8. *
* * @param pClass the class name used for logger name. * @return the level associated to the current class. */ private static int getLevel(Class pClass) { String name= pClass.getName(); loglog4testng("Getting level for logger " + name); while(true) { Integer level= loggerLevels.get(name); if(level != null) { loglog4testng("Found level " + level + " for logger " + name); return level.intValue(); } int dot= name.lastIndexOf('.'); if(dot == -1) { loglog4testng("Found level " + rootLoggerLevel + " for root logger"); // Logger name not found. Defaults to root logger level. return rootLoggerLevel; } name= name.substring(0, dot); } } private boolean isLevelEnabled(int pLevel) { return level <= pLevel; } private void log(int pLevel, Object pMessage, Throwable pT) { if(isLevelEnabled(pLevel)) { PrintStream ps= (pLevel >= ERROR) ? err : out; if(null != pT) { synchronized(ps) { ps.println("[" + m_className + "] [" + levelNames[pLevel] + "] " + pMessage); pT.printStackTrace(ps); } } else { ps.println("[" + m_className + "] [" + levelNames[pLevel] + "] " + pMessage); } } } /** * Logs the message to System.out of debug is on. * @param pmessage the message to log to the console */ private static void loglog4testng(String pmessage) { if(debug) { out.println("[log4testng] [debug] " + pmessage); } } /** * This method is for debugging purpose only. * * @param pProperties a properties bundle initialised as log4testng * property file would be. * @param pOut the standard output stream to be used for logging. * @param pErr the standard error stream to be used for logging. */ private static synchronized void testInitialize(Properties pProperties, PrintStream pOut, PrintStream pErr) { initialized= true; loggers.clear(); rootLoggerLevel= WARN; debug= false; out= pOut; err= pErr; checkProperties(pProperties); } /** * Makes sure the default debug value is false. */ private static void testDebugDefault() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.rootLogger", "WARN"); testInitialize(props, out2, err2); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); } /** * Makes sure the debug value can be turned on and actualls logs something. */ private static void testDebugOn() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.debug", "true"); props.put("log4testng.rootLogger", "WARN"); testInitialize(props, out2, err2); Assert.assertTrue(out1.toString().startsWith("[log4testng][debug]")); Assert.assertEquals(err1.toString(), ""); } /** * Makes sure the debug value can be turned off and logs nothing. */ private static void testDebugOff() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.debug", "false"); props.put("log4testng.rootLogger", "WARN"); testInitialize(props, out2, err2); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); } /** * Makes sure an illegal debug value throws an exception. */ private static void testDebugError() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.debug", "unknown"); props.put("log4testng.rootLogger", "WARN"); try { testInitialize(props, out2, err2); throw new RuntimeException("failure"); } catch(IllegalArgumentException pEx) { // Normal case Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); } } /** * Tests that the root logger's default level is WARN and that loggers do not * log bellow this level and do log in the correct stream for levels equal to * and above WARN. */ private static void testRootLoggerDefault() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); testInitialize(props, out2, err2); Logger strLogger= Logger.getLogger(String.class); strLogger.trace("trace should not appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.debug("debug should not appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.info("info should not appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.warn("warn should appear"); int outlength= out1.toString().length(); Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [WARN] warn should appear")); Assert.assertEquals(err1.toString(), ""); strLogger.error("error should appear"); Assert.assertEquals(out1.toString().length(), outlength); Assert.assertTrue(err1.toString().startsWith("[java.lang.String] [ERROR] error should appear")); strLogger.fatal("fatal should appear"); Assert.assertEquals(out1.toString().length(), outlength); Assert.assertTrue(err1.toString().contains("[java.lang.String] [FATAL] fatal should appear")); } /** * Test setting the root logger level */ private static void testRootLoggerSet() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.rootLogger", "DEBUG"); testInitialize(props, out2, err2); Logger strLogger= Logger.getLogger(String.class); strLogger.trace("trace should appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.debug("debug should appear"); Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear")); Assert.assertEquals(err1.toString(), ""); } /** * Test setting the root logger to an illegal level value throws an exception. */ private static void testRootLoggerSetError() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.rootLogger", "unknown"); try { testInitialize(props, out2, err2); throw new RuntimeException("failure"); } catch(IllegalArgumentException pEx) { // Normal case Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); } } /** * Test setting a user logger level */ private static void testUserLoggerSet() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.logger.java.lang.String", "DEBUG"); testInitialize(props, out2, err2); Logger strLogger= Logger.getLogger(String.class); strLogger.trace("trace should not appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.debug("debug should appear"); int outLength= out1.toString().length(); Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear")); Assert.assertEquals(err1.toString(), ""); Logger classLogger= Logger.getLogger(Class.class); classLogger.debug("debug should not appear"); Assert.assertEquals(out1.toString().length(), outLength); Assert.assertEquals(err1.toString(), ""); classLogger.warn("warn should appear"); Assert.assertTrue(out1.toString().contains("[java.lang.Class] [WARN] warn should appear")); Assert.assertEquals(err1.toString(), ""); } /** * Test setting a user logger to an illegal level value throws an exception */ private static void testUserLoggerSetError() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.logger.java.lang.String", "unknown"); try { testInitialize(props, out2, err2); throw new RuntimeException("failure"); } catch(IllegalArgumentException pEx) { // Normal case Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); } } /** * Tests setting a partial logger name (a hierarchy scope) */ private static void testUserLoggerSetHierarchy() { Properties props= new Properties(); ByteArrayOutputStream out1= new ByteArrayOutputStream(); ByteArrayOutputStream err1= new ByteArrayOutputStream(); PrintStream out2= new PrintStream(out1); PrintStream err2= new PrintStream(err1); props.put("log4testng.logger.java.lang", "DEBUG"); testInitialize(props, out2, err2); Logger strLogger= Logger.getLogger(String.class); strLogger.trace("trace should not appear"); Assert.assertEquals(out1.toString(), ""); Assert.assertEquals(err1.toString(), ""); strLogger.debug("debug should appear"); Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear")); Assert.assertEquals(err1.toString(), ""); } /** * Run all tests. (very crusty ...) * @param pArgs not used */ public static void main(String[] pArgs) { testDebugDefault(); testDebugOn(); testDebugOff(); testDebugError(); testRootLoggerDefault(); testRootLoggerSet(); testRootLoggerSetError(); testUserLoggerSet(); testUserLoggerSetError(); testUserLoggerSetHierarchy(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy