8159245: Loggers created by system classes are not initialized correctly when configured programmatically from application code

Loggers of the same name now share the same configuration.

Reviewed-by: mchung, mli
This commit is contained in:
Daniel Fuchs 2016-07-12 11:29:01 +01:00
parent 1931ac4196
commit d5e84de014
7 changed files with 673 additions and 53 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -387,11 +387,15 @@ public class LogManager {
assert rootLogger == null;
assert initializedCalled && !initializationDone;
// create root logger before reading primordial
// configuration - to ensure that it will be added
// before the global logger, and not after.
owner.rootLogger = owner.new RootLogger();
// Read configuration.
owner.readPrimordialConfiguration();
// Create and retain Logger for the root of the namespace.
owner.rootLogger = owner.new RootLogger();
owner.addLogger(owner.rootLogger);
if (!owner.rootLogger.isLevelInitialized()) {
owner.rootLogger.setLevel(defaultLevel);
@ -516,7 +520,7 @@ public class LogManager {
if (result == null) {
// only allocate the new logger once
Logger newLogger = new Logger(name, resourceBundleName,
module == null ? null : module, this, false);
module, this, false);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
@ -569,15 +573,13 @@ public class LogManager {
} while (logger == null);
// LogManager will set the sysLogger's handlers via LogManager.addLogger method.
if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) {
// if logger already exists but handlers not set
if (logger != sysLogger) {
// if logger already exists we merge the two logger configurations.
final Logger l = logger;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
for (Handler hdl : l.accessCheckedHandlers()) {
sysLogger.addHandler(hdl);
}
l.mergeWithSystemLogger(sysLogger);
return null;
}
});

View File

@ -259,13 +259,185 @@ public class Logger {
private static final RuntimePermission GET_CLASS_LOADER_PERMISSION =
new RuntimePermission("getClassLoader");
// A value class that holds the logger configuration data.
// This configuration can be shared between an application logger
// and a system logger of the same name.
private static final class ConfigurationData {
// The delegate field is used to avoid races while
// merging configuration. This will ensure that any pending
// configuration action on an application logger will either
// be finished before the merge happens, or will be forwarded
// to the system logger configuration after the merge is completed.
// By default delegate=this.
private volatile ConfigurationData delegate;
volatile boolean useParentHandlers;
volatile Filter filter;
volatile Level levelObject;
volatile int levelValue; // current effective level value
final CopyOnWriteArrayList<Handler> handlers =
new CopyOnWriteArrayList<>();
ConfigurationData() {
delegate = this;
useParentHandlers = true;
levelValue = Level.INFO.intValue();
}
void setUseParentHandlers(boolean flag) {
useParentHandlers = flag;
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.useParentHandlers = useParentHandlers;
}
}
}
void setFilter(Filter f) {
filter = f;
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.filter = filter;
}
}
}
void setLevelObject(Level l) {
levelObject = l;
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.levelObject = levelObject;
}
}
}
void setLevelValue(int v) {
levelValue = v;
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.levelValue = levelValue;
}
}
}
void addHandler(Handler h) {
if (handlers.add(h)) {
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.handlers.addIfAbsent(h);
}
}
}
}
void removeHandler(Handler h) {
if (handlers.remove(h)) {
if (delegate != this) {
// merge in progress - propagate value to system peer.
final ConfigurationData system = delegate;
synchronized (system) {
system.handlers.remove(h);
}
}
}
}
ConfigurationData merge(Logger systemPeer) {
if (!systemPeer.isSystemLogger) {
// should never come here
throw new InternalError("not a system logger");
}
ConfigurationData system = systemPeer.config;
if (system == this) {
// nothing to do
return system;
}
synchronized (system) {
// synchronize before checking on delegate to counter
// race conditions where two threads might attempt to
// merge concurrently
if (delegate == system) {
// merge already performed;
return system;
}
// publish system as the temporary delegate configuration.
// This should take care of potential race conditions where
// an other thread might attempt to call e.g. setlevel on
// the application logger while merge is in progress.
// (see implementation of ConfigurationData::setLevel)
delegate = system;
// merge this config object data into the system config
system.useParentHandlers = useParentHandlers;
system.filter = filter;
system.levelObject = levelObject;
system.levelValue = levelValue;
// Prevent race condition in case two threads attempt to merge
// configuration and add handlers at the same time. We don't want
// to add the same handlers twice.
//
// Handlers are created and loaded by LogManager.addLogger. If we
// reach here, then it means that the application logger has
// been created first and added with LogManager.addLogger, and the
// system logger was created after - and no handler has been added
// to it by LogManager.addLogger. Therefore, system.handlers
// should be empty.
//
// A non empty cfg.handlers list indicates a race condition
// where two threads might attempt to merge the configuration
// or add handlers concurrently. Though of no consequence for
// the other data (level etc...) this would be an issue if we
// added the same handlers twice.
//
for (Handler h : handlers) {
if (!system.handlers.contains(h)) {
systemPeer.addHandler(h);
}
}
system.handlers.retainAll(handlers);
system.handlers.addAllAbsent(handlers);
}
// sanity: update effective level after merging
synchronized(treeLock) {
systemPeer.updateEffectiveLevel();
}
return system;
}
}
// The logger configuration data. Ideally, this should be final
// for system loggers, and replace-once for application loggers.
// When an application requests a logger by name, we do not know a-priori
// whether that corresponds to a system logger name or not.
// So if no system logger by that name already exists, we simply return an
// application logger.
// If a system class later requests a system logger of the same name, then
// the application logger and system logger configurations will be merged
// in a single instance of ConfigurationData that both loggers will share.
private volatile ConfigurationData config;
private volatile LogManager manager;
private String name;
private final CopyOnWriteArrayList<Handler> handlers =
new CopyOnWriteArrayList<>();
private volatile LoggerBundle loggerBundle = NO_RESOURCE_BUNDLE;
private volatile boolean useParentHandlers = true;
private volatile Filter filter;
private boolean anonymous;
// Cache to speed up behavior of findResourceBundle:
@ -280,8 +452,6 @@ public class Logger {
// references from children to parents.
private volatile Logger parent; // our nearest parent.
private ArrayList<LogManager.LoggerWeakRef> kids; // WeakReferences to loggers that have us as parent
private volatile Level levelObject;
private volatile int levelValue; // current effective level value
private WeakReference<Module> callerModuleRef;
private final boolean isSystemLogger;
@ -384,9 +554,29 @@ public class Logger {
LogManager manager, boolean isSystemLogger) {
this.manager = manager;
this.isSystemLogger = isSystemLogger;
setupResourceInfo(resourceBundleName, caller);
this.config = new ConfigurationData();
this.name = name;
levelValue = Level.INFO.intValue();
setupResourceInfo(resourceBundleName, caller);
}
// Called by LogManager when a system logger is created
// after a user logger of the same name.
// Ensure that both loggers will share the same
// configuration.
final void mergeWithSystemLogger(Logger system) {
// sanity checks
if (!system.isSystemLogger
|| anonymous
|| name == null
|| !name.equals(system.name)) {
// should never come here
throw new InternalError("invalid logger merge");
}
checkPermission();
final ConfigurationData cfg = config;
if (cfg != system.config) {
config = cfg.merge(system);
}
}
private void setCallerModuleRef(Module callerModule) {
@ -408,7 +598,7 @@ public class Logger {
// The manager field is not initialized here.
this.name = name;
this.isSystemLogger = true;
levelValue = Level.INFO.intValue();
config = new ConfigurationData();
}
// It is called from LoggerContext.addLocalLogger() when the logger
@ -451,7 +641,7 @@ public class Logger {
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager();
if (!SystemLoggerHelper.disableCallerCheck) {
if (caller.getClassLoader() == null) {
if (isSystem(caller.getModule())) {
return manager.demandSystemLogger(name, resourceBundleName, caller);
}
}
@ -740,7 +930,7 @@ public class Logger {
*/
public void setFilter(Filter newFilter) throws SecurityException {
checkPermission();
filter = newFilter;
config.setFilter(newFilter);
}
/**
@ -749,7 +939,7 @@ public class Logger {
* @return a filter object (may be null)
*/
public Filter getFilter() {
return filter;
return config.filter;
}
/**
@ -765,7 +955,7 @@ public class Logger {
if (!isLoggable(record.getLevel())) {
return;
}
Filter theFilter = filter;
Filter theFilter = config.filter;
if (theFilter != null && !theFilter.isLoggable(record)) {
return;
}
@ -784,7 +974,7 @@ public class Logger {
}
final boolean useParentHdls = isSystemLogger
? logger.useParentHandlers
? logger.config.useParentHandlers
: logger.getUseParentHandlers();
if (!useParentHdls) {
@ -1804,13 +1994,13 @@ public class Logger {
public void setLevel(Level newLevel) throws SecurityException {
checkPermission();
synchronized (treeLock) {
levelObject = newLevel;
config.setLevelObject(newLevel);
updateEffectiveLevel();
}
}
final boolean isLevelInitialized() {
return levelObject != null;
return config.levelObject != null;
}
/**
@ -1821,7 +2011,7 @@ public class Logger {
* @return this Logger's level
*/
public Level getLevel() {
return levelObject;
return config.levelObject;
}
/**
@ -1833,6 +2023,7 @@ public class Logger {
* @return true if the given message level is currently being logged.
*/
public boolean isLoggable(Level level) {
int levelValue = config.levelValue;
if (level.intValue() < levelValue || levelValue == offValue) {
return false;
}
@ -1862,7 +2053,7 @@ public class Logger {
public void addHandler(Handler handler) throws SecurityException {
Objects.requireNonNull(handler);
checkPermission();
handlers.add(handler);
config.addHandler(handler);
}
/**
@ -1880,7 +2071,7 @@ public class Logger {
if (handler == null) {
return;
}
handlers.remove(handler);
config.removeHandler(handler);
}
/**
@ -1895,7 +2086,7 @@ public class Logger {
// This method should ideally be marked final - but unfortunately
// it needs to be overridden by LogManager.RootLogger
Handler[] accessCheckedHandlers() {
return handlers.toArray(emptyHandlers);
return config.handlers.toArray(emptyHandlers);
}
/**
@ -1912,7 +2103,7 @@ public class Logger {
*/
public void setUseParentHandlers(boolean useParentHandlers) {
checkPermission();
this.useParentHandlers = useParentHandlers;
config.setUseParentHandlers(useParentHandlers);
}
/**
@ -1922,7 +2113,7 @@ public class Logger {
* @return true if output is to be sent to the logger's parent
*/
public boolean getUseParentHandlers() {
return useParentHandlers;
return config.useParentHandlers;
}
/**
@ -2256,11 +2447,13 @@ public class Logger {
// Figure out our current effective level.
int newLevelValue;
final ConfigurationData cfg = config;
final Level levelObject = cfg.levelObject;
if (levelObject != null) {
newLevelValue = levelObject.intValue();
} else {
if (parent != null) {
newLevelValue = parent.levelValue;
newLevelValue = parent.config.levelValue;
} else {
// This may happen during initialization.
newLevelValue = Level.INFO.intValue();
@ -2268,11 +2461,11 @@ public class Logger {
}
// If our effective value hasn't changed, we're done.
if (levelValue == newLevelValue) {
if (cfg.levelValue == newLevelValue) {
return;
}
levelValue = newLevelValue;
cfg.setLevelValue(newLevelValue);
// System.err.println("effective level: \"" + getName() + "\" := " + level);

View File

@ -539,6 +539,7 @@ public class DefaultLoggerTest {
throw new RuntimeException("identical loggers");
}
final java.util.logging.Logger sink;
final java.util.logging.Logger appSink;
final java.util.logging.Logger sysSink;
final java.util.logging.Handler appHandler;
@ -548,10 +549,9 @@ public class DefaultLoggerTest {
try {
appSink = java.util.logging.Logger.getLogger("foo");
sysSink = accessSystemLogger.demandSystemLogger("foo");
appSink.addHandler(appHandler = new MyHandler());
sysSink.addHandler(sysHandler = new MyHandler());
appSink.setUseParentHandlers(false);
sysSink.setUseParentHandlers(false);
sink = java.util.logging.Logger.getLogger("foo");
sink.addHandler(appHandler = sysHandler = new MyHandler());
sink.setUseParentHandlers(false);
provider = LoggerFinder.getLoggerFinder();
} finally {
allowAll.get().set(false);

View File

@ -299,10 +299,9 @@ public class DefaultLoggerFinderTest {
final java.util.logging.Logger appSink = java.util.logging.Logger.getLogger("foo");
final java.util.logging.Logger sysSink = accessSystemLogger.demandSystemLogger("foo");
appSink.addHandler(new MyHandler());
sysSink.addHandler(new MyHandler());
appSink.setUseParentHandlers(VERBOSE);
sysSink.setUseParentHandlers(VERBOSE);
final java.util.logging.Logger sink = java.util.logging.Logger.getLogger("foo");
sink.addHandler(new MyHandler());
sink.setUseParentHandlers(VERBOSE);
Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
LoggerFinder provider;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -390,6 +390,7 @@ public class DefaultLoggerBridgeTest {
throw new RuntimeException("identical loggers");
}
final java.util.logging.Logger sink;
final java.util.logging.Logger appSink;
final java.util.logging.Logger sysSink;
final MyHandler appHandler;
@ -404,10 +405,13 @@ public class DefaultLoggerBridgeTest {
if (appSink == sysSink) {
throw new RuntimeException("identical backend loggers");
}
appSink.addHandler(appHandler = new MyHandler());
sysSink.addHandler(sysHandler = new MyHandler());
appSink.setUseParentHandlers(VERBOSE);
sysSink.setUseParentHandlers(VERBOSE);
sink = java.util.logging.Logger.getLogger("foo");
if (appSink != sink) {
throw new RuntimeException("expected same application logger");
}
sink.addHandler(appHandler = sysHandler = new MyHandler());
sink.setUseParentHandlers(VERBOSE);
} finally {
allowAll.get().set(old);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -42,9 +42,9 @@ import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.util.logging.Logger;
import sun.util.logging.PlatformLogger;
import sun.util.logging.internal.LoggingProviderImpl;
import java.lang.reflect.Module;
/**
* @test
@ -248,10 +248,9 @@ public class DefaultPlatformLoggerTest {
DefaultPlatformLoggerTest.class.getModule());
java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess()
.demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class.getModule());
appSink.addHandler(new MyHandler());
sysSink.addHandler(new MyHandler());
appSink.setUseParentHandlers(VERBOSE);
sysSink.setUseParentHandlers(VERBOSE);
java.util.logging.Logger sink = Logger.getLogger("foo");
sink.addHandler(new MyHandler());
sink.setUseParentHandlers(VERBOSE);
System.out.println("\n*** Without Security Manager\n");
test(provider, true, appSink, sysSink);
@ -274,7 +273,7 @@ public class DefaultPlatformLoggerTest {
public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
// No way to giva a resource bundle to a platform logger.
// No way to give a resource bundle to a platform logger.
// ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();

View File

@ -0,0 +1,423 @@
/*
* 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.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.ref.Reference;
import java.security.Permission;
import java.security.Policy;
import java.security.ProtectionDomain;
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 NOSECURITY
* @run main/othervm SystemLoggerConfigTest WITHSECURITY
*
* @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) {
if (args == null || args.length == 0) {
args = Stream.of(TestCase.values())
.map(String::valueOf)
.collect(Collectors.toList())
.toArray(new String[0]);
}
Stream.of(args)
.map(TestCase::valueOf)
.forEach(SystemLoggerConfigTest::launch);
}
public static void launch(TestCase test) {
switch(test) {
case WITHSECURITY:
Policy.setPolicy(new Policy() {
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
return true;
}
});
System.setSecurityManager(new SecurityManager());
break;
case NOSECURITY:
break;
default:
throw new InternalError("Unexpected enum: " + test);
}
try {
test(test.name(), ".1", ".child");
test(test.name(), ".2", "");
testUpdateConfiguration(test.name(), ".3");
testSetPlatformLevel(test.name(), ".4");
} catch (IOException io) {
throw new UncheckedIOException(io);
}
}
public static void test(String name, String step, String ext)
throws IOException {
System.out.println("\n*** Testing " + name + step + ext);
final String systemName1a = "system.logger.one.a." + name + step + ext;
final String systemName1b = "system.logger.one.b." + name + step + ext;
final String appName1a = "system.logger.one.a." + name + step;
final String appName1b = "system.logger.one.b." + name + step;
final String msg1a = "logger name: " + systemName1a;
final String msg1b = "logger name: " + systemName1b;
final String systemName2 = "system.logger.two." + name + step + ext;
final String appName2 = "system.logger.two." + name + step;
final String msg2 = "logger name: " + systemName2;
final String systemName3 = "system.logger.three." + name + step + ext;
final String appName3 = "system.logger.three." + name + 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 name, String step) {
System.out.println("\n*** Testing PlatformLogger.setLevel " + name + step);
System.out.println("\n[Case #5] Creating app logger: " + name + step);
// this should return named logger in the global context
Logger foo = Logger.getLogger(name + step);
foo.setLevel(Level.FINE);
System.out.println(" Creating platform logger: " + name + step);
PlatformLogger foo1 = PlatformLogger.getLogger(name + 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 name, String step) throws IOException {
System.out.println("\n*** Testing LogManager.updateConfiguration " + name + step);
final String name1a = "system.logger.one.a." + name + step;
final String name1b = "system.logger.one.b." + name + 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();
}
}