| /* | |
| * Copyright (c) 2016, 2024, 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.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.IOException; | |
| import java.io.UncheckedIOException; | |
| import java.lang.ref.Reference; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.Objects; | |
| import java.util.Properties; | |
| import java.util.concurrent.CopyOnWriteArrayList; | |
| import java.util.concurrent.atomic.AtomicLong; | |
| 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; | |
| import java.util.stream.Collectors; | |
| import java.util.stream.Stream; | |
| import sun.util.logging.PlatformLogger; | |
| /** | |
| * @test | |
| * @bug 8159245 | |
| * @summary Tests configuration of loggers. | |
| * @modules java.logging/sun.util.logging.internal java.base/sun.util.logging | |
| * @run main/othervm SystemLoggerConfigTest | |
| * | |
| * @author danielfuchs | |
| */ | |
| public class SystemLoggerConfigTest { | |
| static Logger createSystemLogger(String name) { | |
| return sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess() | |
| .demandLoggerFor(LogManager.getLogManager(), name, | |
| Thread.class.getModule()); | |
| } | |
| static PlatformLogger createPlatformLogger(String name) { | |
| return PlatformLogger.getLogger(name); | |
| } | |
| private static void assertFalse(boolean value, String msg) { | |
| assertEquals(false, value, msg); | |
| } | |
| private static void assertEquals(boolean expected, boolean actual, String msg) { | |
| if (expected != actual) { | |
| throw new AssertionError(msg+": expected: " + expected + " actual: " + actual); | |
| } | |
| } | |
| private static void assertEquals(int expected, int actual, String msg) { | |
| if (expected != actual) { | |
| throw new AssertionError(msg+": expected: " + expected + " actual: " + actual); | |
| } | |
| } | |
| private static void assertEquals(long expected, long actual, String msg) { | |
| if (expected != actual) { | |
| throw new AssertionError(msg+": expected: " + expected + " actual: " + actual); | |
| } | |
| } | |
| private static void assertEquals(Object expected, Object actual, String msg) { | |
| if (!Objects.equals(expected, actual)) { | |
| throw new AssertionError(msg+": expected: " + expected + " actual: " + actual); | |
| } | |
| } | |
| static class TestHandler extends Handler { | |
| private final List<LogRecord> records = new CopyOnWriteArrayList<>(); | |
| public TestHandler() { | |
| super(); | |
| setLevel(Level.ALL); | |
| } | |
| @Override | |
| public void publish(LogRecord lr) { | |
| records.add(lr); | |
| } | |
| public List<LogRecord> drain() { | |
| List<LogRecord> list = new ArrayList<>(records); | |
| records.clear(); | |
| return list; | |
| } | |
| public void close() { | |
| records.clear(); | |
| } | |
| public void flush() { | |
| } | |
| } | |
| public static class TestHandler1 extends TestHandler { | |
| final static AtomicLong COUNT = new AtomicLong(); | |
| public TestHandler1() { | |
| COUNT.incrementAndGet(); | |
| } | |
| } | |
| public static class TestHandler2 extends TestHandler { | |
| final static AtomicLong COUNT = new AtomicLong(); | |
| public TestHandler2() { | |
| COUNT.incrementAndGet(); | |
| } | |
| } | |
| static enum TestCase { WITHSECURITY, NOSECURITY } | |
| public static void main(String[] args) { | |
| launch(); | |
| } | |
| public static void launch() { | |
| try { | |
| test("1", ".child"); | |
| test("2", ""); | |
| testUpdateConfiguration("3"); | |
| testSetPlatformLevel("4"); | |
| } catch (IOException io) { | |
| throw new UncheckedIOException(io); | |
| } | |
| } | |
| public static void test(String step, String ext) | |
| throws IOException { | |
| System.out.println("\n*** Testing " + step + ext); | |
| final String systemName1a = "system.logger.one.a." + step + ext; | |
| final String systemName1b = "system.logger.one.b." + step + ext; | |
| final String appName1a = "system.logger.one.a." + step; | |
| final String appName1b = "system.logger.one.b." + step; | |
| final String msg1a = "logger name: " + systemName1a; | |
| final String msg1b = "logger name: " + systemName1b; | |
| final String systemName2 = "system.logger.two." + step + ext; | |
| final String appName2 = "system.logger.two." + step; | |
| final String msg2 = "logger name: " + systemName2; | |
| final String systemName3 = "system.logger.three." + step + ext; | |
| final String appName3 = "system.logger.three." + step; | |
| final String msg3 = "logger name: " + systemName3; | |
| List<LogRecord> records; | |
| System.out.println("\n[Case #1] Creating platform logger: " + systemName1a); | |
| PlatformLogger system1a = createPlatformLogger(systemName1a); | |
| System.out.println(" Creating platform logger: " + systemName1b); | |
| PlatformLogger system1b = createPlatformLogger(systemName1b); | |
| System.out.println(" Adding handler on root logger..."); | |
| TestHandler test1 = new TestHandler(); | |
| Logger.getLogger("").addHandler(test1); | |
| System.out.println(" Creating and configuring app logger: " + appName1a | |
| + ", " + appName1b); | |
| Logger app1a = Logger.getLogger(appName1a); | |
| app1a.setLevel(Level.INFO); | |
| Logger app1b = Logger.getLogger(appName1b); | |
| app1b.setLevel(Level.INFO); | |
| assertFalse(system1a.isLoggable(PlatformLogger.Level.FINEST), | |
| "Unexpected level for " + system1a); | |
| System.out.println(" Configuring root logger..."); | |
| Logger.getLogger("").setLevel(Level.FINEST); | |
| System.out.println(" Logging through system logger: " + systemName1a); | |
| system1a.finest(msg1a); | |
| Reference.reachabilityFence(app1a); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| System.out.println(" Logging through system logger: " + systemName1b); | |
| system1b.finest(msg1b); | |
| Reference.reachabilityFence(app1b); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| Logger.getLogger("system.logger.one.a").finest("system.logger.one.a"); | |
| records = test1.drain(); | |
| assertEquals("system.logger.one.a", records.get(0).getMessage(), "Unexpected message: "); | |
| Logger.getLogger("").setLevel(Level.INFO); | |
| Logger.getLogger("system.logger.one.a").finest("system.logger.one.a"); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| Reference.reachabilityFence(system1a); | |
| Reference.reachabilityFence(system1b); | |
| System.out.println("\n[Case #2] Creating system logger: " + systemName2); | |
| Logger system2 = createSystemLogger(systemName2); | |
| System.out.println(" Creating app logger: " + appName2); | |
| Logger app2 = Logger.getLogger(appName2); | |
| System.out.println(" Configuring app logger..."); | |
| TestHandler test2 = new TestHandler(); | |
| app2.setLevel(Level.ALL); | |
| app2.setUseParentHandlers(false); | |
| app2.addHandler(test2); | |
| System.out.println(" Logging through system logger: " + systemName2); | |
| system2.finest(msg2); | |
| records = test2.drain(); | |
| assertEquals(1, records.size(), "Unexpected size for " + records.toString()); | |
| assertEquals(msg2, records.get(0).getMessage(), "Unexpected message: "); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| Reference.reachabilityFence(app2); | |
| Reference.reachabilityFence(system2); | |
| System.out.println("\n[Case #3] Creating app logger: " + appName3); | |
| Logger app3 = Logger.getLogger(appName3); | |
| System.out.println(" Configuring app logger..."); | |
| TestHandler test3 = new TestHandler(); | |
| app3.setLevel(Level.ALL); | |
| app3.setUseParentHandlers(false); | |
| app3.addHandler(test3); | |
| System.out.println(" Creating system logger: " + systemName3); | |
| Logger system3 = createSystemLogger(systemName3); | |
| System.out.println(" Logging through system logger: " + systemName3); | |
| system3.finest(msg3); | |
| records = test3.drain(); | |
| assertEquals(1, records.size(), "Unexpected size for " + records.toString()); | |
| assertEquals(msg3, records.get(0).getMessage(), "Unexpected message: "); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| Reference.reachabilityFence(app3); | |
| Reference.reachabilityFence(system3); | |
| System.gc(); | |
| } | |
| @SuppressWarnings("deprecated") | |
| static void setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level) { | |
| logger.setLevel(level); | |
| } | |
| public static void testSetPlatformLevel(String step) { | |
| System.out.println("\n*** Testing PlatformLogger.setLevel " + step); | |
| System.out.println("\n[Case #5] Creating app logger: " + step); | |
| // this should return named logger in the global context | |
| Logger foo = Logger.getLogger(step); | |
| foo.setLevel(Level.FINE); | |
| System.out.println(" Creating platform logger: " + step); | |
| PlatformLogger foo1 = PlatformLogger.getLogger(step); | |
| System.out.println(" Configuring platform logger..."); | |
| setPlatformLevel(foo1, PlatformLogger.Level.INFO); | |
| System.out.println(" Checking levels..."); | |
| assertEquals(foo.getName(), foo1.getName(), "Bad logger names"); | |
| // both logger share the same config | |
| assertEquals(foo.getLevel(), Level.INFO, "Bad level for user logger"); | |
| assertEquals(foo1.level(), PlatformLogger.Level.INFO, | |
| "Bad level for platform logger"); | |
| } | |
| static void updateConfiguration(Properties props) throws IOException { | |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| props.store(baos, ""); | |
| ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |
| LogManager.getLogManager().updateConfiguration(bais, (k) -> (o,n) -> n != null ? n : o); | |
| } | |
| static void readConfiguration(Properties props) throws IOException { | |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| props.store(baos, ""); | |
| ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |
| LogManager.getLogManager().readConfiguration(bais); | |
| } | |
| // Tests that though two loggers exist, only one handler is created for the | |
| // pair when reading configuration. | |
| // | |
| public static void testUpdateConfiguration(String step) throws IOException { | |
| System.out.println("\n*** Testing LogManager.updateConfiguration " + step); | |
| final String name1a = "system.logger.one.a." + step; | |
| final String name1b = "system.logger.one.b." + step; | |
| final String msg1a = "logger name: " + name1a; | |
| final String msg1b = "logger name: " + name1b; | |
| List<LogRecord> records; | |
| TestHandler1.COUNT.set(0); | |
| TestHandler2.COUNT.set(0); | |
| Properties props = new Properties(); | |
| props.setProperty(name1a+".handlers", TestHandler1.class.getName()); | |
| updateConfiguration(props); | |
| assertEquals(0, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| System.out.println("\n[Case #4] Creating app logger: " + name1a); | |
| Logger app1a = Logger.getLogger(name1a); | |
| System.out.println(" Configuring app logger..."); | |
| TestHandler test1 = new TestHandler(); | |
| app1a.setLevel(Level.ALL); | |
| app1a.setUseParentHandlers(false); | |
| app1a.addHandler(test1); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| System.out.println(" Creating system logger: " + name1a); | |
| Logger system1a = createSystemLogger(name1a); | |
| assertEquals(Level.ALL, system1a.getLevel(), "Bad level for system logger " + name1a); | |
| System.out.println(" Logging through system logger: " + name1a); | |
| system1a.finest(msg1a); | |
| records = test1.drain(); | |
| assertEquals(1, records.size(), "Unexpected size for " + records.toString()); | |
| assertEquals(msg1a, records.get(0).getMessage(), "Unexpected message: "); | |
| records = test1.drain(); | |
| assertEquals(0, records.size(), "Unexpected size for " + records.toString()); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| props.setProperty(name1a+".handlers", TestHandler2.class.getName()); | |
| updateConfiguration(props); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| updateConfiguration(props); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| readConfiguration(props); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| // readConfiguration reset handlers but does not recreate them | |
| assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| updateConfiguration(props); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| LogManager.getLogManager().reset(); | |
| updateConfiguration(props); | |
| assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(2, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| props.setProperty(name1a+".handlers", | |
| TestHandler2.class.getName() + "," + TestHandler1.class.getName()); | |
| updateConfiguration(props); | |
| assertEquals(2, TestHandler1.COUNT.get(), "Bad instance count for " | |
| + TestHandler1.class.getName()); | |
| assertEquals(3, TestHandler2.COUNT.get(), "Bad instance count for " | |
| + TestHandler2.class.getName()); | |
| Reference.reachabilityFence(app1a); | |
| Reference.reachabilityFence(system1a); | |
| LogManager.getLogManager().readConfiguration(); | |
| System.gc(); | |
| } | |
| } |