# HG changeset patch # User dfuchs # Date 1378998099 -7200 # Node ID e481217d82f22f70e4b1ce0d00621adca2aec1fb # Parent ee72cd8a1085e62181e05b4ae1c61de672547031 8024525: Make Logger log methods call isLoggable() Summary: This changeset makes the various Logger logging method call isLoggable() instead of inlining the level checks. Reviewed-by: mchung, alanb diff -r ee72cd8a1085 -r e481217d82f2 jdk/src/share/classes/java/util/logging/Logger.java --- a/jdk/src/share/classes/java/util/logging/Logger.java Thu Sep 12 01:47:05 2013 -0700 +++ b/jdk/src/share/classes/java/util/logging/Logger.java Thu Sep 12 17:01:39 2013 +0200 @@ -635,7 +635,7 @@ * @param record the LogRecord to be published */ public void log(LogRecord record) { - if (record.getLevel().intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(record.getLevel())) { return; } Filter theFilter = filter; @@ -689,7 +689,7 @@ * @param msg The string message (or a key in the message catalog) */ public void log(Level level, String msg) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -710,7 +710,7 @@ * desired log message */ public void log(Level level, Supplier msgSupplier) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msgSupplier.get()); @@ -729,7 +729,7 @@ * @param param1 parameter to the message */ public void log(Level level, String msg, Object param1) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -750,7 +750,7 @@ * @param params array of parameters to the message */ public void log(Level level, String msg, Object params[]) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -775,7 +775,7 @@ * @param thrown Throwable associated with log message. */ public void log(Level level, String msg, Throwable thrown) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -803,7 +803,7 @@ * @since 1.8 */ public void log(Level level, Throwable thrown, Supplier msgSupplier) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msgSupplier.get()); @@ -829,7 +829,7 @@ * @param msg The string message (or a key in the message catalog) */ public void logp(Level level, String sourceClass, String sourceMethod, String msg) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -856,7 +856,7 @@ */ public void logp(Level level, String sourceClass, String sourceMethod, Supplier msgSupplier) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msgSupplier.get()); @@ -881,7 +881,7 @@ */ public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object param1) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -908,7 +908,7 @@ */ public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object params[]) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -939,7 +939,7 @@ */ public void logp(Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -973,7 +973,7 @@ */ public void logp(Level level, String sourceClass, String sourceMethod, Throwable thrown, Supplier msgSupplier) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msgSupplier.get()); @@ -1021,7 +1021,7 @@ */ public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -1052,7 +1052,7 @@ */ public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Object param1) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -1085,7 +1085,7 @@ */ public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Object params[]) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -1122,7 +1122,7 @@ */ public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Throwable thrown) { - if (level.intValue() < levelValue || levelValue == offValue) { + if (!isLoggable(level)) { return; } LogRecord lr = new LogRecord(level, msg); @@ -1148,9 +1148,6 @@ * @param sourceMethod name of method that is being entered */ public void entering(String sourceClass, String sourceMethod) { - if (Level.FINER.intValue() < levelValue) { - return; - } logp(Level.FINER, sourceClass, sourceMethod, "ENTRY"); } @@ -1167,11 +1164,7 @@ * @param param1 parameter to the method being entered */ public void entering(String sourceClass, String sourceMethod, Object param1) { - if (Level.FINER.intValue() < levelValue) { - return; - } - Object params[] = { param1 }; - logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", params); + logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param1); } /** @@ -1188,14 +1181,12 @@ * @param params array of parameters to the method being entered */ public void entering(String sourceClass, String sourceMethod, Object params[]) { - if (Level.FINER.intValue() < levelValue) { - return; - } String msg = "ENTRY"; if (params == null ) { logp(Level.FINER, sourceClass, sourceMethod, msg); return; } + if (!isLoggable(Level.FINER)) return; for (int i = 0; i < params.length; i++) { msg = msg + " {" + i + "}"; } @@ -1213,9 +1204,6 @@ * @param sourceMethod name of the method */ public void exiting(String sourceClass, String sourceMethod) { - if (Level.FINER.intValue() < levelValue) { - return; - } logp(Level.FINER, sourceClass, sourceMethod, "RETURN"); } @@ -1233,10 +1221,6 @@ * @param result Object that is being returned */ public void exiting(String sourceClass, String sourceMethod, Object result) { - if (Level.FINER.intValue() < levelValue) { - return; - } - Object params[] = { result }; logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result); } @@ -1262,7 +1246,7 @@ * @param thrown The Throwable that is being thrown. */ public void throwing(String sourceClass, String sourceMethod, Throwable thrown) { - if (Level.FINER.intValue() < levelValue || levelValue == offValue ) { + if (!isLoggable(Level.FINER)) { return; } LogRecord lr = new LogRecord(Level.FINER, "THROW"); @@ -1286,9 +1270,6 @@ * @param msg The string message (or a key in the message catalog) */ public void severe(String msg) { - if (Level.SEVERE.intValue() < levelValue) { - return; - } log(Level.SEVERE, msg); } @@ -1302,9 +1283,6 @@ * @param msg The string message (or a key in the message catalog) */ public void warning(String msg) { - if (Level.WARNING.intValue() < levelValue) { - return; - } log(Level.WARNING, msg); } @@ -1318,9 +1296,6 @@ * @param msg The string message (or a key in the message catalog) */ public void info(String msg) { - if (Level.INFO.intValue() < levelValue) { - return; - } log(Level.INFO, msg); } @@ -1334,9 +1309,6 @@ * @param msg The string message (or a key in the message catalog) */ public void config(String msg) { - if (Level.CONFIG.intValue() < levelValue) { - return; - } log(Level.CONFIG, msg); } @@ -1350,9 +1322,6 @@ * @param msg The string message (or a key in the message catalog) */ public void fine(String msg) { - if (Level.FINE.intValue() < levelValue) { - return; - } log(Level.FINE, msg); } @@ -1366,9 +1335,6 @@ * @param msg The string message (or a key in the message catalog) */ public void finer(String msg) { - if (Level.FINER.intValue() < levelValue) { - return; - } log(Level.FINER, msg); } @@ -1382,9 +1348,6 @@ * @param msg The string message (or a key in the message catalog) */ public void finest(String msg) { - if (Level.FINEST.intValue() < levelValue) { - return; - } log(Level.FINEST, msg); } diff -r ee72cd8a1085 -r e481217d82f2 jdk/test/java/util/logging/Logger/isLoggable/TestIsLoggable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/Logger/isLoggable/TestIsLoggable.java Thu Sep 12 17:01:39 2013 +0200 @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * @test + * @bug 8024525 + * @summary checks that isLoggable() can be overridden to control logging. + * @author danielfuchs + * @run main/othervm TestIsLoggable + */ +public class TestIsLoggable { + + // This logger can be configured to override its default level + // for a particular set of thread ids + public static final class ThreadLogger extends Logger { + + final Map threadMap = + Collections.synchronizedMap(new HashMap()); + + public ThreadLogger(String name) { + super(name, null); + } + + @Override + public boolean isLoggable(Level level) { + final Level threadLevel = threadMap.get(Thread.currentThread().getId()); + if (threadLevel == null) return super.isLoggable(level); + final int levelValue = threadLevel.intValue(); + final int offValue = Level.OFF.intValue(); + if (level.intValue() < levelValue || levelValue == offValue) { + return false; + } + return true; + } + + } + + public static final class TestHandler extends Handler { + + final List messages = new CopyOnWriteArrayList<>(); + + @Override + public void publish(LogRecord record) { + messages.add(record.getMessage()); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + messages.clear(); + } + + } + + // Sorted list of standard levels + static final List LEVELS = Collections.unmodifiableList( + java.util.Arrays.asList(new Level[] { + Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG, + Level.FINE, Level.FINER, Level.FINEST + })); + + // Test cases: + // LEV_ test logger.severe(msg) .. logger.finest(msg) + // LOG_ logger.log(Level.SEVERE, msg) ... logger.log(Level.FINEST, msg) + // LOG1_ logger.log(Level.SEVERE, msg, param1) ... + // LOG2_ logger.log(Level.SEVERE, msg, params[]) ... + // LOG3_ logger.log(Level.SEVERE, msg, throwable) ... + // LOGP_ logger.logp(Level.SEVERE, class, method, msg) ... + // LOGP1_ logger.logp(Level.SEVERE, class, method, msg, param1) ... + // LOGP2_ logger.logp(Level.SEVERE, class, method, msg, params[]) ... + // LOGP3_ logger.logp(Level.SEVERE, class, method, msg, throwable) ... + public static enum LogTest { + LEV_SEVERE, LEV_WARNING, LEV_INFO, LEV_CONFIG, LEV_FINE, LEV_FINER, LEV_FINEST, + LOG_SEVERE, LOG_WARNING, LOG_INFO, LOG_CONFIG, LOG_FINE, LOG_FINER, LOG_FINEST, + LOG1_SEVERE, LOG1_WARNING, LOG1_INFO, LOG1_CONFIG, LOG1_FINE, LOG1_FINER, LOG1_FINEST, + LOG2_SEVERE, LOG2_WARNING, LOG2_INFO, LOG2_CONFIG, LOG2_FINE, LOG2_FINER, LOG2_FINEST, + LOG3_SEVERE, LOG3_WARNING, LOG3_INFO, LOG3_CONFIG, LOG3_FINE, LOG3_FINER, LOG3_FINEST, + LOGP_SEVERE, LOGP_WARNING, LOGP_INFO, LOGP_CONFIG, LOGP_FINE, LOGP_FINER, LOGP_FINEST, + LOGP1_SEVERE, LOGP1_WARNING, LOGP1_INFO, LOGP1_CONFIG, LOGP1_FINE, LOGP1_FINER, LOGP1_FINEST, + LOGP2_SEVERE, LOGP2_WARNING, LOGP2_INFO, LOGP2_CONFIG, LOGP2_FINE, LOGP2_FINER, LOGP2_FINEST, + LOGP3_SEVERE, LOGP3_WARNING, LOGP3_INFO, LOGP3_CONFIG, LOGP3_FINE, LOGP3_FINER, LOGP3_FINEST; + + // call the method Logger.severe() ... Logger.finest() corresponding + // to the given level 'l' (severe() for SEVERE etc...) + public void loglevel(Level l, Logger logger, String message) { + LogTest test = LogTest.valueOf("LEV_"+l.getName()); + switch(test) { + case LEV_SEVERE: + logger.severe(message); + break; + case LEV_WARNING: + logger.warning(message); + break; + case LEV_INFO: + logger.info(message); + break; + case LEV_CONFIG: + logger.config(message); + break; + case LEV_FINE: + logger.fine(message); + break; + case LEV_FINER: + logger.finer(message); + break; + case LEV_FINEST: + logger.finest(message); + break; + } + } + + // The threshold at which the logger is expected to start logging. + // trick: we derive the threshold level from the testcase name... + public Level threshold() { + for (Level l : LEVELS ) { + if (this.toString().endsWith(l.getName())) { + return l; + } + } + return Level.OFF; + } + + // Levels for which the logger is expected to log something. + public List loggable() { + return LEVELS.subList(0, LEVELS.indexOf(threshold())+1); + } + + // Levels which will be blocked because they are weaker than the + // threshold() + public List weaker() { + return LEVELS.subList(LEVELS.indexOf(threshold())+1, LEVELS.size()); + } + + // Log a message at this testcase threshold, using this testcase method. + public void log(Logger logger, String message) { + log(threshold(), logger, message); + } + + // Log a message at the given level, using this testcase method. + public void log(Level level, Logger logger, String message) { + if (this.toString().startsWith("LOG_")) { + logger.log(level, message); + } else if (this.toString().startsWith("LOG1_")) { + logger.log(level, message, "dummy param"); + } else if (this.toString().startsWith("LOG2_")) { + logger.log(level, message, new Object[] {"dummy", "param"}); + } else if (this.toString().startsWith("LOG3_")) { + logger.log(level, message, new Exception("dummy exception")); + } else if (this.toString().startsWith("LOGP_")) { + logger.logp(level, "TestCase", "log", message); + } else if (this.toString().startsWith("LOGP1_")) { + logger.logp(level, "TestCase", "log", message, "dummy param"); + } else if (this.toString().startsWith("LOGP2_")) { + logger.logp(level, "TestCase", "log", message, + new Object[] {"dummy", "param"}); + } else if (this.toString().startsWith("LOGP3_")) { + logger.logp(level, "TestCase", "log", message, + new Exception("dummy exception")); + } else if (this.toString().startsWith("LEV_")) { + loglevel(level, logger, message); + } + } + + // String description of the logging method called. + public String method() { + if (this.toString().startsWith("LOG_")) { + return "Logger.log(Level." + threshold().getName() +", msg): "; + } else if (this.toString().startsWith("LOG1_")) { + return "Logger.log(Level." + threshold().getName() +", msg, param1): "; + } else if (this.toString().startsWith("LOG2_")) { + return "Logger.log(Level." + threshold().getName() +", msg, params[]): "; + } else if (this.toString().startsWith("LOG3_")) { + return "Logger.log(Level." + threshold().getName() +", msg, throwable): "; + } else if (this.toString().startsWith("LEV_")) { + return "Logger."+threshold().getName().toLowerCase(Locale.ROOT)+"(): "; + } else if (this.toString().startsWith("LOGP_")) { + return "Logger.logp(Level." + threshold().getName() +", msg): "; + } else if (this.toString().startsWith("LOGP1_")) { + return "Logger.logp(Level." + threshold().getName() +", msg, param1): "; + } else if (this.toString().startsWith("LOGP2_")) { + return "Logger.logp(Level." + threshold().getName() +", msg, params[]): "; + } else if (this.toString().startsWith("LOGP3_")) { + return "Logger.logp(Level." + threshold().getName() +", msg, throwable): "; + } + throw new RuntimeException("Unknown test case: "+this); + } + } + + // The purpose of this test is to verify that the various log methods in + // Logger now call Logger.isLoggable(). + // To do that - we're going to use a subclass of Logger, ThreadLogger, which + // only overrides isLoggable() - and compare the level it is given to a level + // it finds in a map indexed with the current thread id. + // We will register a TestHandler with our ThreadLogger which will store + // the messages in a messages map. This will allow us to verify whether the + // logging method we're testing has or hasn't logged. + // + // The TestCase enum above allows us to test a combination of every possible + // log method with every possible level inside a loop - with the + // exception of exiting/entering/throwing that we will be testing + // outside of that loop. + // + public static void main(String... args) { + LogManager manager = LogManager.getLogManager(); + ThreadLogger logger = new ThreadLogger("foo.bar"); + //manager.addLogger(logger); + TestHandler handler = new TestHandler(); + logger.addHandler(handler); + + //By default, logger's level is Level.INFO + final List loggable = LEVELS.subList(0, LEVELS.indexOf(Level.INFO)+1); + + // Check our test implementation of logger.isLoggable(); + // + // Since we haven't put anything in the threadMap, isLoggable() should + // return true for all levels stronger or equals to Level.INFO. + // here we're just checking that our implementation of + // ThreadLogger.isLoggable() returns what we want - we're just testing + // the test code... + for (Level level : LEVELS) { + if (logger.isLoggable(level) != loggable.contains(level)) { + throw new RuntimeException(level + + ": unexpected result for isLoggable(): expected " + + (loggable.contains(level))); + } + } + + // Test that entering/exiting/throwing call isLoggable() + + // Here we test the default behavior: this call shouldn't log anything + // because by default the logger level is Level.INFO and these + // methods log at Level.FINER. + // So by default - these methods don't log anything. We check it here. + logger.entering("blah", "blah"); + logger.entering("blah", "blah", "blah"); + logger.entering("blah", "blah", new Object[] {"blah"}); + if (!handler.messages.isEmpty()) { + throw new RuntimeException("Expected empty, got "+handler.messages); + } + + logger.exiting("blah", "blah"); + logger.exiting("blah", "blah", "blah"); + logger.exiting("blah", "blah", new Object[] {"blah"}); + if (!handler.messages.isEmpty()) { + throw new RuntimeException("Expected empty, got "+handler.messages); + } + + logger.throwing("blah", "blah", new Exception("blah")); + if (!handler.messages.isEmpty()) { + throw new RuntimeException("Expected empty, got "+handler.messages); + } + + // Now we're going to put each level in turn in the threadMap. + // This means that isLoggable(Level.FINER) should now return true if the + // level in the map is not one of the level in the 'stronger' list below + // (here stronger=stronger than FINER) + final List stronger = LEVELS.subList(0, LEVELS.indexOf(Level.FINER)); + for (Level l : LEVELS) { + + logger.threadMap.put(Thread.currentThread().getId(), l); + + // Check that our implementation of isLoggable(level) now returns true + // if 'level' is stronger or equals to 'l' - here we're just checking + // that our implementation of ThreadLogger.isLoggable() returns what + // we want - we're just testing the test code... + final List loggableLevels = LEVELS.subList(0, LEVELS.indexOf(l)+1); + for (Level level : LEVELS) { + if (logger.isLoggable(level) != loggableLevels.contains(level)) { + throw new RuntimeException(level + + ": unexpected result for isLoggable(): expected " + + (loggableLevels.contains(level))); + } + } + + // These methods should now start to log when the level we put in + // the map is weaker or equals to Level.FINER. + // This validates that these methods now call ThreadLogger.isLoggable() + // since the default level for our logger is still Level.INFO. + // If the methods didn't call ThreadLogger.isLoggable() they wouldn't + // log anything, whatever we put in the threadMap... + + logger.entering("blah", "blah"); + logger.entering("blah", "blah", "blah"); + logger.entering("blah", "blah", new Object[] {"blah"}); + if (stronger.contains(l)) { + if (!handler.messages.isEmpty()) { + throw new RuntimeException(l + + ": Expected empty, got " + handler.messages); + } + } else { + if (handler.messages.size() != 3) { + throw new RuntimeException(l + + ": Expected size 3, got " + handler.messages); + } + } + + logger.exiting("blah", "blah"); + logger.exiting("blah", "blah", "blah"); + logger.exiting("blah", "blah", new Object[] {"blah"}); + if (stronger.contains(l)) { + if (!handler.messages.isEmpty()) { + throw new RuntimeException(l + + ": Expected empty, got " + handler.messages); + } + } else { + if (handler.messages.size() != 6) { + throw new RuntimeException(l + + ": Expected size 6, got " + handler.messages); + } + } + + logger.throwing("blah", "blah", new Exception("blah")); + if (stronger.contains(l)) { + if (!handler.messages.isEmpty()) { + throw new RuntimeException(l + + ": Expected empty, got " + handler.messages); + } + } else { + if (handler.messages.size() != 7) { + throw new RuntimeException(l + + ": Expected size 7, got " + handler.messages); + } + } + if (!stronger.contains(l)) { + System.out.println(l + ": Logger.entering/exiting/throwing: " + + handler.messages); + } + handler.messages.clear(); + } + + // Cleanup so that we can start the next test with a clean plate... + handler.messages.clear(); + logger.threadMap.clear(); + + // Test that each logging method calls isLoggable() + // + for (LogTest testCase : LogTest.values()) { + // Each test case is a combination of: + // 1. A level to put in the threadMap. + // 2. A log method to call + final String method = testCase.method(); + + // check our implementation of logger.isLoggable(); + // by default the logger level is Level.INFO, so our implementation + // of isLoggable() should return true for all levels stronger or + // equal to INFO and false for the others. + // We check that here. + for (Level level : LEVELS) { + if (logger.isLoggable(level) != loggable.contains(level)) { + throw new RuntimeException(level + + ": unexpected result for isLoggable(): expected " + + (loggable.contains(level))); + } + } + + // Check that by default the log method will not log for level + // weaker than Level.INFO. + for (Level l : LEVELS.subList(LEVELS.indexOf(Level.INFO) + 1, LEVELS.size())) { + final String test = method + l + ": "; + testCase.log(l, logger, "blah"); + if (!handler.messages.isEmpty()) { + throw new RuntimeException(test + + "Expected empty, got " + handler.messages); + } + } + + // Let's put Level.OFF in the threadMap. Nothing should be logged, + // whichever level is used... + logger.threadMap.put(Thread.currentThread().getId(), Level.OFF); + + // Check that isLoggable() now always return false. + for (Level level : LEVELS) { + if (logger.isLoggable(level)) { + throw new RuntimeException(level + + ": unexpected result for isLoggable(): expected " + + false); + } + } + + // Check that the log method of the test case won't log, whatever + // level we pass to it. This validates that level method calls + // isLoggable() - because otherwise it would log for levels stronger + // or equal to INFO. + for (Level l : LEVELS) { + final String test = "[threadMap=OFF] " + method + l + ": "; + testCase.log(l, logger, "blah"); + if (!handler.messages.isEmpty()) { + throw new RuntimeException(test + + "Expected empty, got " + handler.messages); + } + } + System.out.println("[threadMap=OFF] " + method + "logged " + handler.messages); + + // Now put the testcase's level in the threadMap. + logger.threadMap.put(Thread.currentThread().getId(), testCase.threshold()); + + // The levels for which logging should happen are those that are stronger + // or equals to the testcase's thresholds. + final List loggableLevels = + LEVELS.subList(0, LEVELS.indexOf(testCase.threshold())+1); + + // Check that our implementation of isLoggable() is taking into account + // what we put in the map. + for (Level level : LEVELS) { + if (logger.isLoggable(level) != loggableLevels.contains(level)) { + throw new RuntimeException(level + + ": unexpected result for isLoggable(): expected " + + (loggableLevels.contains(level))); + } + } + + // Now check that the log method is indeed calling our implementation + // of isLoggable(). We do this by first verifying that it won't log + // for levels weaker than what we put in the map. + // + for (Level l : testCase.weaker()) { + final String test = method + l + ": "; + testCase.log(l, logger, "blah"); + if (!handler.messages.isEmpty()) { + throw new RuntimeException(test + + "Expected empty, got " + handler.messages); + } + } + + // Then we check that it will log for the testcase threshold. + final String test2 = method + testCase.threshold() + ": "; + testCase.log(logger, testCase.threshold() + " blah"); + if (handler.messages.isEmpty()) { + throw new RuntimeException(test2 + + "Expected 1 message, but list is empty"); + } + if (!handler.messages.contains(testCase.threshold() + " blah")) { + throw new RuntimeException(test2 + " blah not found: " + + handler.messages); + } + handler.messages.clear(); + + // Now we check that it logs for all 'loggable' level (and doesn't + // log for the others). + for (Level l : LEVELS) { + final String test = method + l + ": "; + testCase.log(l, logger, l + ": blah"); + if (testCase.loggable().contains(l)) { + if (!handler.messages.contains(l + ": blah")) { + throw new RuntimeException(test + "blah not found: " + + handler.messages); + } + } else { + if (handler.messages.contains(l + ": blah")) { + throw new RuntimeException(test + "blah found: " + + handler.messages); + } + } + } + if (handler.messages.size() != testCase.loggable().size()) { + throw new RuntimeException(method + + " Sizes don't match: expected " + + testCase.loggable().size() + " got " + + handler.messages); + } + + // Some visual feedback on what happened. + System.out.println(method + "logged " + handler.messages); + + // Cleanup for next step. + // Since we're iterating over all possible levels we can be + // sure that we haven't missed anything. + // For instance - it could be argued that logger.severe() will + // always log. But since we have 1 case where we put Level.OFF in + // the map and we have verified that severe() didn't log in that + // case, but that it logged in any other case, then we know + // beyond doubt that it called our implementation of isLoggable(). + logger.threadMap.clear(); + handler.messages.clear(); + } + + } +}