Home | History | Annotate | Download | only in log4testng
      1 package org.testng.log4testng;
      2 
      3 
      4 import java.io.ByteArrayOutputStream;
      5 import java.io.IOException;
      6 import java.io.InputStream;
      7 import java.io.PrintStream;
      8 import java.util.Iterator;
      9 import java.util.Map;
     10 import java.util.Map.Entry;
     11 import java.util.Properties;
     12 
     13 import org.testng.Assert;
     14 import org.testng.collections.Maps;
     15 
     16 /**
     17  * TestNG support logging via a custom logging framework similar to
     18  * <a href="http://logging.apache.org/log4j"> Log4j</a>. To control logging, add a
     19  * resource named "log4testng.properties" to your classpath. The logging levels are
     20  * TRACE, DEBUG, INFO, WARN, ERROR and FATAL.
     21  * The Logging framework has the following characteristics:
     22  *
     23  * <ul>
     24  * <li>All logging is done using System.out (for levels &lt; ERROR) or System.err. There
     25  * is no way to specify Appenders.</li>
     26  * <li>There is no way to control logging programmatically.</li>
     27  * <li>The log4testng.properties resource is searched in the classpath on the first
     28  * call to the logging API. If it is not present, logging defaults to the WARN
     29  * level.</li>
     30  * </ul>
     31  *
     32  * The property file contains lines in the following format:
     33  *
     34  * <pre><code>
     35  * # log4testng will log its own behavior (generally used for debugging this package only).
     36  * log4testng.debug=true
     37  *
     38  * # Specifies the root Loggers logging level. Will log DEBUG level and above
     39  * log4testng.rootLogger=DEBUG
     40  *
     41  * # The org.testng.reporters.EmailableReporter Logger will log TRACE level and above
     42  * log4testng.logger.org.testng.reporters.EmailableReporter=TRACE
     43  *
     44  * # All Logger in packages below org.testng will log WARN level and above
     45  * log4testng.logger.org.testng=WARN
     46  * </code></pre>
     47  *
     48  * In your source files you will typically instantiate and use loggers this ways:
     49  * <pre><code>
     50  * import org.testng.log4testng.Logger;
     51  *
     52  * class ThisClass {
     53  *     private static final Logger LOGGER = Logger.getLogger(ThisClass.class);
     54  *
     55  *     ...
     56  *     LOGGER.debug("entering myMethod()");
     57  *     ...
     58  *     LOGGER.warn("unknown file: " + filename);
     59  *     ...
     60  *     LOGGER.error("Unexpected error", exception);
     61  * </code></pre>
     62  */
     63 public class Logger {
     64 
     65   // Attribute an hierarchical integer value to all levels.
     66   private static int i= 0;
     67   private static final int TRACE= i++;
     68   private static final int DEBUG= i++;
     69   private static final int INFO= i++;
     70   private static final int WARN= i++;
     71   private static final int ERROR= i++;
     72   private static final int FATAL= i++;
     73   private static final int LEVEL_COUNT= i;
     74 
     75   /** Standard prefix of all property names in log4testng.properties. */
     76   private static final String PREFIX= "log4testng.";
     77 
     78   /** Standard prefix of all logger names in log4testng.properties. */
     79   private static final String LOGGER_PREFIX= PREFIX + "logger.";
     80 
     81   /** Root logger name in log4testng.properties. */
     82   private static final String ROOT_LOGGER= PREFIX + "rootLogger";
     83 
     84   /** Debug property name in log4testng.properties. */
     85   private static final String DEBUG_PROPERTY= PREFIX + "debug";
     86 
     87   /** The standard error stream (this is allways System.err except for unit tests) */
     88   private static PrintStream err= System.err;
     89 
     90   /** The standard output stream (this is allways System.out except for unit tests) */
     91   private static PrintStream out= System.out;
     92 
     93   /** An ordered list of level names. */
     94   private static final String[] levelNames= new String[LEVEL_COUNT];
     95 
     96   static {
     97     levelNames[TRACE]= "TRACE";
     98     levelNames[DEBUG]= "DEBUG";
     99     levelNames[INFO] = "INFO";
    100     levelNames[WARN] = "WARN";
    101     levelNames[ERROR] = "ERROR";
    102     levelNames[FATAL] = "FATAL";
    103   }
    104 
    105   /** A map from level name to level integer index (TRACE->0, DEBUG->1 ...) */
    106   private static final Map<String, Integer> levelMap= Maps.newHashMap();
    107 
    108   static {
    109     for(i= 0; i < LEVEL_COUNT; ++i) {
    110       levelMap.put(levelNames[i], i);
    111     }
    112   }
    113 
    114   /** true if the Logging system has been initialized. */
    115   private static boolean initialized;
    116 
    117   /** Map from Logger names to level index (as specified in log4testng.properties) */
    118   private static final Map<String, Integer> loggerLevels = Maps.newHashMap();
    119 
    120   /** Map of all known loggers. */
    121   private static final Map<Class, Logger> loggers = Maps.newHashMap();
    122 
    123   /** The logging level of the root logger (defaults to warn). */
    124   private static int rootLoggerLevel= WARN;
    125 
    126   /** Should log4testng log what it is doing (defaults to false). */
    127   private static boolean debug= false;
    128 
    129   /** The logger's level */
    130   private final int level;
    131 
    132   /** The logger's name. */
    133   private final Class klass;
    134   private final String m_className;
    135 
    136   /**
    137    * Retrieve a logger named according to the value of the pClass.getName()
    138    * parameter. If the named logger already exists, then the existing instance
    139    * will be returned. Otherwise, a new instance is created. By default, loggers
    140    * do not have a set level but inherit it from their nearest ancestor with
    141    * a set level.
    142    *
    143    * @param pClass The class' logger to retrieve.
    144    * @return a logger named according to the value of the pClass.getName().
    145    */
    146   public static synchronized Logger getLogger(Class pClass) {
    147     initialize();
    148     Logger logger= loggers.get(pClass);
    149     if(logger != null) {
    150       return logger;
    151     }
    152     int level= getLevel(pClass);
    153     logger= new Logger(pClass, level);
    154     loggers.put(pClass, logger);
    155 
    156     return logger;
    157   }
    158 
    159   /**
    160    * Check whether this logger is enabled for the TRACE Level.
    161    * @return true if this logger is enabled for level TRACE, false otherwise.
    162    */
    163   public boolean isTraceEnabled() {
    164     return isLevelEnabled(TRACE);
    165   }
    166 
    167   /**
    168    * Log a message object with the TRACE level. This method first checks if this
    169    * logger is TRACE enabled. If this logger is TRACE enabled, then it converts
    170    * the message object (passed as parameter) to a string by invoking toString().
    171    * WARNING Note that passing a Throwable to this method will print the name of
    172    * the Throwable but no stack trace. To print a stack trace use the
    173    * trace(Object, Throwable) form instead.
    174    * @param message the message object to log.
    175    */
    176   public void trace(Object message) {
    177     log(TRACE, message, null);
    178   }
    179 
    180   /**
    181    * Log a message object with the TRACE level including the stack trace of the
    182    * Throwable t passed as parameter.
    183    * See Logger.trace(Object) form for more detailed information.
    184    * @param message the message object to log.
    185    * @param t the exception to log, including its stack trace.
    186    */
    187   public void trace(Object message, Throwable t) {
    188     log(TRACE, message, t);
    189   }
    190 
    191   /**
    192    * Check whether this logger is enabled for the DEBUG Level.
    193    * @return true if this logger is enabled for level DEBUG, false otherwise.
    194    */
    195   public boolean isDebugEnabled() {
    196     return isLevelEnabled(DEBUG);
    197   }
    198 
    199   /**
    200    * Log a message object with the DEBUG level.
    201    * See Logger.trace(Object) form for more detailed information.
    202    * @param message the message object to log.
    203    */
    204   public void debug(Object message) {
    205     log(DEBUG, message, null);
    206   }
    207 
    208   /**
    209    * Log a message object with the DEBUG level including the stack trace of the
    210    * Throwable t passed as parameter.
    211    * See Logger.trace(Object, Throwable) form for more detailed information.
    212    * @param message the message object to log.
    213    * @param t the exception to log, including its stack trace.
    214    */
    215   public void debug(Object message, Throwable t) {
    216     log(DEBUG, message, t);
    217   }
    218 
    219   /**
    220    * Check whether this logger is enabled for the INFO Level.
    221    * @return true if this logger is enabled for level INFO, false otherwise.
    222    */
    223   public boolean isInfoEnabled() {
    224     return isLevelEnabled(INFO);
    225   }
    226 
    227   /**
    228    * Log a message object with the INFO level.
    229    * See Logger.trace(Object) form for more detailed information.
    230    * @param message the message object to log.
    231    */
    232   public void info(Object message) {
    233     log(INFO, message, null);
    234   }
    235 
    236   /**
    237    * Log a message object with the WARN level including the stack trace of the
    238    * Throwable t passed as parameter.
    239    * See Logger.trace(Object, Throwable) form for more detailed information.
    240    * @param message the message object to log.
    241    * @param t the exception to log, including its stack trace.
    242    */
    243   public void info(Object message, Throwable t) {
    244     log(INFO, message, t);
    245   }
    246 
    247   /**
    248    * Log a message object with the WARN level.
    249    * See Logger.trace(Object) form for more detailed information.
    250    * @param message the message object to log.
    251    */
    252   public void warn(Object message) {
    253     log(WARN, message, null);
    254   }
    255 
    256   /**
    257    * Log a message object with the ERROR level including the stack trace of the
    258    * Throwable t passed as parameter.
    259    * See Logger.trace(Object, Throwable) form for more detailed information.
    260    * @param message the message object to log.
    261    * @param t the exception to log, including its stack trace.
    262    */
    263   public void warn(Object message, Throwable t) {
    264     log(WARN, message, t);
    265   }
    266 
    267   /**
    268    * Log a message object with the ERROR level.
    269    * See Logger.trace(Object) form for more detailed information.
    270    * @param message the message object to log.
    271    */
    272   public void error(Object message) {
    273     log(ERROR, message, null);
    274   }
    275 
    276   /**
    277    * Log a message object with the DEBUG level including the stack trace of the
    278    * Throwable t passed as parameter.
    279    * See Logger.trace(Object, Throwable) form for more detailed information.
    280    * @param message the message object to log.
    281    * @param t the exception to log, including its stack trace.
    282    */
    283   public void error(Object message, Throwable t) {
    284     log(ERROR, message, t);
    285   }
    286 
    287   /**
    288    * Log a message object with the FATAL level.
    289    * See Logger.trace(Object) form for more detailed information.
    290    * @param message the message object to log.
    291    */
    292   public void fatal(Object message) {
    293     log(FATAL, message, null);
    294   }
    295 
    296   /**
    297    * Log a message object with the FATAL level including the stack trace of the
    298    * Throwable t passed as parameter.
    299    * See Logger.trace(Object, Throwable) form for more detailed information.
    300    * @param message the message object to log.
    301    * @param t the exception to log, including its stack trace.
    302    */
    303   public void fatal(Object message, Throwable t) {
    304     log(FATAL, message, t);
    305   }
    306 
    307   private Logger(Class pClass, int pLevel) {
    308     level= pLevel;
    309     klass= pClass;
    310     m_className= pClass.getName().substring(pClass.getName().lastIndexOf('.') + 1);
    311   }
    312 
    313   private static synchronized void initialize() {
    314     if(initialized) {
    315       return;
    316     }
    317 
    318     // We flag as initialized right away because if anything goes wrong
    319     // We still consider it initialized. TODO Is this OK?
    320     initialized= true;
    321 
    322     InputStream is= Thread.currentThread().getContextClassLoader().getResourceAsStream("log4testng.properties");
    323     if(is == null) {
    324       return;
    325     }
    326     Properties properties= new Properties();
    327     try {
    328       properties.load(is);
    329     }
    330     catch(IOException e) {
    331       throw new RuntimeException(e);
    332     }
    333 
    334     checkProperties(properties);
    335   }
    336 
    337   private static void checkProperties(Properties pProperties) {
    338     {
    339       // See if we want to debug log4testng
    340       String debugStr= pProperties.getProperty(DEBUG_PROPERTY);
    341       if(debugStr != null) {
    342         if(debugStr.equalsIgnoreCase("true")) {
    343           debug= true;
    344         }
    345         else if(debugStr.equalsIgnoreCase("false")) {
    346           debug= false;
    347         }
    348         else {
    349           throw new IllegalArgumentException("Unknown " + DEBUG_PROPERTY
    350                                              + " value " + debugStr);
    351         }
    352       }
    353       loglog4testng("log4testng.debug set to " + debug);
    354     }
    355 
    356     {
    357       // Set the value of the root logger (if any).
    358       String rootLevelStr= pProperties.getProperty(ROOT_LOGGER);
    359       if(rootLevelStr != null) {
    360         Integer ilevel= levelMap.get(rootLevelStr.toUpperCase());
    361         if(ilevel == null) {
    362           throw new IllegalArgumentException("Unknown level for log4testng.rootLogger "
    363                                              + rootLevelStr + " in log4testng.properties");
    364         }
    365         rootLoggerLevel= ilevel;
    366         loglog4testng("Root level logger set to " + rootLevelStr + " level.");
    367       }
    368     }
    369 
    370     Iterator it= pProperties.entrySet().iterator();
    371     while(it.hasNext()) {
    372       Map.Entry entry= (Entry) it.next();
    373       String logger= (String) entry.getKey();
    374       String level= (String) entry.getValue();
    375 
    376       if(!logger.startsWith(PREFIX)) {
    377         throw new IllegalArgumentException("Illegal property value: " + logger);
    378       }
    379       if(logger.equals(DEBUG_PROPERTY)) {
    380         // Already handled
    381       }
    382       else if(logger.equals(ROOT_LOGGER)) {
    383         // Already handled
    384       }
    385       else {
    386         if(!logger.startsWith(LOGGER_PREFIX)) {
    387           throw new IllegalArgumentException("Illegal property value: " + logger);
    388         }
    389 
    390         Integer ilevel= levelMap.get(level.toUpperCase());
    391         if(ilevel == null) {
    392           throw new IllegalArgumentException("Unknown level " + level + " for logger " + logger
    393                                              + " in log4testng.properties");
    394         }
    395 
    396         loggerLevels.put(logger.substring(LOGGER_PREFIX.length()), ilevel);
    397         loglog4testng("logger " + logger + " set to " + ilevel + " level.");
    398       }
    399     }
    400   }
    401 
    402   /**
    403    * Returns the level associated to the current class. The level is obtain by searching
    404    * for a logger in the "testng-logging.properties" resource. For example, if class is
    405    * "org.testng.TestNG" the the following loggers are searched in this order:
    406    * <ol>
    407    * <li>"org.testng.TestNG"</li>
    408    * <li>"org.testng"</li>
    409    * <li>"org"</li>
    410    * <li>The root level</li>
    411    * </ol>
    412    *
    413    * @param pClass the class name used for logger name.
    414    * @return the level associated to the current class.
    415    */
    416   private static int getLevel(Class pClass) {
    417     String name= pClass.getName();
    418     loglog4testng("Getting level for logger " + name);
    419     while(true) {
    420       Integer level= loggerLevels.get(name);
    421       if(level != null) {
    422         loglog4testng("Found level " + level + " for logger " + name);
    423 
    424         return level;
    425       }
    426       int dot= name.lastIndexOf('.');
    427       if(dot == -1) {
    428         loglog4testng("Found level " + rootLoggerLevel + " for root logger");
    429 
    430         // Logger name not found. Defaults to root logger level.
    431         return rootLoggerLevel;
    432       }
    433       name= name.substring(0, dot);
    434     }
    435   }
    436 
    437   private boolean isLevelEnabled(int pLevel) {
    438     return level <= pLevel;
    439   }
    440 
    441   private void log(int pLevel, Object pMessage, Throwable pT) {
    442     if(isLevelEnabled(pLevel)) {
    443       PrintStream ps= (pLevel >= ERROR) ? err : out;
    444       if(null != pT) {
    445         synchronized(ps) {
    446           ps.println("[" + m_className + "] [" + levelNames[pLevel] + "] " + pMessage);
    447           pT.printStackTrace(ps);
    448         }
    449       }
    450       else {
    451         ps.println("[" + m_className + "] [" + levelNames[pLevel] + "] " + pMessage);
    452       }
    453     }
    454   }
    455 
    456   /**
    457    * Logs the message to System.out of debug is on.
    458    * @param pmessage the message to log to the console
    459    */
    460   private static void loglog4testng(String pmessage) {
    461     if(debug) {
    462       out.println("[log4testng] [debug] " + pmessage);
    463     }
    464   }
    465 
    466   /**
    467    * This method is for debugging purpose only.
    468    *
    469    * @param pProperties a properties bundle initialised as log4testng
    470    * property file would be.
    471    * @param pOut the standard output stream to be used for logging.
    472    * @param pErr the standard error stream to be used for logging.
    473    */
    474   private static synchronized void testInitialize(Properties pProperties,
    475                                                   PrintStream pOut,
    476                                                   PrintStream pErr) {
    477     initialized= true;
    478     loggers.clear();
    479     rootLoggerLevel= WARN;
    480     debug= false;
    481     out= pOut;
    482     err= pErr;
    483     checkProperties(pProperties);
    484   }
    485 
    486   /**
    487    * Makes sure the default debug value is false.
    488    */
    489   private static void testDebugDefault() {
    490     Properties props= new Properties();
    491     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    492     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    493     PrintStream out2= new PrintStream(out1);
    494     PrintStream err2= new PrintStream(err1);
    495     props.put("log4testng.rootLogger", "WARN");
    496     testInitialize(props, out2, err2);
    497     Assert.assertEquals(out1.toString(), "");
    498     Assert.assertEquals(err1.toString(), "");
    499   }
    500 
    501   /**
    502    * Makes sure the debug value can be turned on and actualls logs something.
    503    */
    504   private static void testDebugOn() {
    505     Properties props= new Properties();
    506     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    507     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    508     PrintStream out2= new PrintStream(out1);
    509     PrintStream err2= new PrintStream(err1);
    510     props.put("log4testng.debug", "true");
    511     props.put("log4testng.rootLogger", "WARN");
    512     testInitialize(props, out2, err2);
    513     Assert.assertTrue(out1.toString().startsWith("[log4testng][debug]"));
    514     Assert.assertEquals(err1.toString(), "");
    515   }
    516 
    517   /**
    518    * Makes sure the debug value can be turned off and logs nothing.
    519    */
    520   private static void testDebugOff() {
    521     Properties props= new Properties();
    522     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    523     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    524     PrintStream out2= new PrintStream(out1);
    525     PrintStream err2= new PrintStream(err1);
    526     props.put("log4testng.debug", "false");
    527     props.put("log4testng.rootLogger", "WARN");
    528     testInitialize(props, out2, err2);
    529     Assert.assertEquals(out1.toString(), "");
    530     Assert.assertEquals(err1.toString(), "");
    531   }
    532 
    533   /**
    534    * Makes sure an illegal debug value throws an exception.
    535    */
    536   private static void testDebugError() {
    537     Properties props= new Properties();
    538     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    539     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    540     PrintStream out2= new PrintStream(out1);
    541     PrintStream err2= new PrintStream(err1);
    542     props.put("log4testng.debug", "unknown");
    543     props.put("log4testng.rootLogger", "WARN");
    544     try {
    545       testInitialize(props, out2, err2);
    546       throw new RuntimeException("failure");
    547     }
    548     catch(IllegalArgumentException pEx) {
    549 
    550       // Normal case
    551       Assert.assertEquals(out1.toString(), "");
    552       Assert.assertEquals(err1.toString(), "");
    553     }
    554   }
    555 
    556   /**
    557    * Tests that the root logger's default level is WARN and that loggers do not
    558    * log bellow this level and do log in the correct stream for levels equal to
    559    * and above WARN.
    560    */
    561   private static void testRootLoggerDefault() {
    562     Properties props= new Properties();
    563     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    564     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    565     PrintStream out2= new PrintStream(out1);
    566     PrintStream err2= new PrintStream(err1);
    567     testInitialize(props, out2, err2);
    568 
    569     Logger strLogger= Logger.getLogger(String.class);
    570     strLogger.trace("trace should not appear");
    571     Assert.assertEquals(out1.toString(), "");
    572     Assert.assertEquals(err1.toString(), "");
    573     strLogger.debug("debug should not appear");
    574     Assert.assertEquals(out1.toString(), "");
    575     Assert.assertEquals(err1.toString(), "");
    576     strLogger.info("info should not appear");
    577     Assert.assertEquals(out1.toString(), "");
    578     Assert.assertEquals(err1.toString(), "");
    579     strLogger.warn("warn should appear");
    580     int outlength= out1.toString().length();
    581     Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [WARN] warn should appear"));
    582     Assert.assertEquals(err1.toString(), "");
    583     strLogger.error("error should appear");
    584     Assert.assertEquals(out1.toString().length(), outlength);
    585     Assert.assertTrue(err1.toString().startsWith("[java.lang.String] [ERROR] error should appear"));
    586     strLogger.fatal("fatal should appear");
    587     Assert.assertEquals(out1.toString().length(), outlength);
    588     Assert.assertTrue(err1.toString().contains("[java.lang.String] [FATAL] fatal should appear"));
    589   }
    590 
    591   /**
    592    * Test setting the root logger level
    593    */
    594   private static void testRootLoggerSet() {
    595     Properties props= new Properties();
    596     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    597     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    598     PrintStream out2= new PrintStream(out1);
    599     PrintStream err2= new PrintStream(err1);
    600     props.put("log4testng.rootLogger", "DEBUG");
    601     testInitialize(props, out2, err2);
    602 
    603     Logger strLogger= Logger.getLogger(String.class);
    604     strLogger.trace("trace should appear");
    605     Assert.assertEquals(out1.toString(), "");
    606     Assert.assertEquals(err1.toString(), "");
    607     strLogger.debug("debug should appear");
    608     Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear"));
    609     Assert.assertEquals(err1.toString(), "");
    610   }
    611 
    612   /**
    613    * Test setting the root logger to an illegal level value throws an exception.
    614    */
    615   private static void testRootLoggerSetError() {
    616     Properties props= new Properties();
    617     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    618     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    619     PrintStream out2= new PrintStream(out1);
    620     PrintStream err2= new PrintStream(err1);
    621     props.put("log4testng.rootLogger", "unknown");
    622     try {
    623       testInitialize(props, out2, err2);
    624       throw new RuntimeException("failure");
    625     }
    626     catch(IllegalArgumentException pEx) {
    627 
    628       // Normal case
    629       Assert.assertEquals(out1.toString(), "");
    630       Assert.assertEquals(err1.toString(), "");
    631     }
    632   }
    633 
    634   /**
    635    * Test setting a user logger level
    636    */
    637   private static void testUserLoggerSet() {
    638     Properties props= new Properties();
    639     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    640     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    641     PrintStream out2= new PrintStream(out1);
    642     PrintStream err2= new PrintStream(err1);
    643     props.put("log4testng.logger.java.lang.String", "DEBUG");
    644     testInitialize(props, out2, err2);
    645 
    646     Logger strLogger= Logger.getLogger(String.class);
    647     strLogger.trace("trace should not appear");
    648     Assert.assertEquals(out1.toString(), "");
    649     Assert.assertEquals(err1.toString(), "");
    650     strLogger.debug("debug should appear");
    651     int outLength= out1.toString().length();
    652     Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear"));
    653     Assert.assertEquals(err1.toString(), "");
    654 
    655     Logger classLogger= Logger.getLogger(Class.class);
    656     classLogger.debug("debug should not appear");
    657     Assert.assertEquals(out1.toString().length(), outLength);
    658     Assert.assertEquals(err1.toString(), "");
    659     classLogger.warn("warn should appear");
    660     Assert.assertTrue(out1.toString().contains("[java.lang.Class] [WARN] warn should appear"));
    661     Assert.assertEquals(err1.toString(), "");
    662   }
    663 
    664   /**
    665    * Test setting a user logger to an illegal level value throws an exception
    666    */
    667   private static void testUserLoggerSetError() {
    668     Properties props= new Properties();
    669     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    670     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    671     PrintStream out2= new PrintStream(out1);
    672     PrintStream err2= new PrintStream(err1);
    673     props.put("log4testng.logger.java.lang.String", "unknown");
    674     try {
    675       testInitialize(props, out2, err2);
    676       throw new RuntimeException("failure");
    677     }
    678     catch(IllegalArgumentException pEx) {
    679 
    680       // Normal case
    681       Assert.assertEquals(out1.toString(), "");
    682       Assert.assertEquals(err1.toString(), "");
    683     }
    684   }
    685 
    686   /**
    687    * Tests setting a partial logger name (a hierarchy scope)
    688    */
    689   private static void testUserLoggerSetHierarchy() {
    690     Properties props= new Properties();
    691     ByteArrayOutputStream out1= new ByteArrayOutputStream();
    692     ByteArrayOutputStream err1= new ByteArrayOutputStream();
    693     PrintStream out2= new PrintStream(out1);
    694     PrintStream err2= new PrintStream(err1);
    695     props.put("log4testng.logger.java.lang", "DEBUG");
    696     testInitialize(props, out2, err2);
    697 
    698     Logger strLogger= Logger.getLogger(String.class);
    699     strLogger.trace("trace should not appear");
    700     Assert.assertEquals(out1.toString(), "");
    701     Assert.assertEquals(err1.toString(), "");
    702     strLogger.debug("debug should appear");
    703     Assert.assertTrue(out1.toString().startsWith("[java.lang.String] [DEBUG] debug should appear"));
    704     Assert.assertEquals(err1.toString(), "");
    705   }
    706 
    707   /**
    708    * Run all tests. (very crusty ...)
    709    * @param pArgs not used
    710    */
    711   public static void main(String[] pArgs) {
    712     testDebugDefault();
    713     testDebugOn();
    714     testDebugOff();
    715     testDebugError();
    716     testRootLoggerDefault();
    717     testRootLoggerSet();
    718     testRootLoggerSetError();
    719     testUserLoggerSet();
    720     testUserLoggerSetError();
    721     testUserLoggerSetHierarchy();
    722   }
    723 }
    724