/* * Copyright (c) 2016, 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.lang.reflect.Method; import java.util.Arrays; import java.util.LinkedList; import java.util.Objects; import java.util.Queue; import java.util.ResourceBundle; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * @test * @bug 8152389 * @summary Verify the correct behavior of LogRecord.inferCaller() in particular * when a message is directly logged through the root logger. * @run main/othervm TestInferCaller * @author danielfuchs */ public class TestInferCaller { static final class LogEvent { public final String className; public final String methodName; public final LogRecord record; public LogEvent(String className, String methodName, LogRecord record) { this.className = className; this.methodName = methodName; this.record = record; } } static final class TestHandler extends Handler { public static final Queue<LogEvent> PUBLISHED = new LinkedList<LogEvent>(); public TestHandler() { initLevel(Level.ALL); } @Override public void close() throws SecurityException { } @Override public void publish(LogRecord record) { LogEvent event = new LogEvent(record.getSourceClassName(), record.getSourceMethodName(), record); PUBLISHED.add(event); } @Override public void flush() {} private void initLevel(Level newLevel) { super.setLevel(newLevel); } } public void test1(Logger logger) { System.out.println("test1: " + loggerName(logger)); AtomicInteger count = new AtomicInteger(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.setLevel(Level.ALL); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.severe("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); LogEvent event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.warning("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.info("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.config("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.fine("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.finer("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); logger.finest("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test1", "message " + count.get()); } void test2(Logger logger) { AtomicInteger count = new AtomicInteger(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.setLevel(Level.ALL); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); for (Level l : Arrays.asList(Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG, Level.FINE, Level.FINER, Level.FINEST)) { System.out.println("test2: " + loggerName(logger) + " " + l); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.log(l, "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); LogEvent event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); logger.log(l, "message " + count.incrementAndGet(), "param"); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); logger.log(l, "message " + count.incrementAndGet(), new Object[] {"foo", "bar"}); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); logger.log(l, "message " + count.incrementAndGet(), new RuntimeException()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); // JDK 8 & 9 only (uses lambda) logger.log(l, () -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); // JDK 8 & 9 only (uses lambda) logger.log(l, new RuntimeException(), () -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); // JDK 9 only: new API logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), (Object[]) null); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); // JDK 9 only: new API logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), new RuntimeException()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test2", "message " + count.get()); } } void test3(Logger logger) { System.out.println("test3: " + loggerName(logger)); AtomicInteger count = new AtomicInteger(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.setLevel(Level.ALL); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); testReflection(logger, count, "severe", "testReflection"); testReflection(logger, count, "warning", "testReflection"); testReflection(logger, count, "info", "testReflection"); testReflection(logger, count, "config", "testReflection"); testReflection(logger, count, "fine", "testReflection"); testReflection(logger, count, "finer", "testReflection"); testReflection(logger, count, "finest", "testReflection"); } void testReflection(Logger logger, AtomicInteger count, String logm, String method) { try { Method m = Logger.class.getMethod(logm, String.class); m.invoke(logger, "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); LogEvent event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), method, "message " + count.get()); Method m2 = Method.class.getMethod("invoke", Object.class, new Object[0].getClass()); m2.invoke(m, logger, new Object[] { "message " + count.incrementAndGet() }); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), method, "message " + count.get()); m2.invoke(m2, m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }}); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), method, "message " + count.get()); m2.invoke(m2, m2, new Object[] { m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }}}); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), method, "message " + count.get()); } catch (Error | RuntimeException x ) { throw x; } catch (Exception x) { throw new RuntimeException(x); } } // JDK 8 & 9 only (uses lambda) public void test4(Logger logger) { System.out.println("test4: " + loggerName(logger)); AtomicInteger count = new AtomicInteger(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.setLevel(Level.ALL); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.severe(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); LogEvent event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.warning(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.info(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.config(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.fine(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.finer(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); logger.finest(() -> "message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), "test4", "message " + count.get()); } // JDK 8 & 9 only (uses lambda) void test5(Logger logger) { System.out.println("test5: " + loggerName(logger)); AtomicInteger count = new AtomicInteger(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); logger.setLevel(Level.ALL); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); testLambda(count, logger::severe, "testLambda"); testLambda(count, logger::warning, "testLambda"); testLambda(count, logger::info, "testLambda"); testLambda(count, logger::config, "testLambda"); testLambda(count, logger::fine, "testLambda"); testLambda(count, logger::finer, "testLambda"); testLambda(count, logger::finest, "testLambda"); } // JDK 8 & 9 only (uses lambda) void testLambda(AtomicInteger count, Consumer<String> logm, String method) { logm.accept("message " + count.incrementAndGet()); assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: "); LogEvent event = TestHandler.PUBLISHED.remove(); assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: "); checkEvent(event, this.getClass().getName(), method, "message " + count.get()); } private static String loggerName(Logger logger) { String name = logger.getName(); if (name == null) return "<anonymous>"; if (name.isEmpty()) return "<RootLogger>"; return "\"" + name + "\""; } public void test(Logger logger) { test1(logger); test2(logger); test3(logger); // JDK 8 & 9 only (uses lambda) test4(logger); test5(logger); } public static void main(String[] args) { TestInferCaller test = new TestInferCaller(); Logger root = Logger.getLogger(""); for (Handler h : root.getHandlers()) { h.setLevel(Level.OFF); } root.addHandler(new TestHandler()); for (Logger logger : Arrays.asList(root, Logger.getGlobal(), Logger.getAnonymousLogger(), Logger.getLogger("foo.bar"))) { System.out.println("Testing with: " + loggerName(logger) + " " + logger.getClass()); test.test(logger); } } private static void assertEquals(int expected, int actual, String what) { if (expected != actual) { throw new RuntimeException(what + "\n\texpected: " + expected + "\n\tactual: " + actual); } } private static void assertEquals(String expected, String actual, String what) { if (!Objects.equals(expected, actual)) { throw new RuntimeException(what + "\n\texpected: " + expected + "\n\tactual: " + actual); } } private void checkEvent(LogEvent event, String className, String methodName, String message) { assertEquals(className, event.className, "Bad class name: "); assertEquals(className, event.record.getSourceClassName(), "Bad source class name: "); assertEquals(methodName, event.methodName, "Bad method name: "); assertEquals(methodName, event.record.getSourceMethodName(), "Bad source method name: "); assertEquals(message, event.record.getMessage(), "Bad message: "); } }