blob: 6d0cc8de4d6f66bc8566b0dba15311b7c1ae7dce [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
Daniel Fuchs679db142015-04-02 11:42:07 +02002 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
J. Duke319a3b92007-12-01 00:00:00 +00003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07007 * published by the Free Software Foundation. Oracle designates this
J. Duke319a3b92007-12-01 00:00:00 +00008 * particular file as subject to the "Classpath" exception as provided
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07009 * by Oracle in the LICENSE file that accompanied this code.
J. Duke319a3b92007-12-01 00:00:00 +000010 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
Kelly O'Hairfe008ae2010-05-25 15:58:33 -070021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
J. Duke319a3b92007-12-01 00:00:00 +000024 */
25
26
27package java.util.logging;
28
29import java.io.*;
30import java.util.*;
31import java.security.*;
Jeremy Manson9a0c9852010-06-22 10:54:59 -070032import java.lang.ref.ReferenceQueue;
J. Duke319a3b92007-12-01 00:00:00 +000033import java.lang.ref.WeakReference;
Daniel Fuchsce950c02015-04-02 16:24:46 +020034import java.util.concurrent.ConcurrentHashMap;
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020035import java.nio.file.Paths;
Daniel Fuchs96da5b92014-11-24 17:02:37 +010036import java.util.concurrent.CopyOnWriteArrayList;
Peter Levart30bcd972015-05-17 10:38:36 +020037import java.util.concurrent.locks.ReentrantLock;
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020038import java.util.function.BiFunction;
39import java.util.function.Function;
40import java.util.function.Predicate;
41import java.util.stream.Collectors;
42import java.util.stream.Stream;
Chris Hegarty0cc24c22015-09-28 13:39:27 +010043import jdk.internal.misc.JavaAWTAccess;
44import jdk.internal.misc.SharedSecrets;
Daniel Fuchsbd8942b2015-11-20 19:26:16 +010045import sun.util.logging.internal.LoggingProviderImpl;
Daniel Fuchs1e0d1452016-04-27 18:04:16 +020046import java.lang.reflect.Module;
47import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
J. Duke319a3b92007-12-01 00:00:00 +000048
49/**
50 * There is a single global LogManager object that is used to
51 * maintain a set of shared state about Loggers and log services.
52 * <p>
53 * This LogManager object:
54 * <ul>
55 * <li> Manages a hierarchical namespace of Logger objects. All
56 * named Loggers are stored in this namespace.
57 * <li> Manages a set of logging control properties. These are
58 * simple key-value pairs that can be used by Handlers and
59 * other logging objects to configure themselves.
60 * </ul>
61 * <p>
62 * The global LogManager object can be retrieved using LogManager.getLogManager().
63 * The LogManager object is created during class initialization and
64 * cannot subsequently be changed.
65 * <p>
66 * At startup the LogManager class is located using the
67 * java.util.logging.manager system property.
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020068 *
69 * <h3>LogManager Configuration</h3>
70 *
71 * A LogManager initializes the logging configuration via
72 * the {@link #readConfiguration()} method during LogManager initialization.
73 * By default, LogManager default configuration is used.
74 * The logging configuration read by LogManager must be in the
75 * {@linkplain Properties properties file} format.
J. Duke319a3b92007-12-01 00:00:00 +000076 * <p>
Alan Bateman2f3796a2012-11-30 11:18:16 +000077 * The LogManager defines two optional system properties that allow control over
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020078 * the initial configuration, as specified in the {@link #readConfiguration()}
79 * method:
J. Duke319a3b92007-12-01 00:00:00 +000080 * <ul>
81 * <li>"java.util.logging.config.class"
82 * <li>"java.util.logging.config.file"
83 * </ul>
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020084 * <p>
85 * These two system properties may be specified on the command line to the "java"
Alan Bateman2f3796a2012-11-30 11:18:16 +000086 * command, or as system property definitions passed to JNI_CreateJavaVM.
J. Duke319a3b92007-12-01 00:00:00 +000087 * <p>
Daniel Fuchsbd69fa02015-10-12 20:13:22 +020088 * The {@linkplain Properties properties} for loggers and Handlers will have
89 * names starting with the dot-separated name for the handler or logger.<br>
J. Duke319a3b92007-12-01 00:00:00 +000090 * The global logging properties may include:
91 * <ul>
92 * <li>A property "handlers". This defines a whitespace or comma separated
93 * list of class names for handler classes to load and register as
94 * handlers on the root Logger (the Logger named ""). Each class
95 * name must be for a Handler class which has a default constructor.
96 * Note that these Handlers may be created lazily, when they are
97 * first used.
98 *
99 * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
100 * comma separated list of class names for handlers classes to
101 * load and register as handlers to the specified logger. Each class
102 * name must be for a Handler class which has a default constructor.
103 * Note that these Handlers may be created lazily, when they are
104 * first used.
105 *
Daniel Fuchs96da5b92014-11-24 17:02:37 +0100106 * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
107 * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
108 * this property is ignored. Otherwise it defaults to {@code true}. When the
109 * value is {@code true}, the handlers associated with the logger are guaranteed
110 * to be closed on {@linkplain #reset} and shutdown. This can be turned off
111 * by explicitly setting "&lt;logger&gt;.handlers.ensureCloseOnReset=false" in
112 * the configuration. Note that turning this property off causes the risk of
113 * introducing a resource leak, as the logger may get garbage collected before
114 * {@code reset()} is called, thus preventing its handlers from being closed
115 * on {@code reset()}. In that case it is the responsibility of the application
116 * to ensure that the handlers are closed before the logger is garbage
117 * collected.
118 *
J. Duke319a3b92007-12-01 00:00:00 +0000119 * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
120 * value. By default every logger calls its parent in addition to
121 * handling the logging message itself, this often result in messages
122 * being handled by the root logger as well. When setting this property
123 * to false a Handler needs to be configured for this logger otherwise
124 * no logging messages are delivered.
125 *
126 * <li>A property "config". This property is intended to allow
127 * arbitrary configuration code to be run. The property defines a
128 * whitespace or comma separated list of class names. A new instance will be
129 * created for each named class. The default constructor of each class
130 * may execute arbitrary code to update the logging configuration, such as
131 * setting logger levels, adding handlers, adding filters, etc.
132 * </ul>
133 * <p>
134 * Note that all classes loaded during LogManager configuration are
135 * first searched on the system class path before any user class path.
136 * That includes the LogManager class, any config classes, and any
137 * handler classes.
138 * <p>
139 * Loggers are organized into a naming hierarchy based on their
140 * dot separated names. Thus "a.b.c" is a child of "a.b", but
141 * "a.b1" and a.b2" are peers.
142 * <p>
143 * All properties whose names end with ".level" are assumed to define
144 * log levels for Loggers. Thus "foo.level" defines a log level for
145 * the logger called "foo" and (recursively) for any of its children
146 * in the naming hierarchy. Log Levels are applied in the order they
147 * are defined in the properties file. Thus level settings for child
148 * nodes in the tree should come after settings for their parents.
149 * The property name ".level" can be used to set the level for the
150 * root of the tree.
151 * <p>
152 * All methods on the LogManager object are multi-thread safe.
153 *
154 * @since 1.4
155*/
156
157public class LogManager {
158 // The global LogManager object
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200159 private static final LogManager manager;
J. Duke319a3b92007-12-01 00:00:00 +0000160
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +0100161 // 'props' is assigned within a lock but accessed without it.
162 // Declaring it volatile makes sure that another thread will not
163 // be able to see a partially constructed 'props' object.
164 // (seeing a partially constructed 'props' object can result in
165 // NPE being thrown in Hashtable.get(), because it leaves the door
166 // open for props.getProperties() to be called before the construcor
167 // of Hashtable is actually completed).
168 private volatile Properties props = new Properties();
J. Duke319a3b92007-12-01 00:00:00 +0000169 private final static Level defaultLevel = Level.INFO;
170
Mandy Chung7dde39f2012-11-26 22:49:06 -0800171 // LoggerContext for system loggers and user loggers
172 private final LoggerContext systemContext = new SystemLoggerContext();
Mandy Chung3243aaf2013-01-10 19:43:36 -0800173 private final LoggerContext userContext = new LoggerContext();
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200174 // non final field - make it volatile to make sure that other threads
175 // will see the new value once ensureLogManagerInitialized() has finished
176 // executing.
177 private volatile Logger rootLogger;
J. Duke319a3b92007-12-01 00:00:00 +0000178 // Have we done the primordial reading of the configuration file?
179 // (Must be done after a suitable amount of java.lang.System
180 // initialization has been done)
181 private volatile boolean readPrimordialConfiguration;
182 // Have we initialized global (root) handlers yet?
Peter Levart30bcd972015-05-17 10:38:36 +0200183 // This gets set to STATE_UNINITIALIZED in readConfiguration
184 private static final int
185 STATE_INITIALIZED = 0, // initial state
186 STATE_INITIALIZING = 1,
187 STATE_READING_CONFIG = 2,
188 STATE_UNINITIALIZED = 3,
189 STATE_SHUTDOWN = 4; // terminal state
190 private volatile int globalHandlersState; // = STATE_INITIALIZED;
191 // A concurrency lock for reset(), readConfiguration() and Cleaner.
192 private final ReentrantLock configurationLock = new ReentrantLock();
J. Duke319a3b92007-12-01 00:00:00 +0000193
Daniel Fuchs96da5b92014-11-24 17:02:37 +0100194 // This list contains the loggers for which some handlers have been
195 // explicitly configured in the configuration file.
196 // It prevents these loggers from being arbitrarily garbage collected.
197 private static final class CloseOnReset {
198 private final Logger logger;
199 private CloseOnReset(Logger ref) {
200 this.logger = Objects.requireNonNull(ref);
201 }
202 @Override
203 public boolean equals(Object other) {
204 return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;
205 }
206 @Override
207 public int hashCode() {
208 return System.identityHashCode(logger);
209 }
210 public Logger get() {
211 return logger;
212 }
213 public static CloseOnReset create(Logger logger) {
214 return new CloseOnReset(logger);
215 }
216 }
217 private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =
218 new CopyOnWriteArrayList<>();
219
220
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +0200221 private final Map<Object, Runnable> listeners =
222 Collections.synchronizedMap(new IdentityHashMap<>());
223
J. Duke319a3b92007-12-01 00:00:00 +0000224 static {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200225 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
226 @Override
227 public LogManager run() {
228 LogManager mgr = null;
229 String cname = null;
230 try {
231 cname = System.getProperty("java.util.logging.manager");
232 if (cname != null) {
233 try {
234 Class<?> clz = ClassLoader.getSystemClassLoader()
235 .loadClass(cname);
236 mgr = (LogManager) clz.newInstance();
237 } catch (ClassNotFoundException ex) {
238 Class<?> clz = Thread.currentThread()
239 .getContextClassLoader().loadClass(cname);
240 mgr = (LogManager) clz.newInstance();
J. Duke319a3b92007-12-01 00:00:00 +0000241 }
J. Duke319a3b92007-12-01 00:00:00 +0000242 }
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200243 } catch (Exception ex) {
244 System.err.println("Could not load Logmanager \"" + cname + "\"");
245 ex.printStackTrace();
J. Duke319a3b92007-12-01 00:00:00 +0000246 }
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200247 if (mgr == null) {
248 mgr = new LogManager();
249 }
250 return mgr;
251
252 }
253 });
J. Duke319a3b92007-12-01 00:00:00 +0000254 }
255
J. Duke319a3b92007-12-01 00:00:00 +0000256 // This private class is used as a shutdown hook.
257 // It does a "reset" to close all open handlers.
Chris Hegarty78ca5982016-04-18 20:58:30 +0100258 private class Cleaner extends Thread {
Andrew Bryginb56f0732009-02-04 14:06:18 +0300259
260 private Cleaner() {
Chris Hegarty78ca5982016-04-18 20:58:30 +0100261 super(null, null, "Logging-Cleaner", 0, false);
Andrew Bryginb56f0732009-02-04 14:06:18 +0300262 /* Set context class loader to null in order to avoid
263 * keeping a strong reference to an application classloader.
264 */
265 this.setContextClassLoader(null);
266 }
267
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200268 @Override
J. Duke319a3b92007-12-01 00:00:00 +0000269 public void run() {
270 // This is to ensure the LogManager.<clinit> is completed
271 // before synchronized block. Otherwise deadlocks are possible.
272 LogManager mgr = manager;
273
Peter Levart30bcd972015-05-17 10:38:36 +0200274 // set globalHandlersState to STATE_SHUTDOWN atomically so that
275 // no attempts are made to (re)initialize the handlers or (re)read
276 // the configuration again. This is terminal state.
277 configurationLock.lock();
278 globalHandlersState = STATE_SHUTDOWN;
279 configurationLock.unlock();
J. Duke319a3b92007-12-01 00:00:00 +0000280
281 // Do a reset to close all active handlers.
282 reset();
283 }
284 }
285
286
287 /**
288 * Protected constructor. This is protected so that container applications
289 * (such as J2EE containers) can subclass the object. It is non-public as
290 * it is intended that there only be one LogManager object, whose value is
Mandy Chunge4a502d2012-12-10 15:15:57 -0800291 * retrieved by calling LogManager.getLogManager.
J. Duke319a3b92007-12-01 00:00:00 +0000292 */
293 protected LogManager() {
Daniel Fuchs61e7dbb2013-10-07 12:09:22 +0200294 this(checkSubclassPermissions());
295 }
296
297 private LogManager(Void checked) {
298
J. Duke319a3b92007-12-01 00:00:00 +0000299 // Add a shutdown hook to close the global handlers.
300 try {
301 Runtime.getRuntime().addShutdownHook(new Cleaner());
302 } catch (IllegalStateException e) {
303 // If the VM is already shutting down,
304 // We do not need to register shutdownHook.
305 }
306 }
307
Daniel Fuchs61e7dbb2013-10-07 12:09:22 +0200308 private static Void checkSubclassPermissions() {
309 final SecurityManager sm = System.getSecurityManager();
310 if (sm != null) {
311 // These permission will be checked in the LogManager constructor,
312 // in order to register the Cleaner() thread as a shutdown hook.
313 // Check them here to avoid the penalty of constructing the object
314 // etc...
315 sm.checkPermission(new RuntimePermission("shutdownHooks"));
316 sm.checkPermission(new RuntimePermission("setContextClassLoader"));
317 }
318 return null;
319 }
320
J. Duke319a3b92007-12-01 00:00:00 +0000321 /**
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200322 * Lazy initialization: if this instance of manager is the global
323 * manager then this method will read the initial configuration and
324 * add the root logger and global logger by calling addLogger().
325 *
326 * Note that it is subtly different from what we do in LoggerContext.
327 * In LoggerContext we're patching up the logger context tree in order to add
328 * the root and global logger *to the context tree*.
329 *
330 * For this to work, addLogger() must have already have been called
331 * once on the LogManager instance for the default logger being
332 * added.
333 *
334 * This is why ensureLogManagerInitialized() needs to be called before
335 * any logger is added to any logger context.
336 *
337 */
338 private boolean initializedCalled = false;
339 private volatile boolean initializationDone = false;
340 final void ensureLogManagerInitialized() {
341 final LogManager owner = this;
342 if (initializationDone || owner != manager) {
343 // we don't want to do this twice, and we don't want to do
344 // this on private manager instances.
345 return;
346 }
347
348 // Maybe another thread has called ensureLogManagerInitialized()
349 // before us and is still executing it. If so we will block until
350 // the log manager has finished initialized, then acquire the monitor,
351 // notice that initializationDone is now true and return.
352 // Otherwise - we have come here first! We will acquire the monitor,
353 // see that initializationDone is still false, and perform the
354 // initialization.
355 //
Daniel Fuchs6af81452015-08-06 16:36:47 +0200356 configurationLock.lock();
357 try {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200358 // If initializedCalled is true it means that we're already in
359 // the process of initializing the LogManager in this thread.
360 // There has been a recursive call to ensureLogManagerInitialized().
361 final boolean isRecursiveInitialization = (initializedCalled == true);
362
363 assert initializedCalled || !initializationDone
364 : "Initialization can't be done if initialized has not been called!";
365
366 if (isRecursiveInitialization || initializationDone) {
367 // If isRecursiveInitialization is true it means that we're
368 // already in the process of initializing the LogManager in
369 // this thread. There has been a recursive call to
370 // ensureLogManagerInitialized(). We should not proceed as
371 // it would lead to infinite recursion.
372 //
373 // If initializationDone is true then it means the manager
374 // has finished initializing; just return: we're done.
375 return;
376 }
377 // Calling addLogger below will in turn call requiresDefaultLogger()
378 // which will call ensureLogManagerInitialized().
379 // We use initializedCalled to break the recursion.
380 initializedCalled = true;
381 try {
382 AccessController.doPrivileged(new PrivilegedAction<Object>() {
383 @Override
384 public Object run() {
385 assert rootLogger == null;
386 assert initializedCalled && !initializationDone;
387
388 // Read configuration.
389 owner.readPrimordialConfiguration();
390
391 // Create and retain Logger for the root of the namespace.
392 owner.rootLogger = owner.new RootLogger();
393 owner.addLogger(owner.rootLogger);
Daniel Fuchs39db63b2013-12-22 11:20:07 +0100394 if (!owner.rootLogger.isLevelInitialized()) {
395 owner.rootLogger.setLevel(defaultLevel);
396 }
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200397
398 // Adding the global Logger.
399 // Do not call Logger.getGlobal() here as this might trigger
400 // subtle inter-dependency issues.
401 @SuppressWarnings("deprecation")
402 final Logger global = Logger.global;
403
404 // Make sure the global logger will be registered in the
405 // global manager
406 owner.addLogger(global);
407 return null;
408 }
409 });
410 } finally {
411 initializationDone = true;
412 }
Daniel Fuchs6af81452015-08-06 16:36:47 +0200413 } finally {
414 configurationLock.unlock();
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200415 }
416 }
417
418 /**
Joe Darcya8bb2692013-06-27 12:24:26 -0700419 * Returns the global LogManager object.
420 * @return the global LogManager object
J. Duke319a3b92007-12-01 00:00:00 +0000421 */
422 public static LogManager getLogManager() {
423 if (manager != null) {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200424 manager.ensureLogManagerInitialized();
J. Duke319a3b92007-12-01 00:00:00 +0000425 }
426 return manager;
427 }
428
Daniel Fuchs6af81452015-08-06 16:36:47 +0200429 private void readPrimordialConfiguration() { // must be called while holding configurationLock
J. Duke319a3b92007-12-01 00:00:00 +0000430 if (!readPrimordialConfiguration) {
Daniel Fuchs6af81452015-08-06 16:36:47 +0200431 // If System.in/out/err are null, it's a good
432 // indication that we're still in the
433 // bootstrapping phase
434 if (System.out == null) {
435 return;
436 }
437 readPrimordialConfiguration = true;
438 try {
439 readConfiguration();
Mandy Chung7dde39f2012-11-26 22:49:06 -0800440
Daniel Fuchs6af81452015-08-06 16:36:47 +0200441 // Platform loggers begin to delegate to java.util.logging.Logger
Daniel Fuchsbd8942b2015-11-20 19:26:16 +0100442 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
443
Daniel Fuchs6af81452015-08-06 16:36:47 +0200444 } catch (Exception ex) {
445 assert false : "Exception raised while reading logging configuration: " + ex;
J. Duke319a3b92007-12-01 00:00:00 +0000446 }
447 }
448 }
449
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200450 // LoggerContext maps from AppContext
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200451 private WeakHashMap<Object, LoggerContext> contextsMap = null;
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200452
Mandy Chung7dde39f2012-11-26 22:49:06 -0800453 // Returns the LoggerContext for the user code (i.e. application or AppContext).
454 // Loggers are isolated from each AppContext.
Mandy Chung3243aaf2013-01-10 19:43:36 -0800455 private LoggerContext getUserContext() {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800456 LoggerContext context = null;
Daniel D. Daugherty792f2aa2011-05-16 12:57:40 -0700457
Mandy Chung7dde39f2012-11-26 22:49:06 -0800458 SecurityManager sm = System.getSecurityManager();
459 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
460 if (sm != null && javaAwtAccess != null) {
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200461 // for each applet, it has its own LoggerContext isolated from others
Daniel Fuchs7e7e10e2014-12-05 12:20:51 +0100462 final Object ecx = javaAwtAccess.getAppletContext();
463 if (ecx != null) {
464 synchronized (javaAwtAccess) {
465 // find the AppContext of the applet code
466 // will be null if we are in the main app context.
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200467 if (contextsMap == null) {
468 contextsMap = new WeakHashMap<>();
469 }
470 context = contextsMap.get(ecx);
Leonid Romanovd76b61b2013-03-21 02:13:49 +0400471 if (context == null) {
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200472 // Create a new LoggerContext for the applet.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200473 context = new LoggerContext();
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200474 contextsMap.put(ecx, context);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800475 }
Mandy Chung7dde39f2012-11-26 22:49:06 -0800476 }
477 }
J. Duke319a3b92007-12-01 00:00:00 +0000478 }
Daniel Fuchsa0c3d882013-09-04 16:22:22 +0200479 // for standalone app, return userContext
Leonid Romanovd76b61b2013-03-21 02:13:49 +0400480 return context != null ? context : userContext;
J. Duke319a3b92007-12-01 00:00:00 +0000481 }
482
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200483 // The system context.
484 final LoggerContext getSystemContext() {
485 return systemContext;
486 }
487
Mandy Chung7dde39f2012-11-26 22:49:06 -0800488 private List<LoggerContext> contexts() {
489 List<LoggerContext> cxs = new ArrayList<>();
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200490 cxs.add(getSystemContext());
Mandy Chung7dde39f2012-11-26 22:49:06 -0800491 cxs.add(getUserContext());
492 return cxs;
493 }
494
J. Duke319a3b92007-12-01 00:00:00 +0000495 // Find or create a specified logger instance. If a logger has
496 // already been created with the given name it is returned.
497 // Otherwise a new logger instance is created and registered
498 // in the LogManager global namespace.
J. Duke319a3b92007-12-01 00:00:00 +0000499 // This method will always return a non-null Logger object.
500 // Synchronization is not required here. All synchronization for
501 // adding a new Logger object is handled by addLogger().
Mandy Chung3243aaf2013-01-10 19:43:36 -0800502 //
503 // This method must delegate to the LogManager implementation to
504 // add a new Logger or return the one that has been added previously
505 // as a LogManager subclass may override the addLogger, getLogger,
506 // readConfiguration, and other methods.
Jim Gishb0c93f42013-05-16 11:19:00 -0400507 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200508 final Module module = caller == null ? null : caller.getModule();
509 return demandLogger(name, resourceBundleName, module);
510 }
511
512 Logger demandLogger(String name, String resourceBundleName, Module module) {
J. Duke319a3b92007-12-01 00:00:00 +0000513 Logger result = getLogger(name);
514 if (result == null) {
515 // only allocate the new logger once
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200516 Logger newLogger = new Logger(name, resourceBundleName,
517 module == null ? null : module, this, false);
J. Duke319a3b92007-12-01 00:00:00 +0000518 do {
519 if (addLogger(newLogger)) {
520 // We successfully added the new Logger that we
521 // created above so return it without refetching.
522 return newLogger;
523 }
524
525 // We didn't add the new Logger that we created above
526 // because another thread added a Logger with the same
527 // name after our null check above and before our call
528 // to addLogger(). We have to refetch the Logger because
529 // addLogger() returns a boolean instead of the Logger
530 // reference itself. However, if the thread that created
531 // the other Logger is not holding a strong reference to
532 // the other Logger, then it is possible for the other
533 // Logger to be GC'ed after we saw it in addLogger() and
534 // before we can refetch it. If it has been GC'ed then
535 // we'll just loop around and try again.
536 result = getLogger(name);
537 } while (result == null);
538 }
539 return result;
540 }
541
Daniel Fuchs38a04dd2015-06-16 12:15:54 +0200542 Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200543 final Module module = caller == null ? null : caller.getModule();
544 return demandSystemLogger(name, resourceBundleName, module);
545 }
546
547 Logger demandSystemLogger(String name, String resourceBundleName, Module module) {
Mandy Chunge84e88f2013-02-05 22:56:47 -0800548 // Add a system logger in the system context's namespace
Daniel Fuchs38a04dd2015-06-16 12:15:54 +0200549 final Logger sysLogger = getSystemContext()
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200550 .demandLogger(name, resourceBundleName, module);
J. Duke319a3b92007-12-01 00:00:00 +0000551
Mandy Chunge84e88f2013-02-05 22:56:47 -0800552 // Add the system logger to the LogManager's namespace if not exist
553 // so that there is only one single logger of the given name.
554 // System loggers are visible to applications unless a logger of
555 // the same name has been added.
556 Logger logger;
557 do {
558 // First attempt to call addLogger instead of getLogger
559 // This would avoid potential bug in custom LogManager.getLogger
560 // implementation that adds a logger if does not exist
561 if (addLogger(sysLogger)) {
562 // successfully added the new system logger
563 logger = sysLogger;
564 } else {
565 logger = getLogger(name);
J. Duke319a3b92007-12-01 00:00:00 +0000566 }
Mandy Chunge84e88f2013-02-05 22:56:47 -0800567 } while (logger == null);
568
569 // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
Daniel Fuchs1276d6b2014-01-16 17:49:40 +0100570 if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) {
Mandy Chunge84e88f2013-02-05 22:56:47 -0800571 // if logger already exists but handlers not set
572 final Logger l = logger;
573 AccessController.doPrivileged(new PrivilegedAction<Void>() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200574 @Override
Mandy Chunge84e88f2013-02-05 22:56:47 -0800575 public Void run() {
Daniel Fuchs1276d6b2014-01-16 17:49:40 +0100576 for (Handler hdl : l.accessCheckedHandlers()) {
Mandy Chunge84e88f2013-02-05 22:56:47 -0800577 sysLogger.addHandler(hdl);
578 }
579 return null;
580 }
581 });
582 }
583 return sysLogger;
Mandy Chung3243aaf2013-01-10 19:43:36 -0800584 }
585
586 // LoggerContext maintains the logger namespace per context.
587 // The default LogManager implementation has one system context and user
588 // context. The system context is used to maintain the namespace for
589 // all system loggers and is queried by the system code. If a system logger
590 // doesn't exist in the user context, it'll also be added to the user context.
591 // The user context is queried by the user code and all other loggers are
592 // added in the user context.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200593 class LoggerContext {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800594 // Table of named Loggers that maps names to Loggers.
Daniel Fuchsce950c02015-04-02 16:24:46 +0200595 private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =
596 new ConcurrentHashMap<>();
Mandy Chung7dde39f2012-11-26 22:49:06 -0800597 // Tree of named Loggers
598 private final LogNode root;
Mandy Chung7dde39f2012-11-26 22:49:06 -0800599 private LoggerContext() {
600 this.root = new LogNode(null, this);
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200601 }
602
603
604 // Tells whether default loggers are required in this context.
605 // If true, the default loggers will be lazily added.
606 final boolean requiresDefaultLoggers() {
607 final boolean requiresDefaultLoggers = (getOwner() == manager);
608 if (requiresDefaultLoggers) {
609 getOwner().ensureLogManagerInitialized();
610 }
611 return requiresDefaultLoggers;
612 }
613
614 // This context's LogManager.
615 final LogManager getOwner() {
616 return LogManager.this;
617 }
618
619 // This context owner's root logger, which if not null, and if
620 // the context requires default loggers, will be added to the context
621 // logger's tree.
622 final Logger getRootLogger() {
623 return getOwner().rootLogger;
624 }
625
626 // The global logger, which if not null, and if
627 // the context requires default loggers, will be added to the context
628 // logger's tree.
629 final Logger getGlobalLogger() {
Daniel Fuchs769affa2014-03-12 20:18:47 +0100630 @SuppressWarnings("deprecation") // avoids initialization cycles.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200631 final Logger global = Logger.global;
632 return global;
Mandy Chung7dde39f2012-11-26 22:49:06 -0800633 }
634
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200635 Logger demandLogger(String name, String resourceBundleName, Module module) {
Mandy Chung3243aaf2013-01-10 19:43:36 -0800636 // a LogManager subclass may have its own implementation to add and
637 // get a Logger. So delegate to the LogManager to do the work.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200638 final LogManager owner = getOwner();
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200639 return owner.demandLogger(name, resourceBundleName, module);
Mandy Chung3243aaf2013-01-10 19:43:36 -0800640 }
641
Daniel Fuchs67764332013-07-02 11:30:31 +0200642
643 // Due to subtle deadlock issues getUserContext() no longer
644 // calls addLocalLogger(rootLogger);
645 // Therefore - we need to add the default loggers later on.
646 // Checks that the context is properly initialized
647 // This is necessary before calling e.g. find(name)
648 // or getLoggerNames()
649 //
650 private void ensureInitialized() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200651 if (requiresDefaultLoggers()) {
Daniel Fuchs67764332013-07-02 11:30:31 +0200652 // Ensure that the root and global loggers are set.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200653 ensureDefaultLogger(getRootLogger());
654 ensureDefaultLogger(getGlobalLogger());
Daniel Fuchs67764332013-07-02 11:30:31 +0200655 }
656 }
657
658
Daniel Fuchsce950c02015-04-02 16:24:46 +0200659 Logger findLogger(String name) {
660 // Attempt to find logger without locking.
Mandy Chung7dde39f2012-11-26 22:49:06 -0800661 LoggerWeakRef ref = namedLoggers.get(name);
Daniel Fuchsce950c02015-04-02 16:24:46 +0200662 Logger logger = ref == null ? null : ref.get();
663
664 // if logger is not null, then we can return it right away.
665 // if name is "" or "global" and logger is null
666 // we need to fall through and check that this context is
667 // initialized.
668 // if ref is not null and logger is null we also need to
669 // fall through.
670 if (logger != null || (ref == null && !name.isEmpty()
671 && !name.equals(Logger.GLOBAL_LOGGER_NAME))) {
672 return logger;
J. Duke319a3b92007-12-01 00:00:00 +0000673 }
Daniel Fuchsce950c02015-04-02 16:24:46 +0200674
675 // We either found a stale reference, or we were looking for
676 // "" or "global" and didn't find them.
677 // Make sure context is initialized (has the default loggers),
678 // and look up again, cleaning the stale reference if it hasn't
679 // been cleaned up in between. All this needs to be done inside
680 // a synchronized block.
681 synchronized(this) {
682 // ensure that this context is properly initialized before
683 // looking for loggers.
684 ensureInitialized();
685 ref = namedLoggers.get(name);
686 if (ref == null) {
687 return null;
688 }
689 logger = ref.get();
690 if (logger == null) {
691 // The namedLoggers map holds stale weak reference
692 // to a logger which has been GC-ed.
693 ref.dispose();
694 }
695 return logger;
Mandy Chung7dde39f2012-11-26 22:49:06 -0800696 }
Mandy Chung7dde39f2012-11-26 22:49:06 -0800697 }
698
Daniel Fuchs67764332013-07-02 11:30:31 +0200699 // This method is called before adding a logger to the
700 // context.
701 // 'logger' is the context that will be added.
702 // This method will ensure that the defaults loggers are added
703 // before adding 'logger'.
704 //
705 private void ensureAllDefaultLoggers(Logger logger) {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200706 if (requiresDefaultLoggers()) {
Daniel Fuchs67764332013-07-02 11:30:31 +0200707 final String name = logger.getName();
708 if (!name.isEmpty()) {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200709 ensureDefaultLogger(getRootLogger());
710 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
711 ensureDefaultLogger(getGlobalLogger());
712 }
Daniel Fuchs67764332013-07-02 11:30:31 +0200713 }
Leonid Romanovd76b61b2013-03-21 02:13:49 +0400714 }
715 }
716
Daniel Fuchs67764332013-07-02 11:30:31 +0200717 private void ensureDefaultLogger(Logger logger) {
718 // Used for lazy addition of root logger and global logger
719 // to a LoggerContext.
720
721 // This check is simple sanity: we do not want that this
722 // method be called for anything else than Logger.global
723 // or owner.rootLogger.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200724 if (!requiresDefaultLoggers() || logger == null
Daniel Fuchs769affa2014-03-12 20:18:47 +0100725 || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
Daniel Fuchs67764332013-07-02 11:30:31 +0200726
727 // the case where we have a non null logger which is neither
728 // Logger.global nor manager.rootLogger indicates a serious
729 // issue - as ensureDefaultLogger should never be called
730 // with any other loggers than one of these two (or null - if
731 // e.g manager.rootLogger is not yet initialized)...
732 assert logger == null;
733
734 return;
735 }
736
737 // Adds the logger if it's not already there.
738 if (!namedLoggers.containsKey(logger.getName())) {
739 // It is important to prevent addLocalLogger to
740 // call ensureAllDefaultLoggers when we're in the process
741 // off adding one of those default loggers - as this would
742 // immediately cause a stack overflow.
743 // Therefore we must pass addDefaultLoggersIfNeeded=false,
744 // even if requiresDefaultLoggers is true.
745 addLocalLogger(logger, false);
746 }
747 }
748
749 boolean addLocalLogger(Logger logger) {
750 // no need to add default loggers if it's not required
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200751 return addLocalLogger(logger, requiresDefaultLoggers());
Daniel Fuchs67764332013-07-02 11:30:31 +0200752 }
753
Mandy Chung3243aaf2013-01-10 19:43:36 -0800754 // Add a logger to this context. This method will only set its level
755 // and process parent loggers. It doesn't set its handlers.
Daniel Fuchs67764332013-07-02 11:30:31 +0200756 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
757 // addDefaultLoggersIfNeeded serves to break recursion when adding
758 // default loggers. If we're adding one of the default loggers
759 // (we're being called from ensureDefaultLogger()) then
760 // addDefaultLoggersIfNeeded will be false: we don't want to
761 // call ensureAllDefaultLoggers again.
762 //
763 // Note: addDefaultLoggersIfNeeded can also be false when
764 // requiresDefaultLoggers is false - since calling
765 // ensureAllDefaultLoggers would have no effect in this case.
766 if (addDefaultLoggersIfNeeded) {
767 ensureAllDefaultLoggers(logger);
768 }
Leonid Romanovd76b61b2013-03-21 02:13:49 +0400769
Mandy Chung7dde39f2012-11-26 22:49:06 -0800770 final String name = logger.getName();
771 if (name == null) {
772 throw new NullPointerException();
773 }
Mandy Chung7dde39f2012-11-26 22:49:06 -0800774 LoggerWeakRef ref = namedLoggers.get(name);
775 if (ref != null) {
776 if (ref.get() == null) {
Jim Gish8f316cc2013-04-19 16:50:10 -0700777 // It's possible that the Logger was GC'ed after a
Mandy Chung7dde39f2012-11-26 22:49:06 -0800778 // drainLoggerRefQueueBounded() call above so allow
779 // a new one to be registered.
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +0100780 ref.dispose();
Mandy Chung7dde39f2012-11-26 22:49:06 -0800781 } else {
782 // We already have a registered logger with the given name.
783 return false;
784 }
785 }
786
787 // We're adding a new logger.
788 // Note that we are creating a weak reference here.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200789 final LogManager owner = getOwner();
790 logger.setLogManager(owner);
791 ref = owner.new LoggerWeakRef(logger);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800792
Daniel Fuchs2f8b3bf2013-10-21 12:00:58 +0200793 // Apply any initial level defined for the new logger, unless
794 // the logger's level is already initialized
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200795 Level level = owner.getLevelProperty(name + ".level", null);
Daniel Fuchs2f8b3bf2013-10-21 12:00:58 +0200796 if (level != null && !logger.isLevelInitialized()) {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800797 doSetLevel(logger, level);
798 }
799
Mandy Chung3243aaf2013-01-10 19:43:36 -0800800 // instantiation of the handler is done in the LogManager.addLogger
801 // implementation as a handler class may be only visible to LogManager
802 // subclass for the custom log manager case
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200803 processParentHandlers(logger, name, VisitedLoggers.NEVER);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800804
805 // Find the new node and its parent.
806 LogNode node = getNode(name);
807 node.loggerRef = ref;
808 Logger parent = null;
809 LogNode nodep = node.parent;
810 while (nodep != null) {
811 LoggerWeakRef nodeRef = nodep.loggerRef;
812 if (nodeRef != null) {
813 parent = nodeRef.get();
814 if (parent != null) {
815 break;
816 }
817 }
818 nodep = nodep.parent;
819 }
820
821 if (parent != null) {
822 doSetParent(logger, parent);
823 }
824 // Walk over the children and tell them we are their new parent.
825 node.walkAndSetParent(logger);
826 // new LogNode is ready so tell the LoggerWeakRef about it
827 ref.setNode(node);
Daniel Fuchsce950c02015-04-02 16:24:46 +0200828
829 // Do not publish 'ref' in namedLoggers before the logger tree
830 // is fully updated - because the named logger will be visible as
831 // soon as it is published in namedLoggers (findLogger takes
832 // benefit of the ConcurrentHashMap implementation of namedLoggers
833 // to avoid synchronizing on retrieval when that is possible).
834 namedLoggers.put(name, ref);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800835 return true;
836 }
837
Daniel Fuchsce950c02015-04-02 16:24:46 +0200838 void removeLoggerRef(String name, LoggerWeakRef ref) {
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +0100839 namedLoggers.remove(name, ref);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800840 }
841
842 synchronized Enumeration<String> getLoggerNames() {
Daniel Fuchs67764332013-07-02 11:30:31 +0200843 // ensure that this context is properly initialized before
844 // returning logger names.
845 ensureInitialized();
Daniel Fuchsce950c02015-04-02 16:24:46 +0200846 return Collections.enumeration(namedLoggers.keySet());
Mandy Chung7dde39f2012-11-26 22:49:06 -0800847 }
848
Mandy Chung7dde39f2012-11-26 22:49:06 -0800849 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
850 // parents have levels or handlers defined, make sure they are instantiated.
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200851 private void processParentHandlers(final Logger logger, final String name,
852 Predicate<Logger> visited) {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200853 final LogManager owner = getOwner();
Mandy Chung3243aaf2013-01-10 19:43:36 -0800854 AccessController.doPrivileged(new PrivilegedAction<Void>() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200855 @Override
Mandy Chung3243aaf2013-01-10 19:43:36 -0800856 public Void run() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200857 if (logger != owner.rootLogger) {
858 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
Mandy Chung3243aaf2013-01-10 19:43:36 -0800859 if (!useParent) {
860 logger.setUseParentHandlers(false);
861 }
862 }
863 return null;
864 }
865 });
866
Mandy Chung7dde39f2012-11-26 22:49:06 -0800867 int ix = 1;
868 for (;;) {
Otavio Goncalves de Santana8f0e6e32014-05-30 15:46:12 -0400869 int ix2 = name.indexOf('.', ix);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800870 if (ix2 < 0) {
871 break;
872 }
873 String pname = name.substring(0, ix2);
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200874 if (owner.getProperty(pname + ".level") != null ||
875 owner.getProperty(pname + ".handlers") != null) {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800876 // This pname has a level/handlers definition.
877 // Make sure it exists.
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200878 if (visited.test(demandLogger(pname, null, null))) {
879 break;
880 }
Mandy Chung7dde39f2012-11-26 22:49:06 -0800881 }
882 ix = ix2+1;
883 }
884 }
885
886 // Gets a node in our tree of logger nodes.
887 // If necessary, create it.
888 LogNode getNode(String name) {
889 if (name == null || name.equals("")) {
890 return root;
891 }
892 LogNode node = root;
893 while (name.length() > 0) {
Otavio Goncalves de Santana8f0e6e32014-05-30 15:46:12 -0400894 int ix = name.indexOf('.');
Mandy Chung7dde39f2012-11-26 22:49:06 -0800895 String head;
896 if (ix > 0) {
897 head = name.substring(0, ix);
898 name = name.substring(ix + 1);
899 } else {
900 head = name;
901 name = "";
902 }
903 if (node.children == null) {
904 node.children = new HashMap<>();
905 }
906 LogNode child = node.children.get(head);
907 if (child == null) {
908 child = new LogNode(node, this);
909 node.children.put(head, child);
910 }
911 node = child;
912 }
913 return node;
914 }
915 }
916
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200917 final class SystemLoggerContext extends LoggerContext {
Mandy Chung3243aaf2013-01-10 19:43:36 -0800918 // Add a system logger in the system context's namespace as well as
919 // in the LogManager's namespace if not exist so that there is only
920 // one single logger of the given name. System loggers are visible
921 // to applications unless a logger of the same name has been added.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200922 @Override
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200923 Logger demandLogger(String name, String resourceBundleName,
924 Module module) {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800925 Logger result = findLogger(name);
926 if (result == null) {
Mandy Chung3243aaf2013-01-10 19:43:36 -0800927 // only allocate the new system logger once
Daniel Fuchs1e0d1452016-04-27 18:04:16 +0200928 Logger newLogger = new Logger(name, resourceBundleName,
929 module, getOwner(), true);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800930 do {
Mandy Chung3243aaf2013-01-10 19:43:36 -0800931 if (addLocalLogger(newLogger)) {
Mandy Chung7dde39f2012-11-26 22:49:06 -0800932 // We successfully added the new Logger that we
933 // created above so return it without refetching.
Mandy Chung3243aaf2013-01-10 19:43:36 -0800934 result = newLogger;
935 } else {
936 // We didn't add the new Logger that we created above
937 // because another thread added a Logger with the same
938 // name after our null check above and before our call
939 // to addLogger(). We have to refetch the Logger because
940 // addLogger() returns a boolean instead of the Logger
941 // reference itself. However, if the thread that created
942 // the other Logger is not holding a strong reference to
943 // the other Logger, then it is possible for the other
944 // Logger to be GC'ed after we saw it in addLogger() and
945 // before we can refetch it. If it has been GC'ed then
946 // we'll just loop around and try again.
947 result = findLogger(name);
Mandy Chung7dde39f2012-11-26 22:49:06 -0800948 }
Mandy Chung7dde39f2012-11-26 22:49:06 -0800949 } while (result == null);
950 }
951 return result;
J. Duke319a3b92007-12-01 00:00:00 +0000952 }
953 }
954
955 // Add new per logger handlers.
956 // We need to raise privilege here. All our decisions will
957 // be made based on the logging configuration, which can
958 // only be modified by trusted code.
959 private void loadLoggerHandlers(final Logger logger, final String name,
Mandy Chung3243aaf2013-01-10 19:43:36 -0800960 final String handlersPropertyName)
961 {
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200962 AccessController.doPrivileged(new PrivilegedAction<Void>() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +0200963 @Override
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200964 public Void run() {
965 setLoggerHandlers(logger, name, handlersPropertyName,
966 createLoggerHandlers(name, handlersPropertyName));
J. Duke319a3b92007-12-01 00:00:00 +0000967 return null;
Mandy Chung3243aaf2013-01-10 19:43:36 -0800968 }
969 });
J. Duke319a3b92007-12-01 00:00:00 +0000970 }
971
Daniel Fuchsbd69fa02015-10-12 20:13:22 +0200972 private void setLoggerHandlers(final Logger logger, final String name,
973 final String handlersPropertyName,
974 List<Handler> handlers)
975 {
976 final boolean ensureCloseOnReset = ! handlers.isEmpty()
977 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
978 int count = 0;
979 for (Handler hdl : handlers) {
980 logger.addHandler(hdl);
981 if (++count == 1 && ensureCloseOnReset) {
982 // add this logger to the closeOnResetLoggers list.
983 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
984 }
985 }
986 }
987
988 private List<Handler> createLoggerHandlers(final String name, final String handlersPropertyName)
989 {
990 String names[] = parseClassNames(handlersPropertyName);
991 List<Handler> handlers = new ArrayList<>(names.length);
992 for (String type : names) {
993 try {
994 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
995 Handler hdl = (Handler) clz.newInstance();
996 // Check if there is a property defining the
997 // this handler's level.
998 String levs = getProperty(type + ".level");
999 if (levs != null) {
1000 Level l = Level.findLevel(levs);
1001 if (l != null) {
1002 hdl.setLevel(l);
1003 } else {
1004 // Probably a bad level. Drop through.
1005 System.err.println("Can't set level for " + type);
1006 }
1007 }
1008 // Add this Handler to the logger
1009 handlers.add(hdl);
1010 } catch (Exception ex) {
1011 System.err.println("Can't load log handler \"" + type + "\"");
1012 System.err.println("" + ex);
1013 ex.printStackTrace();
1014 }
1015 }
1016
1017 return handlers;
1018 }
1019
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001020
1021 // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1022 // that have been GC'ed.
1023 private final ReferenceQueue<Logger> loggerRefQueue
Stuart Marks6e7cb442010-12-20 13:47:04 -08001024 = new ReferenceQueue<>();
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001025
1026 // Package-level inner class.
1027 // Helper class for managing WeakReferences to Logger objects.
1028 //
1029 // LogManager.namedLoggers
1030 // - has weak references to all named Loggers
1031 // - namedLoggers keeps the LoggerWeakRef objects for the named
1032 // Loggers around until we can deal with the book keeping for
1033 // the named Logger that is being GC'ed.
1034 // LogManager.LogNode.loggerRef
1035 // - has a weak reference to a named Logger
1036 // - the LogNode will also keep the LoggerWeakRef objects for
1037 // the named Loggers around; currently LogNodes never go away.
1038 // Logger.kids
1039 // - has a weak reference to each direct child Logger; this
1040 // includes anonymous and named Loggers
1041 // - anonymous Loggers are always children of the rootLogger
1042 // which is a strong reference; rootLogger.kids keeps the
1043 // LoggerWeakRef objects for the anonymous Loggers around
1044 // until we can deal with the book keeping.
1045 //
1046 final class LoggerWeakRef extends WeakReference<Logger> {
1047 private String name; // for namedLoggers cleanup
1048 private LogNode node; // for loggerRef cleanup
1049 private WeakReference<Logger> parentRef; // for kids cleanup
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +01001050 private boolean disposed = false; // avoid calling dispose twice
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001051
1052 LoggerWeakRef(Logger logger) {
1053 super(logger, loggerRefQueue);
1054
1055 name = logger.getName(); // save for namedLoggers cleanup
1056 }
1057
1058 // dispose of this LoggerWeakRef object
1059 void dispose() {
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +01001060 // Avoid calling dispose twice. When a Logger is gc'ed, its
1061 // LoggerWeakRef will be enqueued.
1062 // However, a new logger of the same name may be added (or looked
1063 // up) before the queue is drained. When that happens, dispose()
1064 // will be called by addLocalLogger() or findLogger().
1065 // Later when the queue is drained, dispose() will be called again
1066 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
1067 // avoids processing the data twice (even though the code should
1068 // now be reentrant).
1069 synchronized(this) {
1070 // Note to maintainers:
1071 // Be careful not to call any method that tries to acquire
1072 // another lock from within this block - as this would surely
1073 // lead to deadlocks, given that dispose() can be called by
1074 // multiple threads, and from within different synchronized
1075 // methods/blocks.
1076 if (disposed) return;
1077 disposed = true;
1078 }
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001079
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +01001080 final LogNode n = node;
1081 if (n != null) {
1082 // n.loggerRef can only be safely modified from within
1083 // a lock on LoggerContext. removeLoggerRef is already
1084 // synchronized on LoggerContext so calling
1085 // n.context.removeLoggerRef from within this lock is safe.
1086 synchronized (n.context) {
1087 // if we have a LogNode, then we were a named Logger
1088 // so clear namedLoggers weak ref to us
1089 n.context.removeLoggerRef(name, this);
1090 name = null; // clear our ref to the Logger's name
1091
1092 // LogNode may have been reused - so only clear
1093 // LogNode.loggerRef if LogNode.loggerRef == this
1094 if (n.loggerRef == this) {
1095 n.loggerRef = null; // clear LogNode's weak ref to us
1096 }
1097 node = null; // clear our ref to LogNode
1098 }
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001099 }
1100
1101 if (parentRef != null) {
1102 // this LoggerWeakRef has or had a parent Logger
1103 Logger parent = parentRef.get();
1104 if (parent != null) {
1105 // the parent Logger is still there so clear the
1106 // parent Logger's weak ref to us
1107 parent.removeChildLogger(this);
1108 }
1109 parentRef = null; // clear our weak ref to the parent Logger
1110 }
1111 }
1112
1113 // set the node field to the specified value
1114 void setNode(LogNode node) {
1115 this.node = node;
1116 }
1117
1118 // set the parentRef field to the specified value
1119 void setParentRef(WeakReference<Logger> parentRef) {
1120 this.parentRef = parentRef;
1121 }
1122 }
1123
1124 // Package-level method.
1125 // Drain some Logger objects that have been GC'ed.
1126 //
1127 // drainLoggerRefQueueBounded() is called by addLogger() below
1128 // and by Logger.getAnonymousLogger(String) so we'll drain up to
1129 // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
1130 //
1131 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1132 // us about a 50/50 mix in increased weak ref counts versus
1133 // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1134 // Here are stats for cleaning up sets of 400 anonymous Loggers:
1135 // - test duration 1 minute
1136 // - sample size of 125 sets of 400
1137 // - average: 1.99 ms
1138 // - minimum: 0.57 ms
1139 // - maximum: 25.3 ms
1140 //
1141 // The same config gives us a better decreased weak ref count
1142 // than increased weak ref count in the LoggerWeakRefLeak test.
1143 // Here are stats for cleaning up sets of 400 named Loggers:
1144 // - test duration 2 minutes
1145 // - sample size of 506 sets of 400
1146 // - average: 0.57 ms
1147 // - minimum: 0.02 ms
1148 // - maximum: 10.9 ms
1149 //
1150 private final static int MAX_ITERATIONS = 400;
Daniel Fuchs2c97b5c2013-12-04 01:58:37 +01001151 final void drainLoggerRefQueueBounded() {
Jeremy Manson9a0c9852010-06-22 10:54:59 -07001152 for (int i = 0; i < MAX_ITERATIONS; i++) {
1153 if (loggerRefQueue == null) {
1154 // haven't finished loading LogManager yet
1155 break;
1156 }
1157
1158 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1159 if (ref == null) {
1160 break;
1161 }
1162 // a Logger object has been GC'ed so clean it up
1163 ref.dispose();
1164 }
1165 }
1166
J. Duke319a3b92007-12-01 00:00:00 +00001167 /**
1168 * Add a named logger. This does nothing and returns false if a logger
1169 * with the same name is already registered.
1170 * <p>
1171 * The Logger factory methods call this method to register each
1172 * newly created Logger.
1173 * <p>
1174 * The application should retain its own reference to the Logger
1175 * object to avoid it being garbage collected. The LogManager
1176 * may only retain a weak reference.
1177 *
1178 * @param logger the new logger.
1179 * @return true if the argument logger was registered successfully,
1180 * false if a logger of that name already exists.
1181 * @exception NullPointerException if the logger name is null.
1182 */
Mandy Chung7dde39f2012-11-26 22:49:06 -08001183 public boolean addLogger(Logger logger) {
J. Duke319a3b92007-12-01 00:00:00 +00001184 final String name = logger.getName();
1185 if (name == null) {
1186 throw new NullPointerException();
1187 }
Jim Gish8f316cc2013-04-19 16:50:10 -07001188 drainLoggerRefQueueBounded();
Mandy Chung3243aaf2013-01-10 19:43:36 -08001189 LoggerContext cx = getUserContext();
1190 if (cx.addLocalLogger(logger)) {
1191 // Do we have a per logger handler too?
1192 // Note: this will add a 200ms penalty
1193 loadLoggerHandlers(logger, name, name + ".handlers");
1194 return true;
1195 } else {
Mandy Chung7dde39f2012-11-26 22:49:06 -08001196 return false;
J. Duke319a3b92007-12-01 00:00:00 +00001197 }
J. Duke319a3b92007-12-01 00:00:00 +00001198 }
1199
J. Duke319a3b92007-12-01 00:00:00 +00001200 // Private method to set a level on a logger.
1201 // If necessary, we raise privilege before doing the call.
1202 private static void doSetLevel(final Logger logger, final Level level) {
1203 SecurityManager sm = System.getSecurityManager();
1204 if (sm == null) {
1205 // There is no security manager, so things are easy.
1206 logger.setLevel(level);
1207 return;
1208 }
1209 // There is a security manager. Raise privilege before
1210 // calling setLevel.
1211 AccessController.doPrivileged(new PrivilegedAction<Object>() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02001212 @Override
J. Duke319a3b92007-12-01 00:00:00 +00001213 public Object run() {
1214 logger.setLevel(level);
1215 return null;
1216 }});
1217 }
1218
J. Duke319a3b92007-12-01 00:00:00 +00001219 // Private method to set a parent on a logger.
1220 // If necessary, we raise privilege before doing the setParent call.
1221 private static void doSetParent(final Logger logger, final Logger parent) {
1222 SecurityManager sm = System.getSecurityManager();
1223 if (sm == null) {
1224 // There is no security manager, so things are easy.
1225 logger.setParent(parent);
1226 return;
1227 }
1228 // There is a security manager. Raise privilege before
1229 // calling setParent.
1230 AccessController.doPrivileged(new PrivilegedAction<Object>() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02001231 @Override
J. Duke319a3b92007-12-01 00:00:00 +00001232 public Object run() {
1233 logger.setParent(parent);
1234 return null;
1235 }});
1236 }
1237
J. Duke319a3b92007-12-01 00:00:00 +00001238 /**
1239 * Method to find a named logger.
1240 * <p>
1241 * Note that since untrusted code may create loggers with
1242 * arbitrary names this method should not be relied on to
1243 * find Loggers for security sensitive logging.
Daniel D. Daughertyc7a512e2010-09-22 07:46:49 -07001244 * It is also important to note that the Logger associated with the
1245 * String {@code name} may be garbage collected at any time if there
1246 * is no strong reference to the Logger. The caller of this method
1247 * must check the return value for null in order to properly handle
1248 * the case where the Logger has been garbage collected.
Alexander Stepanov3dff5402014-04-30 15:02:24 +04001249 *
J. Duke319a3b92007-12-01 00:00:00 +00001250 * @param name name of the logger
1251 * @return matching logger or null if none is found
1252 */
Mandy Chung7dde39f2012-11-26 22:49:06 -08001253 public Logger getLogger(String name) {
Mandy Chung3243aaf2013-01-10 19:43:36 -08001254 return getUserContext().findLogger(name);
J. Duke319a3b92007-12-01 00:00:00 +00001255 }
1256
1257 /**
1258 * Get an enumeration of known logger names.
1259 * <p>
1260 * Note: Loggers may be added dynamically as new classes are loaded.
1261 * This method only reports on the loggers that are currently registered.
Daniel D. Daughertyc7a512e2010-09-22 07:46:49 -07001262 * It is also important to note that this method only returns the name
1263 * of a Logger, not a strong reference to the Logger itself.
1264 * The returned String does nothing to prevent the Logger from being
1265 * garbage collected. In particular, if the returned name is passed
1266 * to {@code LogManager.getLogger()}, then the caller must check the
1267 * return value from {@code LogManager.getLogger()} for null to properly
1268 * handle the case where the Logger has been garbage collected in the
1269 * time since its name was returned by this method.
Alexander Stepanov3dff5402014-04-30 15:02:24 +04001270 *
J. Duke319a3b92007-12-01 00:00:00 +00001271 * @return enumeration of logger name strings
1272 */
Mandy Chung7dde39f2012-11-26 22:49:06 -08001273 public Enumeration<String> getLoggerNames() {
Mandy Chung3243aaf2013-01-10 19:43:36 -08001274 return getUserContext().getLoggerNames();
J. Duke319a3b92007-12-01 00:00:00 +00001275 }
1276
1277 /**
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001278 * Reads and initializes the logging configuration.
J. Duke319a3b92007-12-01 00:00:00 +00001279 * <p>
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001280 * If the "java.util.logging.config.class" system property is set, then the
1281 * property value is treated as a class name. The given class will be
1282 * loaded, an object will be instantiated, and that object's constructor
1283 * is responsible for reading in the initial configuration. (That object
1284 * may use other system properties to control its configuration.) The
1285 * alternate configuration class can use {@code readConfiguration(InputStream)}
1286 * to define properties in the LogManager.
1287 * <p>
1288 * If "java.util.logging.config.class" system property is <b>not</b> set,
1289 * then this method will read the initial configuration from a properties
1290 * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1291 * the configuration. The "java.util.logging.config.file" system property can be used
1292 * to specify the properties file that will be read as the initial configuration;
1293 * if not set, then the LogManager default configuration is used.
1294 * The default configuration is typically loaded from the
1295 * properties file "{@code conf/logging.properties}" in the Java installation
1296 * directory.
1297 *
J. Duke319a3b92007-12-01 00:00:00 +00001298 * <p>
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02001299 * Any {@linkplain #addConfigurationListener registered configuration
1300 * listener} will be invoked after the properties are read.
J. Duke319a3b92007-12-01 00:00:00 +00001301 *
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001302 * @apiNote This {@code readConfiguration} method should only be used for
1303 * initializing the configuration during LogManager initialization or
1304 * used with the "java.util.logging.config.class" property.
1305 * When this method is called after loggers have been created, and
1306 * the "java.util.logging.config.class" system property is not set, all
1307 * existing loggers will be {@linkplain #reset() reset}. Then any
1308 * existing loggers that have a level property specified in the new
1309 * configuration stream will be {@linkplain
1310 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1311 * <p>
1312 * To properly update the logging configuration, use the
1313 * {@link #updateConfiguration(java.util.function.Function)} or
1314 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1315 * methods instead.
1316 *
1317 * @throws SecurityException if a security manager exists and if
1318 * the caller does not have LoggingPermission("control").
1319 * @throws IOException if there are IO problems reading the configuration.
J. Duke319a3b92007-12-01 00:00:00 +00001320 */
1321 public void readConfiguration() throws IOException, SecurityException {
Alan Batemanc6f43f32012-09-08 20:31:42 +01001322 checkPermission();
J. Duke319a3b92007-12-01 00:00:00 +00001323
1324 // if a configuration class is specified, load it and use it.
1325 String cname = System.getProperty("java.util.logging.config.class");
1326 if (cname != null) {
1327 try {
1328 // Instantiate the named class. It is its constructor's
1329 // responsibility to initialize the logging configuration, by
1330 // calling readConfiguration(InputStream) with a suitable stream.
1331 try {
Prasannaa92f21202011-12-07 12:12:50 -08001332 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
J. Duke319a3b92007-12-01 00:00:00 +00001333 clz.newInstance();
1334 return;
1335 } catch (ClassNotFoundException ex) {
Prasannaa92f21202011-12-07 12:12:50 -08001336 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
J. Duke319a3b92007-12-01 00:00:00 +00001337 clz.newInstance();
1338 return;
1339 }
1340 } catch (Exception ex) {
1341 System.err.println("Logging configuration class \"" + cname + "\" failed");
1342 System.err.println("" + ex);
1343 // keep going and useful config file.
1344 }
1345 }
1346
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001347 String fname = getConfigurationFileName();
1348 try (final InputStream in = new FileInputStream(fname)) {
1349 final BufferedInputStream bin = new BufferedInputStream(in);
1350 readConfiguration(bin);
1351 }
1352 }
1353
1354 String getConfigurationFileName() throws IOException {
J. Duke319a3b92007-12-01 00:00:00 +00001355 String fname = System.getProperty("java.util.logging.config.file");
1356 if (fname == null) {
1357 fname = System.getProperty("java.home");
1358 if (fname == null) {
1359 throw new Error("Can't find java.home ??");
1360 }
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001361 fname = Paths.get(fname, "conf", "logging.properties")
1362 .toAbsolutePath().normalize().toString();
J. Duke319a3b92007-12-01 00:00:00 +00001363 }
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001364 return fname;
J. Duke319a3b92007-12-01 00:00:00 +00001365 }
1366
1367 /**
1368 * Reset the logging configuration.
1369 * <p>
1370 * For all named loggers, the reset operation removes and closes
1371 * all Handlers and (except for the root logger) sets the level
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001372 * to {@code null}. The root logger's level is set to {@code Level.INFO}.
J. Duke319a3b92007-12-01 00:00:00 +00001373 *
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001374 * @apiNote Calling this method also clears the LogManager {@linkplain
1375 * #getProperty(java.lang.String) properties}. The {@link
1376 * #updateConfiguration(java.util.function.Function)
1377 * updateConfiguration(Function)} or
1378 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)
1379 * updateConfiguration(InputStream, Function)} method can be used to
1380 * properly update to a new configuration.
1381 *
1382 * @throws SecurityException if a security manager exists and if
J. Duke319a3b92007-12-01 00:00:00 +00001383 * the caller does not have LoggingPermission("control").
1384 */
1385
1386 public void reset() throws SecurityException {
Alan Batemanc6f43f32012-09-08 20:31:42 +01001387 checkPermission();
Peter Levart30bcd972015-05-17 10:38:36 +02001388
Daniel Fuchs96da5b92014-11-24 17:02:37 +01001389 List<CloseOnReset> persistent;
Peter Levart30bcd972015-05-17 10:38:36 +02001390
1391 // We don't want reset() and readConfiguration()
1392 // to run in parallel
1393 configurationLock.lock();
1394 try {
1395 // install new empty properties
J. Duke319a3b92007-12-01 00:00:00 +00001396 props = new Properties();
Daniel Fuchs96da5b92014-11-24 17:02:37 +01001397 // make sure we keep the loggers persistent until reset is done.
1398 // Those are the loggers for which we previously created a
1399 // handler from the configuration, and we need to prevent them
1400 // from being gc'ed until those handlers are closed.
1401 persistent = new ArrayList<>(closeOnResetLoggers);
1402 closeOnResetLoggers.clear();
Peter Levart30bcd972015-05-17 10:38:36 +02001403
1404 // if reset has been called from shutdown-hook (Cleaner),
1405 // or if reset has been called from readConfiguration() which
1406 // already holds the lock and will change the state itself,
1407 // then do not change state here...
1408 if (globalHandlersState != STATE_SHUTDOWN &&
1409 globalHandlersState != STATE_READING_CONFIG) {
1410 // ...else user called reset()...
1411 // Since we are doing a reset we no longer want to initialize
1412 // the global handlers, if they haven't been initialized yet.
1413 globalHandlersState = STATE_INITIALIZED;
Mandy Chung7dde39f2012-11-26 22:49:06 -08001414 }
Peter Levart30bcd972015-05-17 10:38:36 +02001415
1416 for (LoggerContext cx : contexts()) {
1417 resetLoggerContext(cx);
1418 }
1419
1420 persistent.clear();
1421 } finally {
1422 configurationLock.unlock();
J. Duke319a3b92007-12-01 00:00:00 +00001423 }
1424 }
1425
Peter Levart30bcd972015-05-17 10:38:36 +02001426 private void resetLoggerContext(LoggerContext cx) {
1427 Enumeration<String> enum_ = cx.getLoggerNames();
1428 while (enum_.hasMoreElements()) {
1429 String name = enum_.nextElement();
1430 Logger logger = cx.findLogger(name);
1431 if (logger != null) {
1432 resetLogger(logger);
1433 }
1434 }
1435 }
1436
1437 private void closeHandlers(Logger logger) {
J. Duke319a3b92007-12-01 00:00:00 +00001438 Handler[] targets = logger.getHandlers();
Paul Sandozf35ec142013-12-20 13:38:13 +01001439 for (Handler h : targets) {
J. Duke319a3b92007-12-01 00:00:00 +00001440 logger.removeHandler(h);
1441 try {
1442 h.close();
1443 } catch (Exception ex) {
1444 // Problems closing a handler? Keep going...
1445 }
1446 }
Peter Levart30bcd972015-05-17 10:38:36 +02001447 }
1448
1449 // Private method to reset an individual target logger.
1450 private void resetLogger(Logger logger) {
1451 // Close all the Logger handlers.
1452 closeHandlers(logger);
1453
1454 // Reset Logger level
Mandy Chung7dde39f2012-11-26 22:49:06 -08001455 String name = logger.getName();
J. Duke319a3b92007-12-01 00:00:00 +00001456 if (name != null && name.equals("")) {
1457 // This is the root logger.
1458 logger.setLevel(defaultLevel);
1459 } else {
1460 logger.setLevel(null);
1461 }
1462 }
1463
1464 // get a list of whitespace separated classnames from a property.
1465 private String[] parseClassNames(String propertyName) {
1466 String hands = getProperty(propertyName);
1467 if (hands == null) {
1468 return new String[0];
1469 }
1470 hands = hands.trim();
1471 int ix = 0;
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02001472 final List<String> result = new ArrayList<>();
J. Duke319a3b92007-12-01 00:00:00 +00001473 while (ix < hands.length()) {
1474 int end = ix;
1475 while (end < hands.length()) {
1476 if (Character.isWhitespace(hands.charAt(end))) {
1477 break;
1478 }
1479 if (hands.charAt(end) == ',') {
1480 break;
1481 }
1482 end++;
1483 }
1484 String word = hands.substring(ix, end);
1485 ix = end+1;
1486 word = word.trim();
1487 if (word.length() == 0) {
1488 continue;
1489 }
1490 result.add(word);
1491 }
1492 return result.toArray(new String[result.size()]);
1493 }
1494
1495 /**
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001496 * Reads and initializes the logging configuration from the given input stream.
1497 *
1498 * <p>
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02001499 * Any {@linkplain #addConfigurationListener registered configuration
1500 * listener} will be invoked after the properties are read.
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01001501 *
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001502 * @apiNote This {@code readConfiguration} method should only be used for
1503 * initializing the configuration during LogManager initialization or
1504 * used with the "java.util.logging.config.class" property.
1505 * When this method is called after loggers have been created, all
1506 * existing loggers will be {@linkplain #reset() reset}. Then any
1507 * existing loggers that have a level property specified in the
1508 * given input stream will be {@linkplain
1509 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1510 * <p>
1511 * To properly update the logging configuration, use the
1512 * {@link #updateConfiguration(java.util.function.Function)} or
1513 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1514 * method instead.
J. Duke319a3b92007-12-01 00:00:00 +00001515 *
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001516 * @param ins stream to read properties from
1517 * @throws SecurityException if a security manager exists and if
J. Duke319a3b92007-12-01 00:00:00 +00001518 * the caller does not have LoggingPermission("control").
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001519 * @throws IOException if there are problems reading from the stream,
1520 * or the given stream is not in the
1521 * {@linkplain java.util.Properties properties file} format.
J. Duke319a3b92007-12-01 00:00:00 +00001522 */
1523 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
Alan Batemanc6f43f32012-09-08 20:31:42 +01001524 checkPermission();
J. Duke319a3b92007-12-01 00:00:00 +00001525
Peter Levart30bcd972015-05-17 10:38:36 +02001526 // We don't want reset() and readConfiguration() to run
1527 // in parallel.
1528 configurationLock.lock();
Daniel Fuchs679db142015-04-02 11:42:07 +02001529 try {
Peter Levart30bcd972015-05-17 10:38:36 +02001530 if (globalHandlersState == STATE_SHUTDOWN) {
1531 // already in terminal state: don't even bother
1532 // to read the configuration
1533 return;
1534 }
Daniel Fuchs679db142015-04-02 11:42:07 +02001535
Peter Levart30bcd972015-05-17 10:38:36 +02001536 // change state to STATE_READING_CONFIG to signal reset() to not change it
1537 globalHandlersState = STATE_READING_CONFIG;
J. Duke319a3b92007-12-01 00:00:00 +00001538 try {
Peter Levart30bcd972015-05-17 10:38:36 +02001539 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG
1540 // so that while reading configuration, any ongoing logging requests block and
1541 // wait for the outcome (see the end of this try statement)
1542 reset();
1543
1544 try {
1545 // Load the properties
1546 props.load(ins);
1547 } catch (IllegalArgumentException x) {
1548 // props.load may throw an IllegalArgumentException if the stream
1549 // contains malformed Unicode escape sequences.
1550 // We wrap that in an IOException as readConfiguration is
1551 // specified to throw IOException if there are problems reading
1552 // from the stream.
1553 // Note: new IOException(x.getMessage(), x) allow us to get a more
1554 // concise error message than new IOException(x);
1555 throw new IOException(x.getMessage(), x);
1556 }
1557
1558 // Instantiate new configuration objects.
1559 String names[] = parseClassNames("config");
1560
1561 for (String word : names) {
1562 try {
1563 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
1564 clz.newInstance();
1565 } catch (Exception ex) {
1566 System.err.println("Can't load config class \"" + word + "\"");
1567 System.err.println("" + ex);
1568 // ex.printStackTrace();
1569 }
1570 }
1571
1572 // Set levels on any pre-existing loggers, based on the new properties.
1573 setLevelsOnExistingLoggers();
1574
1575 // Note that we need to reinitialize global handles when
1576 // they are first referenced.
1577 globalHandlersState = STATE_UNINITIALIZED;
1578 } catch (Throwable t) {
1579 // If there were any trouble, then set state to STATE_INITIALIZED
1580 // so that no global handlers reinitialization is performed on not fully
1581 // initialized configuration.
1582 globalHandlersState = STATE_INITIALIZED;
1583 // re-throw
1584 throw t;
J. Duke319a3b92007-12-01 00:00:00 +00001585 }
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02001586 } finally {
Peter Levart30bcd972015-05-17 10:38:36 +02001587 configurationLock.unlock();
J. Duke319a3b92007-12-01 00:00:00 +00001588 }
Peter Levart30bcd972015-05-17 10:38:36 +02001589
1590 // should be called out of lock to avoid dead-lock situations
1591 // when user code is involved
1592 invokeConfigurationListeners();
J. Duke319a3b92007-12-01 00:00:00 +00001593 }
1594
Daniel Fuchsbd69fa02015-10-12 20:13:22 +02001595 // This enum enumerate the configuration properties that will be
1596 // updated on existing loggers when the configuration is updated
1597 // with LogManager.updateConfiguration().
1598 //
1599 // Note that this works properly only for the global LogManager - as
1600 // Handler and its subclasses get their configuration from
1601 // LogManager.getLogManager().
1602 //
1603 static enum ConfigProperty {
1604 LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1605 final String suffix;
1606 final int length;
1607 private ConfigProperty(String suffix) {
1608 this.suffix = Objects.requireNonNull(suffix);
1609 length = suffix.length();
1610 }
1611
1612 public boolean handleKey(String key) {
1613 if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1614 if (this == HANDLERS && suffix.equals(key)) return false;
1615 return key.endsWith(suffix);
1616 }
1617 String key(String loggerName) {
1618 if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1619 return suffix.substring(1);
1620 }
1621 return loggerName + suffix;
1622 }
1623 String loggerName(String key) {
1624 assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1625 if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1626 return key.substring(0, key.length() - length);
1627 }
1628
1629 /**
1630 * If the property is one that should be updated on existing loggers by
1631 * updateConfiguration, returns the name of the logger for which the
1632 * property is configured. Otherwise, returns null.
1633 * @param property a property key in 'props'
1634 * @return the name of the logger on which the property is to be set,
1635 * if the property is one that should be updated on existing
1636 * loggers, {@code null} otherwise.
1637 */
1638 static String getLoggerName(String property) {
1639 for (ConfigProperty p : ConfigProperty.ALL) {
1640 if (p.handleKey(property)) {
1641 return p.loggerName(property);
1642 }
1643 }
1644 return null; // Not a property that should be updated.
1645 }
1646
1647 /**
1648 * Find the ConfigProperty corresponding to the given
1649 * property key (may find none).
1650 * @param property a property key in 'props'
1651 * @return An optional containing a ConfigProperty object,
1652 * if the property is one that should be updated on existing
1653 * loggers, empty otherwise.
1654 */
1655 static Optional<ConfigProperty> find(String property) {
1656 return ConfigProperty.ALL.stream()
1657 .filter(p -> p.handleKey(property))
1658 .findFirst();
1659 }
1660
1661 /**
1662 * Returns true if the given property is one that should be updated
1663 * on existing loggers.
1664 * Used to filter property name streams.
1665 * @param property a property key from the configuration.
1666 * @return true if this property is of interest for updateConfiguration.
1667 */
1668 static boolean matches(String property) {
1669 return find(property).isPresent();
1670 }
1671
1672 /**
1673 * Returns true if the new property value is different from the old,
1674 * and therefore needs to be updated on existing loggers.
1675 * @param k a property key in the configuration
1676 * @param previous the old configuration
1677 * @param next the new configuration
1678 * @return true if the property is changing value between the two
1679 * configurations.
1680 */
1681 static boolean needsUpdating(String k, Properties previous, Properties next) {
1682 final String p = trim(previous.getProperty(k, null));
1683 final String n = trim(next.getProperty(k, null));
1684 return ! Objects.equals(p,n);
1685 }
1686
1687 /**
1688 * Applies the mapping function for the given key to the next
1689 * configuration.
1690 * If the mapping function is null then this method does nothing.
1691 * Otherwise, it calls the mapping function to compute the value
1692 * that should be associated with {@code key} in the resulting
1693 * configuration, and applies it to {@code next}.
1694 * If the mapping function returns {@code null} the key is removed
1695 * from {@code next}.
1696 *
1697 * @param k a property key in the configuration
1698 * @param previous the old configuration
1699 * @param next the new configuration (modified by this function)
1700 * @param remappingFunction the mapping function.
1701 */
1702 static void merge(String k, Properties previous, Properties next,
1703 BiFunction<String, String, String> mappingFunction) {
1704 String p = trim(previous.getProperty(k, null));
1705 String n = trim(next.getProperty(k, null));
1706 String mapped = trim(mappingFunction.apply(p,n));
1707 if (!Objects.equals(n, mapped)) {
1708 if (mapped == null) {
1709 next.remove(k);
1710 } else {
1711 next.setProperty(k, mapped);
1712 }
1713 }
1714 }
1715
1716 private static final EnumSet<ConfigProperty> ALL =
1717 EnumSet.allOf(ConfigProperty.class);
1718 }
1719
1720 // trim the value if not null.
1721 private static String trim(String value) {
1722 return value == null ? null : value.trim();
1723 }
1724
1725 /**
1726 * An object that keep track of loggers we have already visited.
1727 * Used when updating configuration, to avoid processing the same logger
1728 * twice.
1729 */
1730 static final class VisitedLoggers implements Predicate<Logger> {
1731 final IdentityHashMap<Logger,Boolean> visited;
1732 private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
1733 this.visited = visited;
1734 }
1735 VisitedLoggers() {
1736 this(new IdentityHashMap<>());
1737 }
1738 @Override
1739 public boolean test(Logger logger) {
1740 return visited != null && visited.put(logger, Boolean.TRUE) != null;
1741 }
1742 public void clear() {
1743 if (visited != null) visited.clear();
1744 }
1745
1746 // An object that considers that no logger has ever been visited.
1747 // This is used when processParentHandlers is called from
1748 // LoggerContext.addLocalLogger
1749 static final VisitedLoggers NEVER = new VisitedLoggers(null);
1750 }
1751
1752
1753 /**
1754 * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
1755 * or REMOVED.
1756 */
1757 static enum ModType {
1758 SAME, // property had no value in the old and new conf, or had the
1759 // same value in both.
1760 ADDED, // property had no value in the old conf, but has one in the new.
1761 CHANGED, // property has a different value in the old conf and the new conf.
1762 REMOVED; // property has no value in the new conf, but had one in the old.
1763 static ModType of(String previous, String next) {
1764 if (previous == null && next != null) {
1765 return ADDED;
1766 }
1767 if (next == null && previous != null) {
1768 return REMOVED;
1769 }
1770 if (!Objects.equals(trim(previous), trim(next))) {
1771 return CHANGED;
1772 }
1773 return SAME;
1774 }
1775 }
1776
1777 /**
1778 * Updates the logging configuration.
1779 * <p>
1780 * If the "java.util.logging.config.file" system property is set,
1781 * then the property value specifies the properties file to be read
1782 * as the new configuration. Otherwise, the LogManager default
1783 * configuration is used.
1784 * <br>The default configuration is typically loaded from the
1785 * properties file "{@code conf/logging.properties}" in the
1786 * Java installation directory.
1787 * <p>
1788 * This method reads the new configuration and calls the {@link
1789 * #updateConfiguration(java.io.InputStream, java.util.function.Function)
1790 * updateConfiguration(ins, mapper)} method to
1791 * update the configuration.
1792 *
1793 * @apiNote
1794 * This method updates the logging configuration from reading
1795 * a properties file and ignores the "java.util.logging.config.class"
1796 * system property. The "java.util.logging.config.class" property is
1797 * only used by the {@link #readConfiguration()} method to load a custom
1798 * configuration class as an initial configuration.
1799 *
1800 * @param mapper a functional interface that takes a configuration
1801 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1802 * value will be applied to the resulting configuration. The
1803 * function <i>f</i> may return {@code null} to indicate that the property
1804 * <i>k</i> will not be added to the resulting configuration.
1805 * <br>
1806 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
1807 * assumed.
1808 * <br>
1809 * For each <i>k</i>, the mapped function <i>f</i> will
1810 * be invoked with the value associated with <i>k</i> in the old
1811 * configuration (i.e <i>o</i>) and the value associated with
1812 * <i>k</i> in the new configuration (i.e. <i>n</i>).
1813 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1814 * value was present for <i>k</i> in the corresponding configuration.
1815 *
1816 * @throws SecurityException if a security manager exists and if
1817 * the caller does not have LoggingPermission("control"), or
1818 * does not have the permissions required to set up the
1819 * configuration (e.g. open file specified for FileHandlers
1820 * etc...)
1821 *
1822 * @throws NullPointerException if {@code mapper} returns a {@code null}
1823 * function when invoked.
1824 *
1825 * @throws IOException if there are problems reading from the
1826 * logging configuration file.
1827 *
1828 * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)
1829 */
1830 public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
1831 throws IOException {
1832 checkPermission();
1833 ensureLogManagerInitialized();
1834 drainLoggerRefQueueBounded();
1835
1836 String fname = getConfigurationFileName();
1837 try (final InputStream in = new FileInputStream(fname)) {
1838 final BufferedInputStream bin = new BufferedInputStream(in);
1839 updateConfiguration(bin, mapper);
1840 }
1841 }
1842
1843 /**
1844 * Updates the logging configuration.
1845 * <p>
1846 * For each configuration key in the {@linkplain
1847 * #getProperty(java.lang.String) existing configuration} and
1848 * the given input stream configuration, the given {@code mapper} function
1849 * is invoked to map from the configuration key to a function,
1850 * <i>f(o,n)</i>, that takes the old value and new value and returns
1851 * the resulting value to be applied in the resulting configuration,
1852 * as specified in the table below.
1853 * <p>Let <i>k</i> be a configuration key in the old or new configuration,
1854 * <i>o</i> be the old value (i.e. the value associated
1855 * with <i>k</i> in the old configuration), <i>n</i> be the
1856 * new value (i.e. the value associated with <i>k</i> in the new
1857 * configuration), and <i>f</i> be the function returned
1858 * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
1859 * resulting value. If <i>v</i> is not {@code null}, then a property
1860 * <i>k</i> with value <i>v</i> will be added to the resulting configuration.
1861 * Otherwise, it will be omitted.
1862 * <br>A {@code null} value may be passed to function
1863 * <i>f</i> to indicate that the corresponding configuration has no
1864 * configuration key <i>k</i>.
1865 * The function <i>f</i> may return {@code null} to indicate that
1866 * there will be no value associated with <i>k</i> in the resulting
1867 * configuration.
1868 * <p>
1869 * If {@code mapper} is {@code null}, then <i>v</i> will be set to
1870 * <i>n</i>.
1871 * <p>
1872 * LogManager {@linkplain #getProperty(java.lang.String) properties} are
1873 * updated with the resulting value in the resulting configuration.
1874 * <p>
1875 * The registered {@linkplain #addConfigurationListener configuration
1876 * listeners} will be invoked after the configuration is successfully updated.
1877 * <br><br>
1878 * <table summary="Updating configuration properties">
1879 * <tr>
1880 * <th>Property</th>
1881 * <th>Resulting Behavior</th>
1882 * </tr>
1883 * <tr>
1884 * <td valign="top">{@code <logger>.level}</td>
1885 * <td>
1886 * <ul>
1887 * <li>If the resulting configuration defines a level for a logger and
1888 * if the resulting level is different than the level specified in the
1889 * the old configuration, or not specified in
1890 * the old configuration, then if the logger exists or if children for
1891 * that logger exist, the level for that logger will be updated,
1892 * and the change propagated to any existing logger children.
1893 * This may cause the logger to be created, if necessary.
1894 * </li>
1895 * <li>If the old configuration defined a level for a logger, and the
1896 * resulting configuration doesn't, then this change will not be
1897 * propagated to existing loggers, if any.
1898 * To completely replace a configuration - the caller should therefore
1899 * call {@link #reset() reset} to empty the current configuration,
1900 * before calling {@code updateConfiguration}.
1901 * </li>
1902 * </ul>
1903 * </td>
1904 * <tr>
1905 * <td valign="top">{@code <logger>.useParentHandlers}</td>
1906 * <td>
1907 * <ul>
1908 * <li>If either the resulting or the old value for the useParentHandlers
1909 * property is not null, then if the logger exists or if children for
1910 * that logger exist, that logger will be updated to the resulting
1911 * value.
1912 * The value of the useParentHandlers property is the value specified
1913 * in the configuration; if not specified, the default is true.
1914 * </li>
1915 * </ul>
1916 * </td>
1917 * </tr>
1918 * <tr>
1919 * <td valign="top">{@code <logger>.handlers}</td>
1920 * <td>
1921 * <ul>
1922 * <li>If the resulting configuration defines a list of handlers for a
1923 * logger, and if the resulting list is different than the list
1924 * specified in the old configuration for that logger (that could be
1925 * empty), then if the logger exists or its children exist, the
1926 * handlers associated with that logger are closed and removed and
1927 * the new handlers will be created per the resulting configuration
1928 * and added to that logger, creating that logger if necessary.
1929 * </li>
1930 * <li>If the old configuration defined some handlers for a logger, and
1931 * the resulting configuration doesn't, if that logger exists,
1932 * its handlers will be removed and closed.
1933 * </li>
1934 * <li>Changing the list of handlers on an existing logger will cause all
1935 * its previous handlers to be removed and closed, regardless of whether
1936 * they had been created from the configuration or programmatically.
1937 * The old handlers will be replaced by new handlers, if any.
1938 * </li>
1939 * </ul>
1940 * </td>
1941 * </tr>
1942 * <tr>
1943 * <td valign="top">{@code <handler-name>.*}</td>
1944 * <td>
1945 * <ul>
1946 * <li>Properties configured/changed on handler classes will only affect
1947 * newly created handlers. If a node is configured with the same list
1948 * of handlers in the old and the resulting configuration, then these
1949 * handlers will remain unchanged.
1950 * </li>
1951 * </ul>
1952 * </td>
1953 * </tr>
1954 * <tr>
1955 * <td valign="top">{@code config} and any other property</td>
1956 * <td>
1957 * <ul>
1958 * <li>The resulting value for these property will be stored in the
1959 * LogManager properties, but {@code updateConfiguration} will not parse
1960 * or process their values.
1961 * </li>
1962 * </ul>
1963 * </td>
1964 * </tr>
1965 * </table>
1966 * <p>
1967 * <em>Example mapper functions:</em>
1968 * <br><br>
1969 * <ul>
1970 * <li>Replace all logging properties with the new configuration:
1971 * <br><br>{@code (k) -> ((o, n) -> n)}:
1972 * <br><br>this is equivalent to passing a null {@code mapper} parameter.
1973 * </li>
1974 * <li>Merge the new configuration and old configuration and use the
1975 * new value if <i>k</i> exists in the new configuration:
1976 * <br><br>{@code (k) -> ((o, n) -> n == null ? o : n)}:
1977 * <br><br>as if merging two collections as follows:
1978 * {@code result.putAll(oldc); result.putAll(newc)}.<br></li>
1979 * <li>Merge the new configuration and old configuration and use the old
1980 * value if <i>k</i> exists in the old configuration:
1981 * <br><br>{@code (k) -> ((o, n) -> o == null ? n : o)}:
1982 * <br><br>as if merging two collections as follows:
1983 * {@code result.putAll(newc); result.putAll(oldc)}.<br></li>
1984 * <li>Replace all properties with the new configuration except the handler
1985 * property to configure Logger's handler that is not root logger:
1986 * <br>
1987 * <pre>{@code (k) -> k.endsWith(".handlers")}
1988 * {@code ? ((o, n) -> (o == null ? n : o))}
1989 * {@code : ((o, n) -> n)}</pre>
1990 * </li>
1991 * </ul>
1992 * <p>
1993 * To completely reinitialize a configuration, an application can first call
1994 * {@link #reset() reset} to fully remove the old configuration, followed by
1995 * {@code updateConfiguration} to initialize the new configuration.
1996 *
1997 * @param ins a stream to read properties from
1998 * @param mapper a functional interface that takes a configuration
1999 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
2000 * value will be applied to the resulting configuration. The
2001 * function <i>f</i> may return {@code null} to indicate that the property
2002 * <i>k</i> will not be added to the resulting configuration.
2003 * <br>
2004 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
2005 * assumed.
2006 * <br>
2007 * For each <i>k</i>, the mapped function <i>f</i> will
2008 * be invoked with the value associated with <i>k</i> in the old
2009 * configuration (i.e <i>o</i>) and the value associated with
2010 * <i>k</i> in the new configuration (i.e. <i>n</i>).
2011 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
2012 * value was present for <i>k</i> in the corresponding configuration.
2013 *
2014 * @throws SecurityException if a security manager exists and if
2015 * the caller does not have LoggingPermission("control"), or
2016 * does not have the permissions required to set up the
2017 * configuration (e.g. open files specified for FileHandlers)
2018 *
2019 * @throws NullPointerException if {@code ins} is null or if
2020 * {@code mapper} returns a null function when invoked.
2021 *
2022 * @throws IOException if there are problems reading from the stream,
2023 * or the given stream is not in the
2024 * {@linkplain java.util.Properties properties file} format.
2025 */
2026 public void updateConfiguration(InputStream ins,
2027 Function<String, BiFunction<String,String,String>> mapper)
2028 throws IOException {
2029 checkPermission();
2030 ensureLogManagerInitialized();
2031 drainLoggerRefQueueBounded();
2032
2033 final Properties previous;
2034 final Set<String> updatePropertyNames;
2035 List<LoggerContext> cxs = Collections.emptyList();
2036 final VisitedLoggers visited = new VisitedLoggers();
2037 final Properties next = new Properties();
2038
2039 try {
2040 // Load the properties
2041 next.load(ins);
2042 } catch (IllegalArgumentException x) {
2043 // props.load may throw an IllegalArgumentException if the stream
2044 // contains malformed Unicode escape sequences.
2045 // We wrap that in an IOException as updateConfiguration is
2046 // specified to throw IOException if there are problems reading
2047 // from the stream.
2048 // Note: new IOException(x.getMessage(), x) allow us to get a more
2049 // concise error message than new IOException(x);
2050 throw new IOException(x.getMessage(), x);
2051 }
2052
2053 if (globalHandlersState == STATE_SHUTDOWN) return;
2054
2055 // exclusive lock: readConfiguration/reset/updateConfiguration can't
2056 // run concurrently.
2057 // configurationLock.writeLock().lock();
2058 configurationLock.lock();
2059 try {
2060 if (globalHandlersState == STATE_SHUTDOWN) return;
2061 previous = props;
2062
2063 // Builds a TreeSet of all (old and new) property names.
2064 updatePropertyNames =
2065 Stream.concat(previous.stringPropertyNames().stream(),
2066 next.stringPropertyNames().stream())
2067 .collect(Collectors.toCollection(TreeSet::new));
2068
2069 if (mapper != null) {
2070 // mapper will potentially modify the content of
2071 // 'next', so we need to call it before affecting props=next.
2072 // give a chance to the mapper to control all
2073 // properties - not just those we will reset.
2074 updatePropertyNames.stream()
2075 .forEachOrdered(k -> ConfigProperty
2076 .merge(k, previous, next,
2077 Objects.requireNonNull(mapper.apply(k))));
2078 }
2079
2080 props = next;
2081
2082 // allKeys will contain all keys:
2083 // - which correspond to a configuration property we are interested in
2084 // (first filter)
2085 // - whose value needs to be updated (because it's new, removed, or
2086 // different) in the resulting configuration (second filter)
2087 final Stream<String> allKeys = updatePropertyNames.stream()
2088 .filter(ConfigProperty::matches)
2089 .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
2090
2091 // Group configuration properties by logger name
2092 // We use a TreeMap so that parent loggers will be visited before
2093 // child loggers.
2094 final Map<String, TreeSet<String>> loggerConfigs =
2095 allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
2096 TreeMap::new,
2097 Collectors.toCollection(TreeSet::new)));
2098
2099 if (!loggerConfigs.isEmpty()) {
2100 cxs = contexts();
2101 }
2102 final List<Logger> loggers = cxs.isEmpty()
2103 ? Collections.emptyList() : new ArrayList<>(cxs.size());
2104 for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
2105 // This can be a logger name, or something else...
2106 // The only thing we know is that we found a property
2107 // we are interested in.
2108 // For instance, if we found x.y.z.level, then x.y.z could be
2109 // a logger, but it could also be a handler class...
2110 // Anyway...
2111 final String name = e.getKey();
2112 final Set<String> properties = e.getValue();
2113 loggers.clear();
2114 for (LoggerContext cx : cxs) {
2115 Logger l = cx.findLogger(name);
2116 if (l != null && !visited.test(l)) {
2117 loggers.add(l);
2118 }
2119 }
2120 if (loggers.isEmpty()) continue;
2121 for (String pk : properties) {
2122 ConfigProperty cp = ConfigProperty.find(pk).get();
2123 String p = previous.getProperty(pk, null);
2124 String n = next.getProperty(pk, null);
2125
2126 // Determines the type of modification.
2127 ModType mod = ModType.of(p, n);
2128
2129 // mod == SAME means that the two values are equals, there
2130 // is nothing to do. Usually, this should not happen as such
2131 // properties should have been filtered above.
2132 // It could happen however if the properties had
2133 // trailing/leading whitespaces.
2134 if (mod == ModType.SAME) continue;
2135
2136 switch (cp) {
2137 case LEVEL:
2138 if (mod == ModType.REMOVED) continue;
2139 Level level = Level.findLevel(trim(n));
2140 if (level != null) {
2141 if (name.isEmpty()) {
2142 rootLogger.setLevel(level);
2143 }
2144 for (Logger l : loggers) {
2145 if (!name.isEmpty() || l != rootLogger) {
2146 l.setLevel(level);
2147 }
2148 }
2149 }
2150 break;
2151 case USEPARENT:
2152 if (!name.isEmpty()) {
2153 boolean useParent = getBooleanProperty(pk, true);
2154 if (n != null || p != null) {
2155 // reset the flag only if the previous value
2156 // or the new value are not null.
2157 for (Logger l : loggers) {
2158 l.setUseParentHandlers(useParent);
2159 }
2160 }
2161 }
2162 break;
2163 case HANDLERS:
2164 List<Handler> hdls = null;
2165 if (name.isEmpty()) {
2166 // special handling for the root logger.
2167 globalHandlersState = STATE_READING_CONFIG;
2168 try {
2169 closeHandlers(rootLogger);
2170 globalHandlersState = STATE_UNINITIALIZED;
2171 } catch (Throwable t) {
2172 globalHandlersState = STATE_INITIALIZED;
2173 throw t;
2174 }
2175 }
2176 for (Logger l : loggers) {
2177 if (l == rootLogger) continue;
2178 closeHandlers(l);
2179 if (mod == ModType.REMOVED) {
2180 closeOnResetLoggers.removeIf(c -> c.logger == l);
2181 continue;
2182 }
2183 if (hdls == null) {
2184 hdls = name.isEmpty()
2185 ? Arrays.asList(rootLogger.getHandlers())
2186 : createLoggerHandlers(name, pk);
2187 }
2188 setLoggerHandlers(l, name, pk, hdls);
2189 }
2190 break;
2191 default: break;
2192 }
2193 }
2194 }
2195 } finally {
2196 configurationLock.unlock();
2197 visited.clear();
2198 }
2199
2200 // Now ensure that if an existing logger has acquired a new parent
2201 // in the configuration, this new parent will be created - if needed,
2202 // and added to the context of the existing child.
2203 //
2204 drainLoggerRefQueueBounded();
2205 for (LoggerContext cx : cxs) {
2206 for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2207 String name = names.nextElement();
2208 if (name.isEmpty()) continue; // don't need to process parents on root.
2209 Logger l = cx.findLogger(name);
2210 if (l != null && !visited.test(l)) {
2211 // should pass visited here to cut the processing when
2212 // reaching a logger already visited.
2213 cx.processParentHandlers(l, name, visited);
2214 }
2215 }
2216 }
2217
2218 // We changed the configuration: invoke configuration listeners
2219 invokeConfigurationListeners();
2220 }
2221
J. Duke319a3b92007-12-01 00:00:00 +00002222 /**
2223 * Get the value of a logging property.
2224 * The method returns null if the property is not found.
2225 * @param name property name
2226 * @return property value
2227 */
2228 public String getProperty(String name) {
2229 return props.getProperty(name);
2230 }
2231
2232 // Package private method to get a String property.
2233 // If the property is not defined we return the given
2234 // default value.
2235 String getStringProperty(String name, String defaultValue) {
2236 String val = getProperty(name);
2237 if (val == null) {
2238 return defaultValue;
2239 }
2240 return val.trim();
2241 }
2242
2243 // Package private method to get an integer property.
2244 // If the property is not defined or cannot be parsed
2245 // we return the given default value.
2246 int getIntProperty(String name, int defaultValue) {
2247 String val = getProperty(name);
2248 if (val == null) {
2249 return defaultValue;
2250 }
2251 try {
2252 return Integer.parseInt(val.trim());
2253 } catch (Exception ex) {
2254 return defaultValue;
2255 }
2256 }
2257
Daniel Fuchs59ca72f2014-10-22 17:23:14 +02002258 // Package private method to get a long property.
2259 // If the property is not defined or cannot be parsed
2260 // we return the given default value.
2261 long getLongProperty(String name, long defaultValue) {
2262 String val = getProperty(name);
2263 if (val == null) {
2264 return defaultValue;
2265 }
2266 try {
2267 return Long.parseLong(val.trim());
2268 } catch (Exception ex) {
2269 return defaultValue;
2270 }
2271 }
2272
J. Duke319a3b92007-12-01 00:00:00 +00002273 // Package private method to get a boolean property.
2274 // If the property is not defined or cannot be parsed
2275 // we return the given default value.
2276 boolean getBooleanProperty(String name, boolean defaultValue) {
2277 String val = getProperty(name);
2278 if (val == null) {
2279 return defaultValue;
2280 }
2281 val = val.toLowerCase();
2282 if (val.equals("true") || val.equals("1")) {
2283 return true;
2284 } else if (val.equals("false") || val.equals("0")) {
2285 return false;
2286 }
2287 return defaultValue;
2288 }
2289
2290 // Package private method to get a Level property.
2291 // If the property is not defined or cannot be parsed
2292 // we return the given default value.
2293 Level getLevelProperty(String name, Level defaultValue) {
2294 String val = getProperty(name);
2295 if (val == null) {
2296 return defaultValue;
2297 }
Mandy Chung7dde39f2012-11-26 22:49:06 -08002298 Level l = Level.findLevel(val.trim());
2299 return l != null ? l : defaultValue;
J. Duke319a3b92007-12-01 00:00:00 +00002300 }
2301
2302 // Package private method to get a filter property.
2303 // We return an instance of the class named by the "name"
2304 // property. If the property is not defined or has problems
2305 // we return the defaultValue.
2306 Filter getFilterProperty(String name, Filter defaultValue) {
2307 String val = getProperty(name);
2308 try {
2309 if (val != null) {
Prasannaa92f21202011-12-07 12:12:50 -08002310 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
J. Duke319a3b92007-12-01 00:00:00 +00002311 return (Filter) clz.newInstance();
2312 }
2313 } catch (Exception ex) {
2314 // We got one of a variety of exceptions in creating the
2315 // class or creating an instance.
2316 // Drop through.
2317 }
2318 // We got an exception. Return the defaultValue.
2319 return defaultValue;
2320 }
2321
2322
2323 // Package private method to get a formatter property.
2324 // We return an instance of the class named by the "name"
2325 // property. If the property is not defined or has problems
2326 // we return the defaultValue.
2327 Formatter getFormatterProperty(String name, Formatter defaultValue) {
2328 String val = getProperty(name);
2329 try {
2330 if (val != null) {
Prasannaa92f21202011-12-07 12:12:50 -08002331 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
J. Duke319a3b92007-12-01 00:00:00 +00002332 return (Formatter) clz.newInstance();
2333 }
2334 } catch (Exception ex) {
2335 // We got one of a variety of exceptions in creating the
2336 // class or creating an instance.
2337 // Drop through.
2338 }
2339 // We got an exception. Return the defaultValue.
2340 return defaultValue;
2341 }
2342
2343 // Private method to load the global handlers.
2344 // We do the real work lazily, when the global handlers
2345 // are first used.
Peter Levart30bcd972015-05-17 10:38:36 +02002346 private void initializeGlobalHandlers() {
2347 int state = globalHandlersState;
2348 if (state == STATE_INITIALIZED ||
2349 state == STATE_SHUTDOWN) {
2350 // Nothing to do: return.
J. Duke319a3b92007-12-01 00:00:00 +00002351 return;
2352 }
2353
Peter Levart30bcd972015-05-17 10:38:36 +02002354 // If we have not initialized global handlers yet (or need to
2355 // reinitialize them), lets do it now (this case is indicated by
2356 // globalHandlersState == STATE_UNINITIALIZED).
2357 // If we are in the process of initializing global handlers we
2358 // also need to lock & wait (this case is indicated by
2359 // globalHandlersState == STATE_INITIALIZING).
2360 // If we are in the process of reading configuration we also need to
2361 // wait to see what the outcome will be (this case
2362 // is indicated by globalHandlersState == STATE_READING_CONFIG)
2363 // So in either case we need to wait for the lock.
2364 configurationLock.lock();
2365 try {
2366 if (globalHandlersState != STATE_UNINITIALIZED) {
2367 return; // recursive call or nothing to do
2368 }
2369 // set globalHandlersState to STATE_INITIALIZING first to avoid
2370 // getting an infinite recursion when loadLoggerHandlers(...)
2371 // is going to call addHandler(...)
2372 globalHandlersState = STATE_INITIALIZING;
2373 try {
2374 loadLoggerHandlers(rootLogger, null, "handlers");
2375 } finally {
2376 globalHandlersState = STATE_INITIALIZED;
2377 }
2378 } finally {
2379 configurationLock.unlock();
J. Duke319a3b92007-12-01 00:00:00 +00002380 }
J. Duke319a3b92007-12-01 00:00:00 +00002381 }
2382
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002383 static final Permission controlPermission =
2384 new LoggingPermission("control", null);
J. Duke319a3b92007-12-01 00:00:00 +00002385
Alan Batemanc6f43f32012-09-08 20:31:42 +01002386 void checkPermission() {
2387 SecurityManager sm = System.getSecurityManager();
2388 if (sm != null)
2389 sm.checkPermission(controlPermission);
2390 }
J. Duke319a3b92007-12-01 00:00:00 +00002391
2392 /**
2393 * Check that the current context is trusted to modify the logging
2394 * configuration. This requires LoggingPermission("control").
2395 * <p>
2396 * If the check fails we throw a SecurityException, otherwise
2397 * we return normally.
2398 *
2399 * @exception SecurityException if a security manager exists and if
2400 * the caller does not have LoggingPermission("control").
2401 */
2402 public void checkAccess() throws SecurityException {
Alan Batemanc6f43f32012-09-08 20:31:42 +01002403 checkPermission();
J. Duke319a3b92007-12-01 00:00:00 +00002404 }
2405
2406 // Nested class to represent a node in our tree of named loggers.
2407 private static class LogNode {
2408 HashMap<String,LogNode> children;
Jeremy Manson9a0c9852010-06-22 10:54:59 -07002409 LoggerWeakRef loggerRef;
J. Duke319a3b92007-12-01 00:00:00 +00002410 LogNode parent;
Mandy Chung7dde39f2012-11-26 22:49:06 -08002411 final LoggerContext context;
J. Duke319a3b92007-12-01 00:00:00 +00002412
Mandy Chung7dde39f2012-11-26 22:49:06 -08002413 LogNode(LogNode parent, LoggerContext context) {
J. Duke319a3b92007-12-01 00:00:00 +00002414 this.parent = parent;
Mandy Chung7dde39f2012-11-26 22:49:06 -08002415 this.context = context;
J. Duke319a3b92007-12-01 00:00:00 +00002416 }
2417
2418 // Recursive method to walk the tree below a node and set
2419 // a new parent logger.
2420 void walkAndSetParent(Logger parent) {
2421 if (children == null) {
2422 return;
2423 }
Paul Sandozf35ec142013-12-20 13:38:13 +01002424 for (LogNode node : children.values()) {
Jeremy Manson9a0c9852010-06-22 10:54:59 -07002425 LoggerWeakRef ref = node.loggerRef;
J. Duke319a3b92007-12-01 00:00:00 +00002426 Logger logger = (ref == null) ? null : ref.get();
2427 if (logger == null) {
2428 node.walkAndSetParent(parent);
2429 } else {
2430 doSetParent(logger, parent);
2431 }
2432 }
2433 }
2434 }
2435
2436 // We use a subclass of Logger for the root logger, so
2437 // that we only instantiate the global handlers when they
2438 // are first needed.
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002439 private final class RootLogger extends Logger {
J. Duke319a3b92007-12-01 00:00:00 +00002440 private RootLogger() {
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002441 // We do not call the protected Logger two args constructor here,
2442 // to avoid calling LogManager.getLogManager() from within the
2443 // RootLogger constructor.
Daniel Fuchs1276d6b2014-01-16 17:49:40 +01002444 super("", null, null, LogManager.this, true);
J. Duke319a3b92007-12-01 00:00:00 +00002445 }
2446
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002447 @Override
J. Duke319a3b92007-12-01 00:00:00 +00002448 public void log(LogRecord record) {
2449 // Make sure that the global handlers have been instantiated.
2450 initializeGlobalHandlers();
2451 super.log(record);
2452 }
2453
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002454 @Override
J. Duke319a3b92007-12-01 00:00:00 +00002455 public void addHandler(Handler h) {
2456 initializeGlobalHandlers();
2457 super.addHandler(h);
2458 }
2459
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002460 @Override
J. Duke319a3b92007-12-01 00:00:00 +00002461 public void removeHandler(Handler h) {
2462 initializeGlobalHandlers();
2463 super.removeHandler(h);
2464 }
2465
Daniel Fuchs33dbc2d2013-09-09 13:59:51 +02002466 @Override
Daniel Fuchs1276d6b2014-01-16 17:49:40 +01002467 Handler[] accessCheckedHandlers() {
J. Duke319a3b92007-12-01 00:00:00 +00002468 initializeGlobalHandlers();
Daniel Fuchs1276d6b2014-01-16 17:49:40 +01002469 return super.accessCheckedHandlers();
J. Duke319a3b92007-12-01 00:00:00 +00002470 }
2471 }
2472
2473
2474 // Private method to be called when the configuration has
2475 // changed to apply any level settings to any pre-existing loggers.
Peter Levart30bcd972015-05-17 10:38:36 +02002476 private void setLevelsOnExistingLoggers() {
Prasannaa92f21202011-12-07 12:12:50 -08002477 Enumeration<?> enum_ = props.propertyNames();
J. Duke319a3b92007-12-01 00:00:00 +00002478 while (enum_.hasMoreElements()) {
2479 String key = (String)enum_.nextElement();
2480 if (!key.endsWith(".level")) {
2481 // Not a level definition.
2482 continue;
2483 }
2484 int ix = key.length() - 6;
2485 String name = key.substring(0, ix);
2486 Level level = getLevelProperty(key, null);
2487 if (level == null) {
2488 System.err.println("Bad level value for property: " + key);
2489 continue;
2490 }
Mandy Chung7dde39f2012-11-26 22:49:06 -08002491 for (LoggerContext cx : contexts()) {
2492 Logger l = cx.findLogger(name);
2493 if (l == null) {
2494 continue;
2495 }
2496 l.setLevel(level);
J. Duke319a3b92007-12-01 00:00:00 +00002497 }
J. Duke319a3b92007-12-01 00:00:00 +00002498 }
2499 }
2500
2501 // Management Support
2502 private static LoggingMXBean loggingMXBean = null;
2503 /**
2504 * String representation of the
Mandy Chung793f1072011-03-29 15:50:55 -07002505 * {@link javax.management.ObjectName} for the management interface
2506 * for the logging facility.
2507 *
2508 * @see java.lang.management.PlatformLoggingMXBean
2509 * @see java.util.logging.LoggingMXBean
2510 *
J. Duke319a3b92007-12-01 00:00:00 +00002511 * @since 1.5
2512 */
2513 public final static String LOGGING_MXBEAN_NAME
2514 = "java.util.logging:type=Logging";
2515
2516 /**
Alexander Stepanovc78487e2015-08-06 19:07:35 +03002517 * Returns {@code LoggingMXBean} for managing loggers.
Mandy Chung793f1072011-03-29 15:50:55 -07002518 * An alternative way to manage loggers is through the
2519 * {@link java.lang.management.PlatformLoggingMXBean} interface
2520 * that can be obtained by calling:
Mandy Chungf66cba82009-10-27 16:32:23 -07002521 * <pre>
Mandy Chung793f1072011-03-29 15:50:55 -07002522 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
2523 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
Mandy Chungf66cba82009-10-27 16:32:23 -07002524 * </pre>
J. Duke319a3b92007-12-01 00:00:00 +00002525 *
2526 * @return a {@link LoggingMXBean} object.
2527 *
Mandy Chung793f1072011-03-29 15:50:55 -07002528 * @see java.lang.management.PlatformLoggingMXBean
J. Duke319a3b92007-12-01 00:00:00 +00002529 * @since 1.5
2530 */
Mandy Chung793f1072011-03-29 15:50:55 -07002531 public static synchronized LoggingMXBean getLoggingMXBean() {
J. Duke319a3b92007-12-01 00:00:00 +00002532 if (loggingMXBean == null) {
2533 loggingMXBean = new Logging();
2534 }
2535 return loggingMXBean;
2536 }
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02002537
2538 /**
2539 * Adds a configuration listener to be invoked each time the logging
2540 * configuration is read.
2541 * If the listener is already registered the method does nothing.
2542 * <p>
2543 * The listener is invoked with privileges that are restricted by the
2544 * calling context of this method.
2545 * The order in which the listeners are invoked is unspecified.
2546 * <p>
2547 * It is recommended that listeners do not throw errors or exceptions.
2548 *
2549 * If a listener terminates with an uncaught error or exception then
2550 * the first exception will be propagated to the caller of
2551 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
2552 * after all listeners have been invoked.
2553 *
2554 * @implNote If more than one listener terminates with an uncaught error or
2555 * exception, an implementation may record the additional errors or
2556 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
2557 * suppressed exceptions}.
2558 *
2559 * @param listener A configuration listener that will be invoked after the
2560 * configuration changed.
2561 * @return This LogManager.
2562 * @throws SecurityException if a security manager exists and if the
2563 * caller does not have LoggingPermission("control").
2564 * @throws NullPointerException if the listener is null.
2565 *
Iris Clark9d07dc02016-01-20 11:02:36 -08002566 * @since 9
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02002567 */
2568 public LogManager addConfigurationListener(Runnable listener) {
2569 final Runnable r = Objects.requireNonNull(listener);
2570 checkPermission();
2571 final SecurityManager sm = System.getSecurityManager();
2572 final AccessControlContext acc =
2573 sm == null ? null : AccessController.getContext();
2574 final PrivilegedAction<Void> pa =
2575 acc == null ? null : () -> { r.run() ; return null; };
2576 final Runnable pr =
2577 acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
2578 // Will do nothing if already registered.
2579 listeners.putIfAbsent(r, pr);
2580 return this;
2581 }
2582
2583 /**
2584 * Removes a previously registered configuration listener.
2585 *
2586 * Returns silently if the listener is not found.
2587 *
2588 * @param listener the configuration listener to remove.
2589 * @throws NullPointerException if the listener is null.
2590 * @throws SecurityException if a security manager exists and if the
2591 * caller does not have LoggingPermission("control").
2592 *
Iris Clark9d07dc02016-01-20 11:02:36 -08002593 * @since 9
Daniel Fuchs0a6f6d92014-09-26 11:29:29 +02002594 */
2595 public void removeConfigurationListener(Runnable listener) {
2596 final Runnable key = Objects.requireNonNull(listener);
2597 checkPermission();
2598 listeners.remove(key);
2599 }
2600
2601 private void invokeConfigurationListeners() {
2602 Throwable t = null;
2603
2604 // We're using an IdentityHashMap because we want to compare
2605 // keys using identity (==).
2606 // We don't want to loop within a block synchronized on 'listeners'
2607 // to avoid invoking listeners from yet another synchronized block.
2608 // So we're taking a snapshot of the values list to avoid the risk of
2609 // ConcurrentModificationException while looping.
2610 //
2611 for (Runnable c : listeners.values().toArray(new Runnable[0])) {
2612 try {
2613 c.run();
2614 } catch (ThreadDeath death) {
2615 throw death;
2616 } catch (Error | RuntimeException x) {
2617 if (t == null) t = x;
2618 else t.addSuppressed(x);
2619 }
2620 }
2621 // Listeners are not supposed to throw exceptions, but if that
2622 // happens, we will rethrow the first error or exception that is raised
2623 // after all listeners have been invoked.
2624 if (t instanceof Error) throw (Error)t;
2625 if (t instanceof RuntimeException) throw (RuntimeException)t;
2626 }
2627
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002628 /**
2629 * This class allows the {@link LoggingProviderImpl} to demand loggers on
2630 * behalf of system and application classes.
2631 */
2632 private static final class LoggingProviderAccess
2633 implements LoggingProviderImpl.LogManagerAccess,
2634 PrivilegedAction<Void> {
2635
2636 private LoggingProviderAccess() {
2637 }
2638
2639 /**
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002640 * Demands a logger on behalf of the given {@code module}.
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002641 * <p>
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002642 * If a named logger suitable for the given module is found
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002643 * returns it.
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002644 * Otherwise, creates a new logger suitable for the given module.
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002645 *
2646 * @param name The logger name.
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002647 * @param module The module on which behalf the logger is created/retrieved.
2648 * @return A logger for the given {@code module}.
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002649 *
2650 * @throws NullPointerException if {@code name} is {@code null}
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002651 * or {@code module} is {@code null}.
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002652 * @throws IllegalArgumentException if {@code manager} is not the default
2653 * LogManager.
2654 * @throws SecurityException if a security manager is present and the
2655 * calling code doesn't have the
2656 * {@link LoggingPermission LoggingPermission("demandLogger", null)}.
2657 */
2658 @Override
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002659 public Logger demandLoggerFor(LogManager manager, String name, Module module) {
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002660 if (manager != getLogManager()) {
2661 // having LogManager as parameter just ensures that the
2662 // caller will have initialized the LogManager before reaching
2663 // here.
2664 throw new IllegalArgumentException("manager");
2665 }
2666 Objects.requireNonNull(name);
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002667 Objects.requireNonNull(module);
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002668 SecurityManager sm = System.getSecurityManager();
2669 if (sm != null) {
2670 sm.checkPermission(controlPermission);
2671 }
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002672 if (isSystem(module)) {
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002673 return manager.demandSystemLogger(name,
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002674 Logger.SYSTEM_LOGGER_RB_NAME, module);
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002675 } else {
Daniel Fuchs1e0d1452016-04-27 18:04:16 +02002676 return manager.demandLogger(name, null, module);
Daniel Fuchsbd8942b2015-11-20 19:26:16 +01002677 }
2678 }
2679
2680 @Override
2681 public Void run() {
2682 LoggingProviderImpl.setLogManagerAccess(INSTANCE);
2683 return null;
2684 }
2685
2686 static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
2687 }
2688
2689 static {
2690 AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
2691 controlPermission);
2692 }
2693
J. Duke319a3b92007-12-01 00:00:00 +00002694}