1 /*
2 * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
3 * 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
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
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 *
21 * 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.
24 */
25
26 package java.util.logging;
27
28 import java.io.*;
29 import java.util.*;
30 import java.security.*;
31 import java.lang.ref.ReferenceQueue;
32 import java.lang.ref.WeakReference;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.nio.file.Paths;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 import java.util.concurrent.locks.ReentrantLock;
37 import java.util.function.BiFunction;
38 import java.util.function.Function;
39 import java.util.function.Predicate;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
42 import jdk.internal.misc.JavaAWTAccess;
43 import jdk.internal.misc.SharedSecrets;
44 import sun.util.logging.internal.LoggingProviderImpl;
45 import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
46
47 /**
48 * There is a single global LogManager object that is used to
49 * maintain a set of shared state about Loggers and log services.
50 * <p>
51 * This LogManager object:
52 * <ul>
53 * <li> Manages a hierarchical namespace of Logger objects. All
54 * named Loggers are stored in this namespace.
55 * <li> Manages a set of logging control properties. These are
56 * simple key-value pairs that can be used by Handlers and
57 * other logging objects to configure themselves.
58 * </ul>
59 * <p>
60 * The global LogManager object can be retrieved using LogManager.getLogManager().
61 * The LogManager object is created during class initialization and
62 * cannot subsequently be changed.
63 * <p>
64 * At startup the LogManager class is located using the
65 * java.util.logging.manager system property.
66 *
67 * <h3>LogManager Configuration</h3>
68 *
69 * A LogManager initializes the logging configuration via
70 * the {@link #readConfiguration()} method during LogManager initialization.
71 * By default, LogManager default configuration is used.
72 * The logging configuration read by LogManager must be in the
73 * {@linkplain Properties properties file} format.
74 * <p>
75 * The LogManager defines two optional system properties that allow control over
76 * the initial configuration, as specified in the {@link #readConfiguration()}
77 * method:
78 * <ul>
79 * <li>"java.util.logging.config.class"
80 * <li>"java.util.logging.config.file"
81 * </ul>
82 * <p>
83 * These two system properties may be specified on the command line to the "java"
84 * command, or as system property definitions passed to JNI_CreateJavaVM.
85 * <p>
86 * The {@linkplain Properties properties} for loggers and Handlers will have
87 * names starting with the dot-separated name for the handler or logger.<br>
88 * The global logging properties may include:
89 * <ul>
90 * <li>A property "handlers". This defines a whitespace or comma separated
91 * list of class names for handler classes to load and register as
92 * handlers on the root Logger (the Logger named ""). Each class
93 * name must be for a Handler class which has a default constructor.
94 * Note that these Handlers may be created lazily, when they are
95 * first used.
96 *
97 * <li>A property "<logger>.handlers". This defines a whitespace or
98 * comma separated list of class names for handlers classes to
99 * load and register as handlers to the specified logger. Each class
100 * name must be for a Handler class which has a default constructor.
101 * Note that these Handlers may be created lazily, when they are
102 * first used.
103 *
104 * <li>A property "<logger>.handlers.ensureCloseOnReset". This defines a
105 * a boolean value. If "<logger>.handlers" is not defined or is empty,
106 * this property is ignored. Otherwise it defaults to {@code true}. When the
107 * value is {@code true}, the handlers associated with the logger are guaranteed
108 * to be closed on {@linkplain #reset} and shutdown. This can be turned off
109 * by explicitly setting "<logger>.handlers.ensureCloseOnReset=false" in
110 * the configuration. Note that turning this property off causes the risk of
111 * introducing a resource leak, as the logger may get garbage collected before
112 * {@code reset()} is called, thus preventing its handlers from being closed
113 * on {@code reset()}. In that case it is the responsibility of the application
114 * to ensure that the handlers are closed before the logger is garbage
115 * collected.
116 *
117 * <li>A property "<logger>.useParentHandlers". This defines a boolean
118 * value. By default every logger calls its parent in addition to
119 * handling the logging message itself, this often result in messages
120 * being handled by the root logger as well. When setting this property
121 * to false a Handler needs to be configured for this logger otherwise
122 * no logging messages are delivered.
123 *
124 * <li>A property "config". This property is intended to allow
125 * arbitrary configuration code to be run. The property defines a
126 * whitespace or comma separated list of class names. A new instance will be
127 * created for each named class. The default constructor of each class
128 * may execute arbitrary code to update the logging configuration, such as
129 * setting logger levels, adding handlers, adding filters, etc.
130 * </ul>
131 * <p>
132 * Note that all classes loaded during LogManager configuration are
133 * first searched on the system class path before any user class path.
134 * That includes the LogManager class, any config classes, and any
135 * handler classes.
136 * <p>
137 * Loggers are organized into a naming hierarchy based on their
138 * dot separated names. Thus "a.b.c" is a child of "a.b", but
139 * "a.b1" and a.b2" are peers.
140 * <p>
141 * All properties whose names end with ".level" are assumed to define
142 * log levels for Loggers. Thus "foo.level" defines a log level for
143 * the logger called "foo" and (recursively) for any of its children
144 * in the naming hierarchy. Log Levels are applied in the order they
145 * are defined in the properties file. Thus level settings for child
146 * nodes in the tree should come after settings for their parents.
147 * The property name ".level" can be used to set the level for the
148 * root of the tree.
149 * <p>
150 * All methods on the LogManager object are multi-thread safe.
151 *
152 * @since 1.4
153 */
154
155 public class LogManager {
156 // The global LogManager object
157 private static final LogManager manager;
158
159 // 'props' is assigned within a lock but accessed without it.
160 // Declaring it volatile makes sure that another thread will not
161 // be able to see a partially constructed 'props' object.
162 // (seeing a partially constructed 'props' object can result in
163 // NPE being thrown in Hashtable.get(), because it leaves the door
164 // open for props.getProperties() to be called before the construcor
165 // of Hashtable is actually completed).
166 private volatile Properties props = new Properties();
167 private final static Level defaultLevel = Level.INFO;
168
169 // LoggerContext for system loggers and user loggers
170 private final LoggerContext systemContext = new SystemLoggerContext();
171 private final LoggerContext userContext = new LoggerContext();
172 // non final field - make it volatile to make sure that other threads
173 // will see the new value once ensureLogManagerInitialized() has finished
174 // executing.
175 private volatile Logger rootLogger;
176 // Have we done the primordial reading of the configuration file?
177 // (Must be done after a suitable amount of java.lang.System
178 // initialization has been done)
179 private volatile boolean readPrimordialConfiguration;
180 // Have we initialized global (root) handlers yet?
181 // This gets set to STATE_UNINITIALIZED in readConfiguration
182 private static final int
183 STATE_INITIALIZED = 0, // initial state
184 STATE_INITIALIZING = 1,
185 STATE_READING_CONFIG = 2,
186 STATE_UNINITIALIZED = 3,
187 STATE_SHUTDOWN = 4; // terminal state
188 private volatile int globalHandlersState; // = STATE_INITIALIZED;
189 // A concurrency lock for reset(), readConfiguration() and Cleaner.
190 private final ReentrantLock configurationLock = new ReentrantLock();
191
192 // This list contains the loggers for which some handlers have been
193 // explicitly configured in the configuration file.
194 // It prevents these loggers from being arbitrarily garbage collected.
195 private static final class CloseOnReset {
196 private final Logger logger;
197 private CloseOnReset(Logger ref) {
198 this.logger = Objects.requireNonNull(ref);
199 }
200 @Override
201 public boolean equals(Object other) {
202 return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;
203 }
204 @Override
205 public int hashCode() {
206 return System.identityHashCode(logger);
207 }
208 public Logger get() {
209 return logger;
210 }
211 public static CloseOnReset create(Logger logger) {
212 return new CloseOnReset(logger);
213 }
214 }
215 private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =
216 new CopyOnWriteArrayList<>();
217
218
219 private final Map<Object, Runnable> listeners =
220 Collections.synchronizedMap(new IdentityHashMap<>());
221
222 static {
223 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
224 @Override
225 public LogManager run() {
226 LogManager mgr = null;
227 String cname = null;
228 try {
229 cname = System.getProperty("java.util.logging.manager");
230 if (cname != null) {
231 try {
232 @SuppressWarnings("deprecation")
233 Object tmp = ClassLoader.getSystemClassLoader()
234 .loadClass(cname).newInstance();
235 mgr = (LogManager) tmp;
236 } catch (ClassNotFoundException ex) {
237 @SuppressWarnings("deprecation")
238 Object tmp = Thread.currentThread()
239 .getContextClassLoader().loadClass(cname).newInstance();
240 mgr = (LogManager) tmp;
241 }
242 }
243 } catch (Exception ex) {
244 System.err.println("Could not load Logmanager \"" + cname + "\"");
245 ex.printStackTrace();
246 }
247 if (mgr == null) {
248 mgr = new LogManager();
249 }
250 return mgr;
251
252 }
253 });
254 }
255
256 // This private class is used as a shutdown hook.
257 // It does a "reset" to close all open handlers.
258 private class Cleaner extends Thread {
259
260 private Cleaner() {
261 super(null, null, "Logging-Cleaner", 0, false);
262 /* 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
268 @Override
269 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
274 // 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();
280
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
291 * retrieved by calling LogManager.getLogManager.
292 */
293 protected LogManager() {
294 this(checkSubclassPermissions());
295 }
296
297 private LogManager(Void checked) {
298
299 // 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
308 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
321 /**
322 * 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 //
356 configurationLock.lock();
357 try {
358 // 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 // create root logger before reading primordial
389 // configuration - to ensure that it will be added
390 // before the global logger, and not after.
391 final Logger root = owner.rootLogger = owner.new RootLogger();
392
393 // Read configuration.
394 owner.readPrimordialConfiguration();
395
396 // Create and retain Logger for the root of the namespace.
397 owner.addLogger(root);
398
399 // Initialize level if not yet initialized
400 if (!root.isLevelInitialized()) {
401 root.setLevel(defaultLevel);
402 }
403
404 // Adding the global Logger.
405 // Do not call Logger.getGlobal() here as this might trigger
406 // subtle inter-dependency issues.
407 @SuppressWarnings("deprecation")
408 final Logger global = Logger.global;
409
410 // Make sure the global logger will be registered in the
411 // global manager
412 owner.addLogger(global);
413 return null;
414 }
415 });
416 } finally {
417 initializationDone = true;
418 }
419 } finally {
420 configurationLock.unlock();
421 }
422 }
423
424 /**
425 * Returns the global LogManager object.
426 * @return the global LogManager object
427 */
428 public static LogManager getLogManager() {
429 if (manager != null) {
430 manager.ensureLogManagerInitialized();
431 }
432 return manager;
433 }
434
435 private void readPrimordialConfiguration() { // must be called while holding configurationLock
436 if (!readPrimordialConfiguration) {
437 // If System.in/out/err are null, it's a good
438 // indication that we're still in the
439 // bootstrapping phase
440 if (System.out == null) {
441 return;
442 }
443 readPrimordialConfiguration = true;
444 try {
445 readConfiguration();
446
447 // Platform loggers begin to delegate to java.util.logging.Logger
448 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
449
450 } catch (Exception ex) {
451 assert false : "Exception raised while reading logging configuration: " + ex;
452 }
453 }
454 }
455
456 // LoggerContext maps from AppContext
457 private WeakHashMap<Object, LoggerContext> contextsMap = null;
458
459 // Returns the LoggerContext for the user code (i.e. application or AppContext).
460 // Loggers are isolated from each AppContext.
461 private LoggerContext getUserContext() {
462 LoggerContext context = null;
463
464 SecurityManager sm = System.getSecurityManager();
465 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
466 if (sm != null && javaAwtAccess != null) {
467 // for each applet, it has its own LoggerContext isolated from others
468 final Object ecx = javaAwtAccess.getAppletContext();
469 if (ecx != null) {
470 synchronized (javaAwtAccess) {
471 // find the AppContext of the applet code
472 // will be null if we are in the main app context.
473 if (contextsMap == null) {
474 contextsMap = new WeakHashMap<>();
475 }
476 context = contextsMap.get(ecx);
477 if (context == null) {
478 // Create a new LoggerContext for the applet.
479 context = new LoggerContext();
480 contextsMap.put(ecx, context);
481 }
482 }
483 }
484 }
485 // for standalone app, return userContext
486 return context != null ? context : userContext;
487 }
488
489 // The system context.
490 final LoggerContext getSystemContext() {
491 return systemContext;
492 }
493
494 private List<LoggerContext> contexts() {
495 List<LoggerContext> cxs = new ArrayList<>();
496 cxs.add(getSystemContext());
497 cxs.add(getUserContext());
498 return cxs;
499 }
500
501 // Find or create a specified logger instance. If a logger has
502 // already been created with the given name it is returned.
503 // Otherwise a new logger instance is created and registered
504 // in the LogManager global namespace.
505 // This method will always return a non-null Logger object.
506 // Synchronization is not required here. All synchronization for
507 // adding a new Logger object is handled by addLogger().
508 //
509 // This method must delegate to the LogManager implementation to
510 // add a new Logger or return the one that has been added previously
511 // as a LogManager subclass may override the addLogger, getLogger,
512 // readConfiguration, and other methods.
513 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
514 final Module module = caller == null ? null : caller.getModule();
515 return demandLogger(name, resourceBundleName, module);
516 }
517
518 Logger demandLogger(String name, String resourceBundleName, Module module) {
519 Logger result = getLogger(name);
520 if (result == null) {
521 // only allocate the new logger once
522 Logger newLogger = new Logger(name, resourceBundleName,
523 module, this, false);
524 do {
525 if (addLogger(newLogger)) {
526 // We successfully added the new Logger that we
527 // created above so return it without refetching.
528 return newLogger;
529 }
530
531 // We didn't add the new Logger that we created above
532 // because another thread added a Logger with the same
533 // name after our null check above and before our call
534 // to addLogger(). We have to refetch the Logger because
535 // addLogger() returns a boolean instead of the Logger
536 // reference itself. However, if the thread that created
537 // the other Logger is not holding a strong reference to
538 // the other Logger, then it is possible for the other
539 // Logger to be GC'ed after we saw it in addLogger() and
540 // before we can refetch it. If it has been GC'ed then
541 // we'll just loop around and try again.
542 result = getLogger(name);
543 } while (result == null);
544 }
545 return result;
546 }
547
548 Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {
549 final Module module = caller == null ? null : caller.getModule();
550 return demandSystemLogger(name, resourceBundleName, module);
551 }
552
553 Logger demandSystemLogger(String name, String resourceBundleName, Module module) {
554 // Add a system logger in the system context's namespace
555 final Logger sysLogger = getSystemContext()
556 .demandLogger(name, resourceBundleName, module);
557
558 // Add the system logger to the LogManager's namespace if not exist
559 // so that there is only one single logger of the given name.
560 // System loggers are visible to applications unless a logger of
561 // the same name has been added.
562 Logger logger;
563 do {
564 // First attempt to call addLogger instead of getLogger
565 // This would avoid potential bug in custom LogManager.getLogger
566 // implementation that adds a logger if does not exist
567 if (addLogger(sysLogger)) {
568 // successfully added the new system logger
569 logger = sysLogger;
570 } else {
571 logger = getLogger(name);
572 }
573 } while (logger == null);
574
575 // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
576 if (logger != sysLogger) {
577 // if logger already exists we merge the two logger configurations.
578 final Logger l = logger;
579 AccessController.doPrivileged(new PrivilegedAction<Void>() {
580 @Override
581 public Void run() {
582 l.mergeWithSystemLogger(sysLogger);
583 return null;
584 }
585 });
586 }
587 return sysLogger;
588 }
589
590 // LoggerContext maintains the logger namespace per context.
591 // The default LogManager implementation has one system context and user
592 // context. The system context is used to maintain the namespace for
593 // all system loggers and is queried by the system code. If a system logger
594 // doesn't exist in the user context, it'll also be added to the user context.
595 // The user context is queried by the user code and all other loggers are
596 // added in the user context.
597 class LoggerContext {
598 // Table of named Loggers that maps names to Loggers.
599 private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =
600 new ConcurrentHashMap<>();
601 // Tree of named Loggers
602 private final LogNode root;
603 private LoggerContext() {
604 this.root = new LogNode(null, this);
605 }
606
607
608 // Tells whether default loggers are required in this context.
609 // If true, the default loggers will be lazily added.
610 final boolean requiresDefaultLoggers() {
611 final boolean requiresDefaultLoggers = (getOwner() == manager);
612 if (requiresDefaultLoggers) {
613 getOwner().ensureLogManagerInitialized();
614 }
615 return requiresDefaultLoggers;
616 }
617
618 // This context's LogManager.
619 final LogManager getOwner() {
620 return LogManager.this;
621 }
622
623 // This context owner's root logger, which if not null, and if
624 // the context requires default loggers, will be added to the context
625 // logger's tree.
626 final Logger getRootLogger() {
627 return getOwner().rootLogger;
628 }
629
630 // The global logger, which if not null, and if
631 // the context requires default loggers, will be added to the context
632 // logger's tree.
633 final Logger getGlobalLogger() {
634 @SuppressWarnings("deprecation") // avoids initialization cycles.
635 final Logger global = Logger.global;
636 return global;
637 }
638
639 Logger demandLogger(String name, String resourceBundleName, Module module) {
640 // a LogManager subclass may have its own implementation to add and
641 // get a Logger. So delegate to the LogManager to do the work.
642 final LogManager owner = getOwner();
643 return owner.demandLogger(name, resourceBundleName, module);
644 }
645
646
647 // Due to subtle deadlock issues getUserContext() no longer
648 // calls addLocalLogger(rootLogger);
649 // Therefore - we need to add the default loggers later on.
650 // Checks that the context is properly initialized
651 // This is necessary before calling e.g. find(name)
652 // or getLoggerNames()
653 //
654 private void ensureInitialized() {
655 if (requiresDefaultLoggers()) {
656 // Ensure that the root and global loggers are set.
657 ensureDefaultLogger(getRootLogger());
658 ensureDefaultLogger(getGlobalLogger());
659 }
660 }
661
662
663 Logger findLogger(String name) {
664 // Attempt to find logger without locking.
665 LoggerWeakRef ref = namedLoggers.get(name);
666 Logger logger = ref == null ? null : ref.get();
667
668 // if logger is not null, then we can return it right away.
669 // if name is "" or "global" and logger is null
670 // we need to fall through and check that this context is
671 // initialized.
672 // if ref is not null and logger is null we also need to
673 // fall through.
674 if (logger != null || (ref == null && !name.isEmpty()
675 && !name.equals(Logger.GLOBAL_LOGGER_NAME))) {
676 return logger;
677 }
678
679 // We either found a stale reference, or we were looking for
680 // "" or "global" and didn't find them.
681 // Make sure context is initialized (has the default loggers),
682 // and look up again, cleaning the stale reference if it hasn't
683 // been cleaned up in between. All this needs to be done inside
684 // a synchronized block.
685 synchronized(this) {
686 // ensure that this context is properly initialized before
687 // looking for loggers.
688 ensureInitialized();
689 ref = namedLoggers.get(name);
690 if (ref == null) {
691 return null;
692 }
693 logger = ref.get();
694 if (logger == null) {
695 // The namedLoggers map holds stale weak reference
696 // to a logger which has been GC-ed.
697 ref.dispose();
698 }
699 return logger;
700 }
701 }
702
703 // This method is called before adding a logger to the
704 // context.
705 // 'logger' is the context that will be added.
706 // This method will ensure that the defaults loggers are added
707 // before adding 'logger'.
708 //
709 private void ensureAllDefaultLoggers(Logger logger) {
710 if (requiresDefaultLoggers()) {
711 final String name = logger.getName();
712 if (!name.isEmpty()) {
713 ensureDefaultLogger(getRootLogger());
714 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
715 ensureDefaultLogger(getGlobalLogger());
716 }
717 }
718 }
719 }
720
721 private void ensureDefaultLogger(Logger logger) {
722 // Used for lazy addition of root logger and global logger
723 // to a LoggerContext.
724
725 // This check is simple sanity: we do not want that this
726 // method be called for anything else than Logger.global
727 // or owner.rootLogger.
728 if (!requiresDefaultLoggers() || logger == null
729 || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
730
731 // the case where we have a non null logger which is neither
732 // Logger.global nor manager.rootLogger indicates a serious
733 // issue - as ensureDefaultLogger should never be called
734 // with any other loggers than one of these two (or null - if
735 // e.g manager.rootLogger is not yet initialized)...
736 assert logger == null;
737
738 return;
739 }
740
741 // Adds the logger if it's not already there.
742 if (!namedLoggers.containsKey(logger.getName())) {
743 // It is important to prevent addLocalLogger to
744 // call ensureAllDefaultLoggers when we're in the process
745 // off adding one of those default loggers - as this would
746 // immediately cause a stack overflow.
747 // Therefore we must pass addDefaultLoggersIfNeeded=false,
748 // even if requiresDefaultLoggers is true.
749 addLocalLogger(logger, false);
750 }
751 }
752
753 boolean addLocalLogger(Logger logger) {
754 // no need to add default loggers if it's not required
755 return addLocalLogger(logger, requiresDefaultLoggers());
756 }
757
758 // Add a logger to this context. This method will only set its level
759 // and process parent loggers. It doesn't set its handlers.
760 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
761 // addDefaultLoggersIfNeeded serves to break recursion when adding
762 // default loggers. If we're adding one of the default loggers
763 // (we're being called from ensureDefaultLogger()) then
764 // addDefaultLoggersIfNeeded will be false: we don't want to
765 // call ensureAllDefaultLoggers again.
766 //
767 // Note: addDefaultLoggersIfNeeded can also be false when
768 // requiresDefaultLoggers is false - since calling
769 // ensureAllDefaultLoggers would have no effect in this case.
770 if (addDefaultLoggersIfNeeded) {
771 ensureAllDefaultLoggers(logger);
772 }
773
774 final String name = logger.getName();
775 if (name == null) {
776 throw new NullPointerException();
777 }
778 LoggerWeakRef ref = namedLoggers.get(name);
779 if (ref != null) {
780 if (ref.get() == null) {
781 // It's possible that the Logger was GC'ed after a
782 // drainLoggerRefQueueBounded() call above so allow
783 // a new one to be registered.
784 ref.dispose();
785 } else {
786 // We already have a registered logger with the given name.
787 return false;
788 }
789 }
790
791 // We're adding a new logger.
792 // Note that we are creating a weak reference here.
793 final LogManager owner = getOwner();
794 logger.setLogManager(owner);
795 ref = owner.new LoggerWeakRef(logger);
796
797 // Apply any initial level defined for the new logger, unless
798 // the logger's level is already initialized
799 Level level = owner.getLevelProperty(name + ".level", null);
800 if (level != null && !logger.isLevelInitialized()) {
801 doSetLevel(logger, level);
802 }
803
804 // instantiation of the handler is done in the LogManager.addLogger
805 // implementation as a handler class may be only visible to LogManager
806 // subclass for the custom log manager case
807 processParentHandlers(logger, name, VisitedLoggers.NEVER);
808
809 // Find the new node and its parent.
810 LogNode node = getNode(name);
811 node.loggerRef = ref;
812 Logger parent = null;
813 LogNode nodep = node.parent;
814 while (nodep != null) {
815 LoggerWeakRef nodeRef = nodep.loggerRef;
816 if (nodeRef != null) {
817 parent = nodeRef.get();
818 if (parent != null) {
819 break;
820 }
821 }
822 nodep = nodep.parent;
823 }
824
825 if (parent != null) {
826 doSetParent(logger, parent);
827 }
828 // Walk over the children and tell them we are their new parent.
829 node.walkAndSetParent(logger);
830 // new LogNode is ready so tell the LoggerWeakRef about it
831 ref.setNode(node);
832
833 // Do not publish 'ref' in namedLoggers before the logger tree
834 // is fully updated - because the named logger will be visible as
835 // soon as it is published in namedLoggers (findLogger takes
836 // benefit of the ConcurrentHashMap implementation of namedLoggers
837 // to avoid synchronizing on retrieval when that is possible).
838 namedLoggers.put(name, ref);
839 return true;
840 }
841
842 void removeLoggerRef(String name, LoggerWeakRef ref) {
843 namedLoggers.remove(name, ref);
844 }
845
846 synchronized Enumeration<String> getLoggerNames() {
847 // ensure that this context is properly initialized before
848 // returning logger names.
849 ensureInitialized();
850 return Collections.enumeration(namedLoggers.keySet());
851 }
852
853 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
854 // parents have levels or handlers defined, make sure they are instantiated.
855 private void processParentHandlers(final Logger logger, final String name,
856 Predicate<Logger> visited) {
857 final LogManager owner = getOwner();
858 AccessController.doPrivileged(new PrivilegedAction<Void>() {
859 @Override
860 public Void run() {
861 if (logger != owner.rootLogger) {
862 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
863 if (!useParent) {
864 logger.setUseParentHandlers(false);
865 }
866 }
867 return null;
868 }
869 });
870
871 int ix = 1;
872 for (;;) {
873 int ix2 = name.indexOf('.', ix);
874 if (ix2 < 0) {
875 break;
876 }
877 String pname = name.substring(0, ix2);
878 if (owner.getProperty(pname + ".level") != null ||
879 owner.getProperty(pname + ".handlers") != null) {
880 // This pname has a level/handlers definition.
881 // Make sure it exists.
882 if (visited.test(demandLogger(pname, null, null))) {
883 break;
884 }
885 }
886 ix = ix2+1;
887 }
888 }
889
890 // Gets a node in our tree of logger nodes.
891 // If necessary, create it.
892 LogNode getNode(String name) {
893 if (name == null || name.equals("")) {
894 return root;
895 }
896 LogNode node = root;
897 while (name.length() > 0) {
898 int ix = name.indexOf('.');
899 String head;
900 if (ix > 0) {
901 head = name.substring(0, ix);
902 name = name.substring(ix + 1);
903 } else {
904 head = name;
905 name = "";
906 }
907 if (node.children == null) {
908 node.children = new HashMap<>();
909 }
910 LogNode child = node.children.get(head);
911 if (child == null) {
912 child = new LogNode(node, this);
913 node.children.put(head, child);
914 }
915 node = child;
916 }
917 return node;
918 }
919 }
920
921 final class SystemLoggerContext extends LoggerContext {
922 // Add a system logger in the system context's namespace as well as
923 // in the LogManager's namespace if not exist so that there is only
924 // one single logger of the given name. System loggers are visible
925 // to applications unless a logger of the same name has been added.
926 @Override
927 Logger demandLogger(String name, String resourceBundleName,
928 Module module) {
929 Logger result = findLogger(name);
930 if (result == null) {
931 // only allocate the new system logger once
932 Logger newLogger = new Logger(name, resourceBundleName,
933 module, getOwner(), true);
934 do {
935 if (addLocalLogger(newLogger)) {
936 // We successfully added the new Logger that we
937 // created above so return it without refetching.
938 result = newLogger;
939 } else {
940 // We didn't add the new Logger that we created above
941 // because another thread added a Logger with the same
942 // name after our null check above and before our call
943 // to addLogger(). We have to refetch the Logger because
944 // addLogger() returns a boolean instead of the Logger
945 // reference itself. However, if the thread that created
946 // the other Logger is not holding a strong reference to
947 // the other Logger, then it is possible for the other
948 // Logger to be GC'ed after we saw it in addLogger() and
949 // before we can refetch it. If it has been GC'ed then
950 // we'll just loop around and try again.
951 result = findLogger(name);
952 }
953 } while (result == null);
954 }
955 return result;
956 }
957 }
958
959 // Add new per logger handlers.
960 // We need to raise privilege here. All our decisions will
961 // be made based on the logging configuration, which can
962 // only be modified by trusted code.
963 private void loadLoggerHandlers(final Logger logger, final String name,
964 final String handlersPropertyName)
965 {
966 AccessController.doPrivileged(new PrivilegedAction<Void>() {
967 @Override
968 public Void run() {
969 setLoggerHandlers(logger, name, handlersPropertyName,
970 createLoggerHandlers(name, handlersPropertyName));
971 return null;
972 }
973 });
974 }
975
976 private void setLoggerHandlers(final Logger logger, final String name,
977 final String handlersPropertyName,
978 List<Handler> handlers)
979 {
980 final boolean ensureCloseOnReset = ! handlers.isEmpty()
981 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
982 int count = 0;
983 for (Handler hdl : handlers) {
984 logger.addHandler(hdl);
985 if (++count == 1 && ensureCloseOnReset) {
986 // add this logger to the closeOnResetLoggers list.
987 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
988 }
989 }
990 }
991
992 private List<Handler> createLoggerHandlers(final String name,
993 final String handlersPropertyName)
994 {
995 String names[] = parseClassNames(handlersPropertyName);
996 List<Handler> handlers = new ArrayList<>(names.length);
997 for (String type : names) {
998 try {
999 @SuppressWarnings("deprecation")
1000 Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance();
1001 Handler hdl = (Handler) o;
1002 // Check if there is a property defining the
1003 // this handler's level.
1004 String levs = getProperty(type + ".level");
1005 if (levs != null) {
1006 Level l = Level.findLevel(levs);
1007 if (l != null) {
1008 hdl.setLevel(l);
1009 } else {
1010 // Probably a bad level. Drop through.
1011 System.err.println("Can't set level for " + type);
1012 }
1013 }
1014 // Add this Handler to the logger
1015 handlers.add(hdl);
1016 } catch (Exception ex) {
1017 System.err.println("Can't load log handler \"" + type + "\"");
1018 System.err.println("" + ex);
1019 ex.printStackTrace();
1020 }
1021 }
1022
1023 return handlers;
1024 }
1025
1026
1027 // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1028 // that have been GC'ed.
1029 private final ReferenceQueue<Logger> loggerRefQueue
1030 = new ReferenceQueue<>();
1031
1032 // Package-level inner class.
1033 // Helper class for managing WeakReferences to Logger objects.
1034 //
1035 // LogManager.namedLoggers
1036 // - has weak references to all named Loggers
1037 // - namedLoggers keeps the LoggerWeakRef objects for the named
1038 // Loggers around until we can deal with the book keeping for
1039 // the named Logger that is being GC'ed.
1040 // LogManager.LogNode.loggerRef
1041 // - has a weak reference to a named Logger
1042 // - the LogNode will also keep the LoggerWeakRef objects for
1043 // the named Loggers around; currently LogNodes never go away.
1044 // Logger.kids
1045 // - has a weak reference to each direct child Logger; this
1046 // includes anonymous and named Loggers
1047 // - anonymous Loggers are always children of the rootLogger
1048 // which is a strong reference; rootLogger.kids keeps the
1049 // LoggerWeakRef objects for the anonymous Loggers around
1050 // until we can deal with the book keeping.
1051 //
1052 final class LoggerWeakRef extends WeakReference<Logger> {
1053 private String name; // for namedLoggers cleanup
1054 private LogNode node; // for loggerRef cleanup
1055 private WeakReference<Logger> parentRef; // for kids cleanup
1056 private boolean disposed = false; // avoid calling dispose twice
1057
1058 LoggerWeakRef(Logger logger) {
1059 super(logger, loggerRefQueue);
1060
1061 name = logger.getName(); // save for namedLoggers cleanup
1062 }
1063
1064 // dispose of this LoggerWeakRef object
1065 void dispose() {
1066 // Avoid calling dispose twice. When a Logger is gc'ed, its
1067 // LoggerWeakRef will be enqueued.
1068 // However, a new logger of the same name may be added (or looked
1069 // up) before the queue is drained. When that happens, dispose()
1070 // will be called by addLocalLogger() or findLogger().
1071 // Later when the queue is drained, dispose() will be called again
1072 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
1073 // avoids processing the data twice (even though the code should
1074 // now be reentrant).
1075 synchronized(this) {
1076 // Note to maintainers:
1077 // Be careful not to call any method that tries to acquire
1078 // another lock from within this block - as this would surely
1079 // lead to deadlocks, given that dispose() can be called by
1080 // multiple threads, and from within different synchronized
1081 // methods/blocks.
1082 if (disposed) return;
1083 disposed = true;
1084 }
1085
1086 final LogNode n = node;
1087 if (n != null) {
1088 // n.loggerRef can only be safely modified from within
1089 // a lock on LoggerContext. removeLoggerRef is already
1090 // synchronized on LoggerContext so calling
1091 // n.context.removeLoggerRef from within this lock is safe.
1092 synchronized (n.context) {
1093 // if we have a LogNode, then we were a named Logger
1094 // so clear namedLoggers weak ref to us
1095 n.context.removeLoggerRef(name, this);
1096 name = null; // clear our ref to the Logger's name
1097
1098 // LogNode may have been reused - so only clear
1099 // LogNode.loggerRef if LogNode.loggerRef == this
1100 if (n.loggerRef == this) {
1101 n.loggerRef = null; // clear LogNode's weak ref to us
1102 }
1103 node = null; // clear our ref to LogNode
1104 }
1105 }
1106
1107 if (parentRef != null) {
1108 // this LoggerWeakRef has or had a parent Logger
1109 Logger parent = parentRef.get();
1110 if (parent != null) {
1111 // the parent Logger is still there so clear the
1112 // parent Logger's weak ref to us
1113 parent.removeChildLogger(this);
1114 }
1115 parentRef = null; // clear our weak ref to the parent Logger
1116 }
1117 }
1118
1119 // set the node field to the specified value
1120 void setNode(LogNode node) {
1121 this.node = node;
1122 }
1123
1124 // set the parentRef field to the specified value
1125 void setParentRef(WeakReference<Logger> parentRef) {
1126 this.parentRef = parentRef;
1127 }
1128 }
1129
1130 // Package-level method.
1131 // Drain some Logger objects that have been GC'ed.
1132 //
1133 // drainLoggerRefQueueBounded() is called by addLogger() below
1134 // and by Logger.getAnonymousLogger(String) so we'll drain up to
1135 // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
1136 //
1137 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1138 // us about a 50/50 mix in increased weak ref counts versus
1139 // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1140 // Here are stats for cleaning up sets of 400 anonymous Loggers:
1141 // - test duration 1 minute
1142 // - sample size of 125 sets of 400
1143 // - average: 1.99 ms
1144 // - minimum: 0.57 ms
1145 // - maximum: 25.3 ms
1146 //
1147 // The same config gives us a better decreased weak ref count
1148 // than increased weak ref count in the LoggerWeakRefLeak test.
1149 // Here are stats for cleaning up sets of 400 named Loggers:
1150 // - test duration 2 minutes
1151 // - sample size of 506 sets of 400
1152 // - average: 0.57 ms
1153 // - minimum: 0.02 ms
1154 // - maximum: 10.9 ms
1155 //
1156 private final static int MAX_ITERATIONS = 400;
1157 final void drainLoggerRefQueueBounded() {
1158 for (int i = 0; i < MAX_ITERATIONS; i++) {
1159 if (loggerRefQueue == null) {
1160 // haven't finished loading LogManager yet
1161 break;
1162 }
1163
1164 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1165 if (ref == null) {
1166 break;
1167 }
1168 // a Logger object has been GC'ed so clean it up
1169 ref.dispose();
1170 }
1171 }
1172
1173 /**
1174 * Add a named logger. This does nothing and returns false if a logger
1175 * with the same name is already registered.
1176 * <p>
1177 * The Logger factory methods call this method to register each
1178 * newly created Logger.
1179 * <p>
1180 * The application should retain its own reference to the Logger
1181 * object to avoid it being garbage collected. The LogManager
1182 * may only retain a weak reference.
1183 *
1184 * @param logger the new logger.
1185 * @return true if the argument logger was registered successfully,
1186 * false if a logger of that name already exists.
1187 * @exception NullPointerException if the logger name is null.
1188 */
1189 public boolean addLogger(Logger logger) {
1190 final String name = logger.getName();
1191 if (name == null) {
1192 throw new NullPointerException();
1193 }
1194 drainLoggerRefQueueBounded();
1195 LoggerContext cx = getUserContext();
1196 if (cx.addLocalLogger(logger) || forceLoadHandlers(logger)) {
1197 // Do we have a per logger handler too?
1198 // Note: this will add a 200ms penalty
1199 loadLoggerHandlers(logger, name, name + ".handlers");
1200 return true;
1201 } else {
1202 return false;
1203 }
1204 }
1205
1206
1207 // Checks whether the given logger is a special logger
1208 // that still requires handler initialization.
1209 // This method will only return true for the root and
1210 // global loggers and only if called by the thread that
1211 // performs initialization of the LogManager, during that
1212 // initialization. Must only be called by addLogger.
1213 @SuppressWarnings("deprecation")
1214 private boolean forceLoadHandlers(Logger logger) {
1215 // Called just after reading the primordial configuration, in
1216 // the same thread that reads it.
1217 // The root and global logger would already be present in the context
1218 // by this point, but we would not have called loadLoggerHandlers
1219 // yet.
1220 return (logger == rootLogger || logger == Logger.global)
1221 && !initializationDone
1222 && initializedCalled
1223 && configurationLock.isHeldByCurrentThread();
1224 }
1225
1226 // Private method to set a level on a logger.
1227 // If necessary, we raise privilege before doing the call.
1228 private static void doSetLevel(final Logger logger, final Level level) {
1229 SecurityManager sm = System.getSecurityManager();
1230 if (sm == null) {
1231 // There is no security manager, so things are easy.
1232 logger.setLevel(level);
1233 return;
1234 }
1235 // There is a security manager. Raise privilege before
1236 // calling setLevel.
1237 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1238 @Override
1239 public Object run() {
1240 logger.setLevel(level);
1241 return null;
1242 }});
1243 }
1244
1245 // Private method to set a parent on a logger.
1246 // If necessary, we raise privilege before doing the setParent call.
1247 private static void doSetParent(final Logger logger, final Logger parent) {
1248 SecurityManager sm = System.getSecurityManager();
1249 if (sm == null) {
1250 // There is no security manager, so things are easy.
1251 logger.setParent(parent);
1252 return;
1253 }
1254 // There is a security manager. Raise privilege before
1255 // calling setParent.
1256 AccessController.doPrivileged(new PrivilegedAction<Object>() {
1257 @Override
1258 public Object run() {
1259 logger.setParent(parent);
1260 return null;
1261 }});
1262 }
1263
1264 /**
1265 * Method to find a named logger.
1266 * <p>
1267 * Note that since untrusted code may create loggers with
1268 * arbitrary names this method should not be relied on to
1269 * find Loggers for security sensitive logging.
1270 * It is also important to note that the Logger associated with the
1271 * String {@code name} may be garbage collected at any time if there
1272 * is no strong reference to the Logger. The caller of this method
1273 * must check the return value for null in order to properly handle
1274 * the case where the Logger has been garbage collected.
1275 *
1276 * @param name name of the logger
1277 * @return matching logger or null if none is found
1278 */
1279 public Logger getLogger(String name) {
1280 return getUserContext().findLogger(name);
1281 }
1282
1283 /**
1284 * Get an enumeration of known logger names.
1285 * <p>
1286 * Note: Loggers may be added dynamically as new classes are loaded.
1287 * This method only reports on the loggers that are currently registered.
1288 * It is also important to note that this method only returns the name
1289 * of a Logger, not a strong reference to the Logger itself.
1290 * The returned String does nothing to prevent the Logger from being
1291 * garbage collected. In particular, if the returned name is passed
1292 * to {@code LogManager.getLogger()}, then the caller must check the
1293 * return value from {@code LogManager.getLogger()} for null to properly
1294 * handle the case where the Logger has been garbage collected in the
1295 * time since its name was returned by this method.
1296 *
1297 * @return enumeration of logger name strings
1298 */
1299 public Enumeration<String> getLoggerNames() {
1300 return getUserContext().getLoggerNames();
1301 }
1302
1303 /**
1304 * Reads and initializes the logging configuration.
1305 * <p>
1306 * If the "java.util.logging.config.class" system property is set, then the
1307 * property value is treated as a class name. The given class will be
1308 * loaded, an object will be instantiated, and that object's constructor
1309 * is responsible for reading in the initial configuration. (That object
1310 * may use other system properties to control its configuration.) The
1311 * alternate configuration class can use {@code readConfiguration(InputStream)}
1312 * to define properties in the LogManager.
1313 * <p>
1314 * If "java.util.logging.config.class" system property is <b>not</b> set,
1315 * then this method will read the initial configuration from a properties
1316 * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1317 * the configuration. The "java.util.logging.config.file" system property can be used
1318 * to specify the properties file that will be read as the initial configuration;
1319 * if not set, then the LogManager default configuration is used.
1320 * The default configuration is typically loaded from the
1321 * properties file "{@code conf/logging.properties}" in the Java installation
1322 * directory.
1323 *
1324 * <p>
1325 * Any {@linkplain #addConfigurationListener registered configuration
1326 * listener} will be invoked after the properties are read.
1327 *
1328 * @apiNote This {@code readConfiguration} method should only be used for
1329 * initializing the configuration during LogManager initialization or
1330 * used with the "java.util.logging.config.class" property.
1331 * When this method is called after loggers have been created, and
1332 * the "java.util.logging.config.class" system property is not set, all
1333 * existing loggers will be {@linkplain #reset() reset}. Then any
1334 * existing loggers that have a level property specified in the new
1335 * configuration stream will be {@linkplain
1336 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1337 * <p>
1338 * To properly update the logging configuration, use the
1339 * {@link #updateConfiguration(java.util.function.Function)} or
1340 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1341 * methods instead.
1342 *
1343 * @throws SecurityException if a security manager exists and if
1344 * the caller does not have LoggingPermission("control").
1345 * @throws IOException if there are IO problems reading the configuration.
1346 */
1347 public void readConfiguration() throws IOException, SecurityException {
1348 checkPermission();
1349
1350 // if a configuration class is specified, load it and use it.
1351 String cname = System.getProperty("java.util.logging.config.class");
1352 if (cname != null) {
1353 try {
1354 // Instantiate the named class. It is its constructor's
1355 // responsibility to initialize the logging configuration, by
1356 // calling readConfiguration(InputStream) with a suitable stream.
1357 try {
1358 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1359 @SuppressWarnings("deprecation")
1360 Object witness = clz.newInstance();
1361 return;
1362 } catch (ClassNotFoundException ex) {
1363 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1364 @SuppressWarnings("deprecation")
1365 Object witness = clz.newInstance();
1366 return;
1367 }
1368 } catch (Exception ex) {
1369 System.err.println("Logging configuration class \"" + cname + "\" failed");
1370 System.err.println("" + ex);
1371 // keep going and useful config file.
1372 }
1373 }
1374
1375 String fname = getConfigurationFileName();
1376 try (final InputStream in = new FileInputStream(fname)) {
1377 final BufferedInputStream bin = new BufferedInputStream(in);
1378 readConfiguration(bin);
1379 }
1380 }
1381
1382 String getConfigurationFileName() throws IOException {
1383 String fname = System.getProperty("java.util.logging.config.file");
1384 if (fname == null) {
1385 fname = System.getProperty("java.home");
1386 if (fname == null) {
1387 throw new Error("Can't find java.home ??");
1388 }
1389 fname = Paths.get(fname, "conf", "logging.properties")
1390 .toAbsolutePath().normalize().toString();
1391 }
1392 return fname;
1393 }
1394
1395 /**
1396 * Reset the logging configuration.
1397 * <p>
1398 * For all named loggers, the reset operation removes and closes
1399 * all Handlers and (except for the root logger) sets the level
1400 * to {@code null}. The root logger's level is set to {@code Level.INFO}.
1401 *
1402 * @apiNote Calling this method also clears the LogManager {@linkplain
1403 * #getProperty(java.lang.String) properties}. The {@link
1404 * #updateConfiguration(java.util.function.Function)
1405 * updateConfiguration(Function)} or
1406 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)
1407 * updateConfiguration(InputStream, Function)} method can be used to
1408 * properly update to a new configuration.
1409 *
1410 * @throws SecurityException if a security manager exists and if
1411 * the caller does not have LoggingPermission("control").
1412 */
1413
1414 public void reset() throws SecurityException {
1415 checkPermission();
1416
1417 List<CloseOnReset> persistent;
1418
1419 // We don't want reset() and readConfiguration()
1420 // to run in parallel
1421 configurationLock.lock();
1422 try {
1423 // install new empty properties
1424 props = new Properties();
1425 // make sure we keep the loggers persistent until reset is done.
1426 // Those are the loggers for which we previously created a
1427 // handler from the configuration, and we need to prevent them
1428 // from being gc'ed until those handlers are closed.
1429 persistent = new ArrayList<>(closeOnResetLoggers);
1430 closeOnResetLoggers.clear();
1431
1432 // if reset has been called from shutdown-hook (Cleaner),
1433 // or if reset has been called from readConfiguration() which
1434 // already holds the lock and will change the state itself,
1435 // then do not change state here...
1436 if (globalHandlersState != STATE_SHUTDOWN &&
1437 globalHandlersState != STATE_READING_CONFIG) {
1438 // ...else user called reset()...
1439 // Since we are doing a reset we no longer want to initialize
1440 // the global handlers, if they haven't been initialized yet.
1441 globalHandlersState = STATE_INITIALIZED;
1442 }
1443
1444 for (LoggerContext cx : contexts()) {
1445 resetLoggerContext(cx);
1446 }
1447
1448 persistent.clear();
1449 } finally {
1450 configurationLock.unlock();
1451 }
1452 }
1453
1454 private void resetLoggerContext(LoggerContext cx) {
1455 Enumeration<String> enum_ = cx.getLoggerNames();
1456 while (enum_.hasMoreElements()) {
1457 String name = enum_.nextElement();
1458 Logger logger = cx.findLogger(name);
1459 if (logger != null) {
1460 resetLogger(logger);
1461 }
1462 }
1463 }
1464
1465 private void closeHandlers(Logger logger) {
1466 Handler[] targets = logger.getHandlers();
1467 for (Handler h : targets) {
1468 logger.removeHandler(h);
1469 try {
1470 h.close();
1471 } catch (Exception ex) {
1472 // Problems closing a handler? Keep going...
1473 } catch (Error e) {
1474 // ignore Errors while shutting down
1475 if (globalHandlersState != STATE_SHUTDOWN) {
1476 throw e;
1477 }
1478 }
1479 }
1480 }
1481
1482 // Private method to reset an individual target logger.
1483 private void resetLogger(Logger logger) {
1484 // Close all the Logger handlers.
1485 closeHandlers(logger);
1486
1487 // Reset Logger level
1488 String name = logger.getName();
1489 if (name != null && name.equals("")) {
1490 // This is the root logger.
1491 logger.setLevel(defaultLevel);
1492 } else {
1493 logger.setLevel(null);
1494 }
1495 }
1496
1497 // get a list of whitespace separated classnames from a property.
1498 private String[] parseClassNames(String propertyName) {
1499 String hands = getProperty(propertyName);
1500 if (hands == null) {
1501 return new String[0];
1502 }
1503 hands = hands.trim();
1504 int ix = 0;
1505 final List<String> result = new ArrayList<>();
1506 while (ix < hands.length()) {
1507 int end = ix;
1508 while (end < hands.length()) {
1509 if (Character.isWhitespace(hands.charAt(end))) {
1510 break;
1511 }
1512 if (hands.charAt(end) == ',') {
1513 break;
1514 }
1515 end++;
1516 }
1517 String word = hands.substring(ix, end);
1518 ix = end+1;
1519 word = word.trim();
1520 if (word.length() == 0) {
1521 continue;
1522 }
1523 result.add(word);
1524 }
1525 return result.toArray(new String[result.size()]);
1526 }
1527
1528 /**
1529 * Reads and initializes the logging configuration from the given input stream.
1530 *
1531 * <p>
1532 * Any {@linkplain #addConfigurationListener registered configuration
1533 * listener} will be invoked after the properties are read.
1534 *
1535 * @apiNote This {@code readConfiguration} method should only be used for
1536 * initializing the configuration during LogManager initialization or
1537 * used with the "java.util.logging.config.class" property.
1538 * When this method is called after loggers have been created, all
1539 * existing loggers will be {@linkplain #reset() reset}. Then any
1540 * existing loggers that have a level property specified in the
1541 * given input stream will be {@linkplain
1542 * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1543 * <p>
1544 * To properly update the logging configuration, use the
1545 * {@link #updateConfiguration(java.util.function.Function)} or
1546 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1547 * method instead.
1548 *
1549 * @param ins stream to read properties from
1550 * @throws SecurityException if a security manager exists and if
1551 * the caller does not have LoggingPermission("control").
1552 * @throws IOException if there are problems reading from the stream,
1553 * or the given stream is not in the
1554 * {@linkplain java.util.Properties properties file} format.
1555 */
1556 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1557 checkPermission();
1558
1559 // We don't want reset() and readConfiguration() to run
1560 // in parallel.
1561 configurationLock.lock();
1562 try {
1563 if (globalHandlersState == STATE_SHUTDOWN) {
1564 // already in terminal state: don't even bother
1565 // to read the configuration
1566 return;
1567 }
1568
1569 // change state to STATE_READING_CONFIG to signal reset() to not change it
1570 globalHandlersState = STATE_READING_CONFIG;
1571 try {
1572 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG
1573 // so that while reading configuration, any ongoing logging requests block and
1574 // wait for the outcome (see the end of this try statement)
1575 reset();
1576
1577 try {
1578 // Load the properties
1579 props.load(ins);
1580 } catch (IllegalArgumentException x) {
1581 // props.load may throw an IllegalArgumentException if the stream
1582 // contains malformed Unicode escape sequences.
1583 // We wrap that in an IOException as readConfiguration is
1584 // specified to throw IOException if there are problems reading
1585 // from the stream.
1586 // Note: new IOException(x.getMessage(), x) allow us to get a more
1587 // concise error message than new IOException(x);
1588 throw new IOException(x.getMessage(), x);
1589 }
1590
1591 // Instantiate new configuration objects.
1592 String names[] = parseClassNames("config");
1593
1594 for (String word : names) {
1595 try {
1596 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
1597 @SuppressWarnings("deprecation")
1598 Object witness = clz.newInstance();
1599 } catch (Exception ex) {
1600 System.err.println("Can't load config class \"" + word + "\"");
1601 System.err.println("" + ex);
1602 // ex.printStackTrace();
1603 }
1604 }
1605
1606 // Set levels on any pre-existing loggers, based on the new properties.
1607 setLevelsOnExistingLoggers();
1608
1609 // Note that we need to reinitialize global handles when
1610 // they are first referenced.
1611 globalHandlersState = STATE_UNINITIALIZED;
1612 } catch (Throwable t) {
1613 // If there were any trouble, then set state to STATE_INITIALIZED
1614 // so that no global handlers reinitialization is performed on not fully
1615 // initialized configuration.
1616 globalHandlersState = STATE_INITIALIZED;
1617 // re-throw
1618 throw t;
1619 }
1620 } finally {
1621 configurationLock.unlock();
1622 }
1623
1624 // should be called out of lock to avoid dead-lock situations
1625 // when user code is involved
1626 invokeConfigurationListeners();
1627 }
1628
1629 // This enum enumerate the configuration properties that will be
1630 // updated on existing loggers when the configuration is updated
1631 // with LogManager.updateConfiguration().
1632 //
1633 // Note that this works properly only for the global LogManager - as
1634 // Handler and its subclasses get their configuration from
1635 // LogManager.getLogManager().
1636 //
1637 static enum ConfigProperty {
1638 LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1639 final String suffix;
1640 final int length;
1641 private ConfigProperty(String suffix) {
1642 this.suffix = Objects.requireNonNull(suffix);
1643 length = suffix.length();
1644 }
1645
1646 public boolean handleKey(String key) {
1647 if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1648 if (this == HANDLERS && suffix.equals(key)) return false;
1649 return key.endsWith(suffix);
1650 }
1651 String key(String loggerName) {
1652 if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1653 return suffix.substring(1);
1654 }
1655 return loggerName + suffix;
1656 }
1657 String loggerName(String key) {
1658 assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1659 if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1660 return key.substring(0, key.length() - length);
1661 }
1662
1663 /**
1664 * If the property is one that should be updated on existing loggers by
1665 * updateConfiguration, returns the name of the logger for which the
1666 * property is configured. Otherwise, returns null.
1667 * @param property a property key in 'props'
1668 * @return the name of the logger on which the property is to be set,
1669 * if the property is one that should be updated on existing
1670 * loggers, {@code null} otherwise.
1671 */
1672 static String getLoggerName(String property) {
1673 for (ConfigProperty p : ConfigProperty.ALL) {
1674 if (p.handleKey(property)) {
1675 return p.loggerName(property);
1676 }
1677 }
1678 return null; // Not a property that should be updated.
1679 }
1680
1681 /**
1682 * Find the ConfigProperty corresponding to the given
1683 * property key (may find none).
1684 * @param property a property key in 'props'
1685 * @return An optional containing a ConfigProperty object,
1686 * if the property is one that should be updated on existing
1687 * loggers, empty otherwise.
1688 */
1689 static Optional<ConfigProperty> find(String property) {
1690 return ConfigProperty.ALL.stream()
1691 .filter(p -> p.handleKey(property))
1692 .findFirst();
1693 }
1694
1695 /**
1696 * Returns true if the given property is one that should be updated
1697 * on existing loggers.
1698 * Used to filter property name streams.
1699 * @param property a property key from the configuration.
1700 * @return true if this property is of interest for updateConfiguration.
1701 */
1702 static boolean matches(String property) {
1703 return find(property).isPresent();
1704 }
1705
1706 /**
1707 * Returns true if the new property value is different from the old,
1708 * and therefore needs to be updated on existing loggers.
1709 * @param k a property key in the configuration
1710 * @param previous the old configuration
1711 * @param next the new configuration
1712 * @return true if the property is changing value between the two
1713 * configurations.
1714 */
1715 static boolean needsUpdating(String k, Properties previous, Properties next) {
1716 final String p = trim(previous.getProperty(k, null));
1717 final String n = trim(next.getProperty(k, null));
1718 return ! Objects.equals(p,n);
1719 }
1720
1721 /**
1722 * Applies the mapping function for the given key to the next
1723 * configuration.
1724 * If the mapping function is null then this method does nothing.
1725 * Otherwise, it calls the mapping function to compute the value
1726 * that should be associated with {@code key} in the resulting
1727 * configuration, and applies it to {@code next}.
1728 * If the mapping function returns {@code null} the key is removed
1729 * from {@code next}.
1730 *
1731 * @param k a property key in the configuration
1732 * @param previous the old configuration
1733 * @param next the new configuration (modified by this function)
1734 * @param mappingFunction the mapping function.
1735 */
1736 static void merge(String k, Properties previous, Properties next,
1737 BiFunction<String, String, String> mappingFunction) {
1738 String p = trim(previous.getProperty(k, null));
1739 String n = trim(next.getProperty(k, null));
1740 String mapped = trim(mappingFunction.apply(p,n));
1741 if (!Objects.equals(n, mapped)) {
1742 if (mapped == null) {
1743 next.remove(k);
1744 } else {
1745 next.setProperty(k, mapped);
1746 }
1747 }
1748 }
1749
1750 private static final EnumSet<ConfigProperty> ALL =
1751 EnumSet.allOf(ConfigProperty.class);
1752 }
1753
1754 // trim the value if not null.
1755 private static String trim(String value) {
1756 return value == null ? null : value.trim();
1757 }
1758
1759 /**
1760 * An object that keep track of loggers we have already visited.
1761 * Used when updating configuration, to avoid processing the same logger
1762 * twice.
1763 */
1764 static final class VisitedLoggers implements Predicate<Logger> {
1765 final IdentityHashMap<Logger,Boolean> visited;
1766 private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
1767 this.visited = visited;
1768 }
1769 VisitedLoggers() {
1770 this(new IdentityHashMap<>());
1771 }
1772 @Override
1773 public boolean test(Logger logger) {
1774 return visited != null && visited.put(logger, Boolean.TRUE) != null;
1775 }
1776 public void clear() {
1777 if (visited != null) visited.clear();
1778 }
1779
1780 // An object that considers that no logger has ever been visited.
1781 // This is used when processParentHandlers is called from
1782 // LoggerContext.addLocalLogger
1783 static final VisitedLoggers NEVER = new VisitedLoggers(null);
1784 }
1785
1786
1787 /**
1788 * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
1789 * or REMOVED.
1790 */
1791 static enum ModType {
1792 SAME, // property had no value in the old and new conf, or had the
1793 // same value in both.
1794 ADDED, // property had no value in the old conf, but has one in the new.
1795 CHANGED, // property has a different value in the old conf and the new conf.
1796 REMOVED; // property has no value in the new conf, but had one in the old.
1797 static ModType of(String previous, String next) {
1798 if (previous == null && next != null) {
1799 return ADDED;
1800 }
1801 if (next == null && previous != null) {
1802 return REMOVED;
1803 }
1804 if (!Objects.equals(trim(previous), trim(next))) {
1805 return CHANGED;
1806 }
1807 return SAME;
1808 }
1809 }
1810
1811 /**
1812 * Updates the logging configuration.
1813 * <p>
1814 * If the "java.util.logging.config.file" system property is set,
1815 * then the property value specifies the properties file to be read
1816 * as the new configuration. Otherwise, the LogManager default
1817 * configuration is used.
1818 * <br>The default configuration is typically loaded from the
1819 * properties file "{@code conf/logging.properties}" in the
1820 * Java installation directory.
1821 * <p>
1822 * This method reads the new configuration and calls the {@link
1823 * #updateConfiguration(java.io.InputStream, java.util.function.Function)
1824 * updateConfiguration(ins, mapper)} method to
1825 * update the configuration.
1826 *
1827 * @apiNote
1828 * This method updates the logging configuration from reading
1829 * a properties file and ignores the "java.util.logging.config.class"
1830 * system property. The "java.util.logging.config.class" property is
1831 * only used by the {@link #readConfiguration()} method to load a custom
1832 * configuration class as an initial configuration.
1833 *
1834 * @param mapper a functional interface that takes a configuration
1835 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1836 * value will be applied to the resulting configuration. The
1837 * function <i>f</i> may return {@code null} to indicate that the property
1838 * <i>k</i> will not be added to the resulting configuration.
1839 * <br>
1840 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
1841 * assumed.
1842 * <br>
1843 * For each <i>k</i>, the mapped function <i>f</i> will
1844 * be invoked with the value associated with <i>k</i> in the old
1845 * configuration (i.e <i>o</i>) and the value associated with
1846 * <i>k</i> in the new configuration (i.e. <i>n</i>).
1847 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1848 * value was present for <i>k</i> in the corresponding configuration.
1849 *
1850 * @throws SecurityException if a security manager exists and if
1851 * the caller does not have LoggingPermission("control"), or
1852 * does not have the permissions required to set up the
1853 * configuration (e.g. open file specified for FileHandlers
1854 * etc...)
1855 *
1856 * @throws NullPointerException if {@code mapper} returns a {@code null}
1857 * function when invoked.
1858 *
1859 * @throws IOException if there are problems reading from the
1860 * logging configuration file.
1861 *
1862 * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)
1863 * @since 9
1864 */
1865 public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
1866 throws IOException {
1867 checkPermission();
1868 ensureLogManagerInitialized();
1869 drainLoggerRefQueueBounded();
1870
1871 String fname = getConfigurationFileName();
1872 try (final InputStream in = new FileInputStream(fname)) {
1873 final BufferedInputStream bin = new BufferedInputStream(in);
1874 updateConfiguration(bin, mapper);
1875 }
1876 }
1877
1878 /**
1879 * Updates the logging configuration.
1880 * <p>
1881 * For each configuration key in the {@linkplain
1882 * #getProperty(java.lang.String) existing configuration} and
1883 * the given input stream configuration, the given {@code mapper} function
1884 * is invoked to map from the configuration key to a function,
1885 * <i>f(o,n)</i>, that takes the old value and new value and returns
1886 * the resulting value to be applied in the resulting configuration,
1887 * as specified in the table below.
1888 * <p>Let <i>k</i> be a configuration key in the old or new configuration,
1889 * <i>o</i> be the old value (i.e. the value associated
1890 * with <i>k</i> in the old configuration), <i>n</i> be the
1891 * new value (i.e. the value associated with <i>k</i> in the new
1892 * configuration), and <i>f</i> be the function returned
1893 * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
1894 * resulting value. If <i>v</i> is not {@code null}, then a property
1895 * <i>k</i> with value <i>v</i> will be added to the resulting configuration.
1896 * Otherwise, it will be omitted.
1897 * <br>A {@code null} value may be passed to function
1898 * <i>f</i> to indicate that the corresponding configuration has no
1899 * configuration key <i>k</i>.
1900 * The function <i>f</i> may return {@code null} to indicate that
1901 * there will be no value associated with <i>k</i> in the resulting
1902 * configuration.
1903 * <p>
1904 * If {@code mapper} is {@code null}, then <i>v</i> will be set to
1905 * <i>n</i>.
1906 * <p>
1907 * LogManager {@linkplain #getProperty(java.lang.String) properties} are
1908 * updated with the resulting value in the resulting configuration.
1909 * <p>
1910 * The registered {@linkplain #addConfigurationListener configuration
1911 * listeners} will be invoked after the configuration is successfully updated.
1912 * <br><br>
1913 * <table class="striped">
1914 * <caption style="display:none">Updating configuration properties</caption>
1915 * <thead>
1916 * <tr>
1917 * <th scope="col">Property</th>
1918 * <th scope="col">Resulting Behavior</th>
1919 * </tr>
1920 * </thead>
1921 * <tbody>
1922 * <tr>
1923 * <th scope="row" valign="top">{@code <logger>.level}</th>
1924 * <td>
1925 * <ul>
1926 * <li>If the resulting configuration defines a level for a logger and
1927 * if the resulting level is different than the level specified in the
1928 * the old configuration, or not specified in
1929 * the old configuration, then if the logger exists or if children for
1930 * that logger exist, the level for that logger will be updated,
1931 * and the change propagated to any existing logger children.
1932 * This may cause the logger to be created, if necessary.
1933 * </li>
1934 * <li>If the old configuration defined a level for a logger, and the
1935 * resulting configuration doesn't, then this change will not be
1936 * propagated to existing loggers, if any.
1937 * To completely replace a configuration - the caller should therefore
1938 * call {@link #reset() reset} to empty the current configuration,
1939 * before calling {@code updateConfiguration}.
1940 * </li>
1941 * </ul>
1942 * </td>
1943 * <tr>
1944 * <th scope="row" valign="top">{@code <logger>.useParentHandlers}</th>
1945 * <td>
1946 * <ul>
1947 * <li>If either the resulting or the old value for the useParentHandlers
1948 * property is not null, then if the logger exists or if children for
1949 * that logger exist, that logger will be updated to the resulting
1950 * value.
1951 * The value of the useParentHandlers property is the value specified
1952 * in the configuration; if not specified, the default is true.
1953 * </li>
1954 * </ul>
1955 * </td>
1956 * </tr>
1957 * <tr>
1958 * <th scope="row" valign="top">{@code <logger>.handlers}</th>
1959 * <td>
1960 * <ul>
1961 * <li>If the resulting configuration defines a list of handlers for a
1962 * logger, and if the resulting list is different than the list
1963 * specified in the old configuration for that logger (that could be
1964 * empty), then if the logger exists or its children exist, the
1965 * handlers associated with that logger are closed and removed and
1966 * the new handlers will be created per the resulting configuration
1967 * and added to that logger, creating that logger if necessary.
1968 * </li>
1969 * <li>If the old configuration defined some handlers for a logger, and
1970 * the resulting configuration doesn't, if that logger exists,
1971 * its handlers will be removed and closed.
1972 * </li>
1973 * <li>Changing the list of handlers on an existing logger will cause all
1974 * its previous handlers to be removed and closed, regardless of whether
1975 * they had been created from the configuration or programmatically.
1976 * The old handlers will be replaced by new handlers, if any.
1977 * </li>
1978 * </ul>
1979 * </td>
1980 * </tr>
1981 * <tr>
1982 * <th scope="row" valign="top">{@code <handler-name>.*}</th>
1983 * <td>
1984 * <ul>
1985 * <li>Properties configured/changed on handler classes will only affect
1986 * newly created handlers. If a node is configured with the same list
1987 * of handlers in the old and the resulting configuration, then these
1988 * handlers will remain unchanged.
1989 * </li>
1990 * </ul>
1991 * </td>
1992 * </tr>
1993 * <tr>
1994 * <th scope="row" valign="top">{@code config} and any other property</th>
1995 * <td>
1996 * <ul>
1997 * <li>The resulting value for these property will be stored in the
1998 * LogManager properties, but {@code updateConfiguration} will not parse
1999 * or process their values.
2000 * </li>
2001 * </ul>
2002 * </td>
2003 * </tr>
2004 * </tbody>
2005 * </table>
2006 * <p>
2007 * <em>Example mapper functions:</em>
2008 * <br><br>
2009 * <ul>
2010 * <li>Replace all logging properties with the new configuration:
2011 * <br><br>{@code (k) -> ((o, n) -> n)}:
2012 * <br><br>this is equivalent to passing a null {@code mapper} parameter.
2013 * </li>
2014 * <li>Merge the new configuration and old configuration and use the
2015 * new value if <i>k</i> exists in the new configuration:
2016 * <br><br>{@code (k) -> ((o, n) -> n == null ? o : n)}:
2017 * <br><br>as if merging two collections as follows:
2018 * {@code result.putAll(oldc); result.putAll(newc)}.<br></li>
2019 * <li>Merge the new configuration and old configuration and use the old
2020 * value if <i>k</i> exists in the old configuration:
2021 * <br><br>{@code (k) -> ((o, n) -> o == null ? n : o)}:
2022 * <br><br>as if merging two collections as follows:
2023 * {@code result.putAll(newc); result.putAll(oldc)}.<br></li>
2024 * <li>Replace all properties with the new configuration except the handler
2025 * property to configure Logger's handler that is not root logger:
2026 * <br>
2027 * <pre>{@code (k) -> k.endsWith(".handlers")}
2028 * {@code ? ((o, n) -> (o == null ? n : o))}
2029 * {@code : ((o, n) -> n)}</pre>
2030 * </li>
2031 * </ul>
2032 * <p>
2033 * To completely reinitialize a configuration, an application can first call
2034 * {@link #reset() reset} to fully remove the old configuration, followed by
2035 * {@code updateConfiguration} to initialize the new configuration.
2036 *
2037 * @param ins a stream to read properties from
2038 * @param mapper a functional interface that takes a configuration
2039 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
2040 * value will be applied to the resulting configuration. The
2041 * function <i>f</i> may return {@code null} to indicate that the property
2042 * <i>k</i> will not be added to the resulting configuration.
2043 * <br>
2044 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
2045 * assumed.
2046 * <br>
2047 * For each <i>k</i>, the mapped function <i>f</i> will
2048 * be invoked with the value associated with <i>k</i> in the old
2049 * configuration (i.e <i>o</i>) and the value associated with
2050 * <i>k</i> in the new configuration (i.e. <i>n</i>).
2051 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
2052 * value was present for <i>k</i> in the corresponding configuration.
2053 *
2054 * @throws SecurityException if a security manager exists and if
2055 * the caller does not have LoggingPermission("control"), or
2056 * does not have the permissions required to set up the
2057 * configuration (e.g. open files specified for FileHandlers)
2058 *
2059 * @throws NullPointerException if {@code ins} is null or if
2060 * {@code mapper} returns a null function when invoked.
2061 *
2062 * @throws IOException if there are problems reading from the stream,
2063 * or the given stream is not in the
2064 * {@linkplain java.util.Properties properties file} format.
2065 * @since 9
2066 */
2067 public void updateConfiguration(InputStream ins,
2068 Function<String, BiFunction<String,String,String>> mapper)
2069 throws IOException {
2070 checkPermission();
2071 ensureLogManagerInitialized();
2072 drainLoggerRefQueueBounded();
2073
2074 final Properties previous;
2075 final Set<String> updatePropertyNames;
2076 List<LoggerContext> cxs = Collections.emptyList();
2077 final VisitedLoggers visited = new VisitedLoggers();
2078 final Properties next = new Properties();
2079
2080 try {
2081 // Load the properties
2082 next.load(ins);
2083 } catch (IllegalArgumentException x) {
2084 // props.load may throw an IllegalArgumentException if the stream
2085 // contains malformed Unicode escape sequences.
2086 // We wrap that in an IOException as updateConfiguration is
2087 // specified to throw IOException if there are problems reading
2088 // from the stream.
2089 // Note: new IOException(x.getMessage(), x) allow us to get a more
2090 // concise error message than new IOException(x);
2091 throw new IOException(x.getMessage(), x);
2092 }
2093
2094 if (globalHandlersState == STATE_SHUTDOWN) return;
2095
2096 // exclusive lock: readConfiguration/reset/updateConfiguration can't
2097 // run concurrently.
2098 // configurationLock.writeLock().lock();
2099 configurationLock.lock();
2100 try {
2101 if (globalHandlersState == STATE_SHUTDOWN) return;
2102 previous = props;
2103
2104 // Builds a TreeSet of all (old and new) property names.
2105 updatePropertyNames =
2106 Stream.concat(previous.stringPropertyNames().stream(),
2107 next.stringPropertyNames().stream())
2108 .collect(Collectors.toCollection(TreeSet::new));
2109
2110 if (mapper != null) {
2111 // mapper will potentially modify the content of
2112 // 'next', so we need to call it before affecting props=next.
2113 // give a chance to the mapper to control all
2114 // properties - not just those we will reset.
2115 updatePropertyNames.stream()
2116 .forEachOrdered(k -> ConfigProperty
2117 .merge(k, previous, next,
2118 Objects.requireNonNull(mapper.apply(k))));
2119 }
2120
2121 props = next;
2122
2123 // allKeys will contain all keys:
2124 // - which correspond to a configuration property we are interested in
2125 // (first filter)
2126 // - whose value needs to be updated (because it's new, removed, or
2127 // different) in the resulting configuration (second filter)
2128 final Stream<String> allKeys = updatePropertyNames.stream()
2129 .filter(ConfigProperty::matches)
2130 .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
2131
2132 // Group configuration properties by logger name
2133 // We use a TreeMap so that parent loggers will be visited before
2134 // child loggers.
2135 final Map<String, TreeSet<String>> loggerConfigs =
2136 allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
2137 TreeMap::new,
2138 Collectors.toCollection(TreeSet::new)));
2139
2140 if (!loggerConfigs.isEmpty()) {
2141 cxs = contexts();
2142 }
2143 final List<Logger> loggers = cxs.isEmpty()
2144 ? Collections.emptyList() : new ArrayList<>(cxs.size());
2145 for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
2146 // This can be a logger name, or something else...
2147 // The only thing we know is that we found a property
2148 // we are interested in.
2149 // For instance, if we found x.y.z.level, then x.y.z could be
2150 // a logger, but it could also be a handler class...
2151 // Anyway...
2152 final String name = e.getKey();
2153 final Set<String> properties = e.getValue();
2154 loggers.clear();
2155 for (LoggerContext cx : cxs) {
2156 Logger l = cx.findLogger(name);
2157 if (l != null && !visited.test(l)) {
2158 loggers.add(l);
2159 }
2160 }
2161 if (loggers.isEmpty()) continue;
2162 for (String pk : properties) {
2163 ConfigProperty cp = ConfigProperty.find(pk).get();
2164 String p = previous.getProperty(pk, null);
2165 String n = next.getProperty(pk, null);
2166
2167 // Determines the type of modification.
2168 ModType mod = ModType.of(p, n);
2169
2170 // mod == SAME means that the two values are equals, there
2171 // is nothing to do. Usually, this should not happen as such
2172 // properties should have been filtered above.
2173 // It could happen however if the properties had
2174 // trailing/leading whitespaces.
2175 if (mod == ModType.SAME) continue;
2176
2177 switch (cp) {
2178 case LEVEL:
2179 if (mod == ModType.REMOVED) continue;
2180 Level level = Level.findLevel(trim(n));
2181 if (level != null) {
2182 if (name.isEmpty()) {
2183 rootLogger.setLevel(level);
2184 }
2185 for (Logger l : loggers) {
2186 if (!name.isEmpty() || l != rootLogger) {
2187 l.setLevel(level);
2188 }
2189 }
2190 }
2191 break;
2192 case USEPARENT:
2193 if (!name.isEmpty()) {
2194 boolean useParent = getBooleanProperty(pk, true);
2195 if (n != null || p != null) {
2196 // reset the flag only if the previous value
2197 // or the new value are not null.
2198 for (Logger l : loggers) {
2199 l.setUseParentHandlers(useParent);
2200 }
2201 }
2202 }
2203 break;
2204 case HANDLERS:
2205 List<Handler> hdls = null;
2206 if (name.isEmpty()) {
2207 // special handling for the root logger.
2208 globalHandlersState = STATE_READING_CONFIG;
2209 try {
2210 closeHandlers(rootLogger);
2211 globalHandlersState = STATE_UNINITIALIZED;
2212 } catch (Throwable t) {
2213 globalHandlersState = STATE_INITIALIZED;
2214 throw t;
2215 }
2216 }
2217 for (Logger l : loggers) {
2218 if (l == rootLogger) continue;
2219 closeHandlers(l);
2220 if (mod == ModType.REMOVED) {
2221 closeOnResetLoggers.removeIf(c -> c.logger == l);
2222 continue;
2223 }
2224 if (hdls == null) {
2225 hdls = name.isEmpty()
2226 ? Arrays.asList(rootLogger.getHandlers())
2227 : createLoggerHandlers(name, pk);
2228 }
2229 setLoggerHandlers(l, name, pk, hdls);
2230 }
2231 break;
2232 default: break;
2233 }
2234 }
2235 }
2236 } finally {
2237 configurationLock.unlock();
2238 visited.clear();
2239 }
2240
2241 // Now ensure that if an existing logger has acquired a new parent
2242 // in the configuration, this new parent will be created - if needed,
2243 // and added to the context of the existing child.
2244 //
2245 drainLoggerRefQueueBounded();
2246 for (LoggerContext cx : cxs) {
2247 for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2248 String name = names.nextElement();
2249 if (name.isEmpty()) continue; // don't need to process parents on root.
2250 Logger l = cx.findLogger(name);
2251 if (l != null && !visited.test(l)) {
2252 // should pass visited here to cut the processing when
2253 // reaching a logger already visited.
2254 cx.processParentHandlers(l, name, visited);
2255 }
2256 }
2257 }
2258
2259 // We changed the configuration: invoke configuration listeners
2260 invokeConfigurationListeners();
2261 }
2262
2263 /**
2264 * Get the value of a logging property.
2265 * The method returns null if the property is not found.
2266 * @param name property name
2267 * @return property value
2268 */
2269 public String getProperty(String name) {
2270 return props.getProperty(name);
2271 }
2272
2273 // Package private method to get a String property.
2274 // If the property is not defined we return the given
2275 // default value.
2276 String getStringProperty(String name, String defaultValue) {
2277 String val = getProperty(name);
2278 if (val == null) {
2279 return defaultValue;
2280 }
2281 return val.trim();
2282 }
2283
2284 // Package private method to get an integer property.
2285 // If the property is not defined or cannot be parsed
2286 // we return the given default value.
2287 int getIntProperty(String name, int defaultValue) {
2288 String val = getProperty(name);
2289 if (val == null) {
2290 return defaultValue;
2291 }
2292 try {
2293 return Integer.parseInt(val.trim());
2294 } catch (Exception ex) {
2295 return defaultValue;
2296 }
2297 }
2298
2299 // Package private method to get a long property.
2300 // If the property is not defined or cannot be parsed
2301 // we return the given default value.
2302 long getLongProperty(String name, long defaultValue) {
2303 String val = getProperty(name);
2304 if (val == null) {
2305 return defaultValue;
2306 }
2307 try {
2308 return Long.parseLong(val.trim());
2309 } catch (Exception ex) {
2310 return defaultValue;
2311 }
2312 }
2313
2314 // Package private method to get a boolean property.
2315 // If the property is not defined or cannot be parsed
2316 // we return the given default value.
2317 boolean getBooleanProperty(String name, boolean defaultValue) {
2318 String val = getProperty(name);
2319 if (val == null) {
2320 return defaultValue;
2321 }
2322 val = val.toLowerCase();
2323 if (val.equals("true") || val.equals("1")) {
2324 return true;
2325 } else if (val.equals("false") || val.equals("0")) {
2326 return false;
2327 }
2328 return defaultValue;
2329 }
2330
2331 // Package private method to get a Level property.
2332 // If the property is not defined or cannot be parsed
2333 // we return the given default value.
2334 Level getLevelProperty(String name, Level defaultValue) {
2335 String val = getProperty(name);
2336 if (val == null) {
2337 return defaultValue;
2338 }
2339 Level l = Level.findLevel(val.trim());
2340 return l != null ? l : defaultValue;
2341 }
2342
2343 // Package private method to get a filter property.
2344 // We return an instance of the class named by the "name"
2345 // property. If the property is not defined or has problems
2346 // we return the defaultValue.
2347 Filter getFilterProperty(String name, Filter defaultValue) {
2348 String val = getProperty(name);
2349 try {
2350 if (val != null) {
2351 @SuppressWarnings("deprecation")
2352 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2353 return (Filter) o;
2354 }
2355 } catch (Exception ex) {
2356 // We got one of a variety of exceptions in creating the
2357 // class or creating an instance.
2358 // Drop through.
2359 }
2360 // We got an exception. Return the defaultValue.
2361 return defaultValue;
2362 }
2363
2364
2365 // Package private method to get a formatter property.
2366 // We return an instance of the class named by the "name"
2367 // property. If the property is not defined or has problems
2368 // we return the defaultValue.
2369 Formatter getFormatterProperty(String name, Formatter defaultValue) {
2370 String val = getProperty(name);
2371 try {
2372 if (val != null) {
2373 @SuppressWarnings("deprecation")
2374 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2375 return (Formatter) o;
2376 }
2377 } catch (Exception ex) {
2378 // We got one of a variety of exceptions in creating the
2379 // class or creating an instance.
2380 // Drop through.
2381 }
2382 // We got an exception. Return the defaultValue.
2383 return defaultValue;
2384 }
2385
2386 // Private method to load the global handlers.
2387 // We do the real work lazily, when the global handlers
2388 // are first used.
2389 private void initializeGlobalHandlers() {
2390 int state = globalHandlersState;
2391 if (state == STATE_INITIALIZED ||
2392 state == STATE_SHUTDOWN) {
2393 // Nothing to do: return.
2394 return;
2395 }
2396
2397 // If we have not initialized global handlers yet (or need to
2398 // reinitialize them), lets do it now (this case is indicated by
2399 // globalHandlersState == STATE_UNINITIALIZED).
2400 // If we are in the process of initializing global handlers we
2401 // also need to lock & wait (this case is indicated by
2402 // globalHandlersState == STATE_INITIALIZING).
2403 // If we are in the process of reading configuration we also need to
2404 // wait to see what the outcome will be (this case
2405 // is indicated by globalHandlersState == STATE_READING_CONFIG)
2406 // So in either case we need to wait for the lock.
2407 configurationLock.lock();
2408 try {
2409 if (globalHandlersState != STATE_UNINITIALIZED) {
2410 return; // recursive call or nothing to do
2411 }
2412 // set globalHandlersState to STATE_INITIALIZING first to avoid
2413 // getting an infinite recursion when loadLoggerHandlers(...)
2414 // is going to call addHandler(...)
2415 globalHandlersState = STATE_INITIALIZING;
2416 try {
2417 loadLoggerHandlers(rootLogger, null, "handlers");
2418 } finally {
2419 globalHandlersState = STATE_INITIALIZED;
2420 }
2421 } finally {
2422 configurationLock.unlock();
2423 }
2424 }
2425
2426 static final Permission controlPermission =
2427 new LoggingPermission("control", null);
2428
2429 void checkPermission() {
2430 SecurityManager sm = System.getSecurityManager();
2431 if (sm != null)
2432 sm.checkPermission(controlPermission);
2433 }
2434
2435 /**
2436 * Check that the current context is trusted to modify the logging
2437 * configuration. This requires LoggingPermission("control").
2438 * <p>
2439 * If the check fails we throw a SecurityException, otherwise
2440 * we return normally.
2441 *
2442 * @exception SecurityException if a security manager exists and if
2443 * the caller does not have LoggingPermission("control").
2444 */
2445 public void checkAccess() throws SecurityException {
2446 checkPermission();
2447 }
2448
2449 // Nested class to represent a node in our tree of named loggers.
2450 private static class LogNode {
2451 HashMap<String,LogNode> children;
2452 LoggerWeakRef loggerRef;
2453 LogNode parent;
2454 final LoggerContext context;
2455
2456 LogNode(LogNode parent, LoggerContext context) {
2457 this.parent = parent;
2458 this.context = context;
2459 }
2460
2461 // Recursive method to walk the tree below a node and set
2462 // a new parent logger.
2463 void walkAndSetParent(Logger parent) {
2464 if (children == null) {
2465 return;
2466 }
2467 for (LogNode node : children.values()) {
2468 LoggerWeakRef ref = node.loggerRef;
2469 Logger logger = (ref == null) ? null : ref.get();
2470 if (logger == null) {
2471 node.walkAndSetParent(parent);
2472 } else {
2473 doSetParent(logger, parent);
2474 }
2475 }
2476 }
2477 }
2478
2479 // We use a subclass of Logger for the root logger, so
2480 // that we only instantiate the global handlers when they
2481 // are first needed.
2482 private final class RootLogger extends Logger {
2483 private RootLogger() {
2484 // We do not call the protected Logger two args constructor here,
2485 // to avoid calling LogManager.getLogManager() from within the
2486 // RootLogger constructor.
2487 super("", null, null, LogManager.this, true);
2488 }
2489
2490 @Override
2491 public void log(LogRecord record) {
2492 // Make sure that the global handlers have been instantiated.
2493 initializeGlobalHandlers();
2494 super.log(record);
2495 }
2496
2497 @Override
2498 public void addHandler(Handler h) {
2499 initializeGlobalHandlers();
2500 super.addHandler(h);
2501 }
2502
2503 @Override
2504 public void removeHandler(Handler h) {
2505 initializeGlobalHandlers();
2506 super.removeHandler(h);
2507 }
2508
2509 @Override
2510 Handler[] accessCheckedHandlers() {
2511 initializeGlobalHandlers();
2512 return super.accessCheckedHandlers();
2513 }
2514 }
2515
2516
2517 // Private method to be called when the configuration has
2518 // changed to apply any level settings to any pre-existing loggers.
2519 private void setLevelsOnExistingLoggers() {
2520 Enumeration<?> enum_ = props.propertyNames();
2521 while (enum_.hasMoreElements()) {
2522 String key = (String)enum_.nextElement();
2523 if (!key.endsWith(".level")) {
2524 // Not a level definition.
2525 continue;
2526 }
2527 int ix = key.length() - 6;
2528 String name = key.substring(0, ix);
2529 Level level = getLevelProperty(key, null);
2530 if (level == null) {
2531 System.err.println("Bad level value for property: " + key);
2532 continue;
2533 }
2534 for (LoggerContext cx : contexts()) {
2535 Logger l = cx.findLogger(name);
2536 if (l == null) {
2537 continue;
2538 }
2539 l.setLevel(level);
2540 }
2541 }
2542 }
2543
2544 /**
2545 * String representation of the
2546 * {@link javax.management.ObjectName} for the management interface
2547 * for the logging facility.
2548 *
2549 * @see java.lang.management.PlatformLoggingMXBean
2550 *
2551 * @since 1.5
2552 */
2553 public final static String LOGGING_MXBEAN_NAME
2554 = "java.util.logging:type=Logging";
2555
2556 /**
2557 * Returns {@code LoggingMXBean} for managing loggers.
2558 *
2559 * @return a {@link LoggingMXBean} object.
2560 *
2561 * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and
2562 * replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use
2563 * {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
2564 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)
2565 * instead.
2566 *
2567 * @see java.lang.management.PlatformLoggingMXBean
2568 * @since 1.5
2569 */
2570 @Deprecated(since="9")
2571 public static synchronized LoggingMXBean getLoggingMXBean() {
2572 return Logging.getInstance();
2573 }
2574
2575 /**
2576 * Adds a configuration listener to be invoked each time the logging
2577 * configuration is read.
2578 * If the listener is already registered the method does nothing.
2579 * <p>
2580 * The listener is invoked with privileges that are restricted by the
2581 * calling context of this method.
2582 * The order in which the listeners are invoked is unspecified.
2583 * <p>
2584 * It is recommended that listeners do not throw errors or exceptions.
2585 *
2586 * If a listener terminates with an uncaught error or exception then
2587 * the first exception will be propagated to the caller of
2588 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
2589 * after all listeners have been invoked.
2590 *
2591 * @implNote If more than one listener terminates with an uncaught error or
2592 * exception, an implementation may record the additional errors or
2593 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
2594 * suppressed exceptions}.
2595 *
2596 * @param listener A configuration listener that will be invoked after the
2597 * configuration changed.
2598 * @return This LogManager.
2599 * @throws SecurityException if a security manager exists and if the
2600 * caller does not have LoggingPermission("control").
2601 * @throws NullPointerException if the listener is null.
2602 *
2603 * @since 9
2604 */
2605 public LogManager addConfigurationListener(Runnable listener) {
2606 final Runnable r = Objects.requireNonNull(listener);
2607 checkPermission();
2608 final SecurityManager sm = System.getSecurityManager();
2609 final AccessControlContext acc =
2610 sm == null ? null : AccessController.getContext();
2611 final PrivilegedAction<Void> pa =
2612 acc == null ? null : () -> { r.run() ; return null; };
2613 final Runnable pr =
2614 acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
2615 // Will do nothing if already registered.
2616 listeners.putIfAbsent(r, pr);
2617 return this;
2618 }
2619
2620 /**
2621 * Removes a previously registered configuration listener.
2622 *
2623 * Returns silently if the listener is not found.
2624 *
2625 * @param listener the configuration listener to remove.
2626 * @throws NullPointerException if the listener is null.
2627 * @throws SecurityException if a security manager exists and if the
2628 * caller does not have LoggingPermission("control").
2629 *
2630 * @since 9
2631 */
2632 public void removeConfigurationListener(Runnable listener) {
2633 final Runnable key = Objects.requireNonNull(listener);
2634 checkPermission();
2635 listeners.remove(key);
2636 }
2637
2638 private void invokeConfigurationListeners() {
2639 Throwable t = null;
2640
2641 // We're using an IdentityHashMap because we want to compare
2642 // keys using identity (==).
2643 // We don't want to loop within a block synchronized on 'listeners'
2644 // to avoid invoking listeners from yet another synchronized block.
2645 // So we're taking a snapshot of the values list to avoid the risk of
2646 // ConcurrentModificationException while looping.
2647 //
2648 for (Runnable c : listeners.values().toArray(new Runnable[0])) {
2649 try {
2650 c.run();
2651 } catch (ThreadDeath death) {
2652 throw death;
2653 } catch (Error | RuntimeException x) {
2654 if (t == null) t = x;
2655 else t.addSuppressed(x);
2656 }
2657 }
2658 // Listeners are not supposed to throw exceptions, but if that
2659 // happens, we will rethrow the first error or exception that is raised
2660 // after all listeners have been invoked.
2661 if (t instanceof Error) throw (Error)t;
2662 if (t instanceof RuntimeException) throw (RuntimeException)t;
2663 }
2664
2665 /**
2666 * This class allows the {@link LoggingProviderImpl} to demand loggers on
2667 * behalf of system and application classes.
2668 */
2669 private static final class LoggingProviderAccess
2670 implements LoggingProviderImpl.LogManagerAccess,
2671 PrivilegedAction<Void> {
2672
2673 private LoggingProviderAccess() {
2674 }
2675
2676 /**
2677 * Demands a logger on behalf of the given {@code module}.
2678 * <p>
2679 * If a named logger suitable for the given module is found
2680 * returns it.
2681 * Otherwise, creates a new logger suitable for the given module.
2682 *
2683 * @param name The logger name.
2684 * @param module The module on which behalf the logger is created/retrieved.
2685 * @return A logger for the given {@code module}.
2686 *
2687 * @throws NullPointerException if {@code name} is {@code null}
2688 * or {@code module} is {@code null}.
2689 * @throws IllegalArgumentException if {@code manager} is not the default
2690 * LogManager.
2691 * @throws SecurityException if a security manager is present and the
2692 * calling code doesn't have the
2693 * {@link LoggingPermission LoggingPermission("demandLogger", null)}.
2694 */
2695 @Override
2696 public Logger demandLoggerFor(LogManager manager, String name, Module module) {
2697 if (manager != getLogManager()) {
2698 // having LogManager as parameter just ensures that the
2699 // caller will have initialized the LogManager before reaching
2700 // here.
2701 throw new IllegalArgumentException("manager");
2702 }
2703 Objects.requireNonNull(name);
2704 Objects.requireNonNull(module);
2705 SecurityManager sm = System.getSecurityManager();
2706 if (sm != null) {
2707 sm.checkPermission(controlPermission);
2708 }
2709 if (isSystem(module)) {
2710 return manager.demandSystemLogger(name,
2711 Logger.SYSTEM_LOGGER_RB_NAME, module);
2712 } else {
2713 return manager.demandLogger(name, null, module);
2714 }
2715 }
2716
2717 @Override
2718 public Void run() {
2719 LoggingProviderImpl.setLogManagerAccess(INSTANCE);
2720 return null;
2721 }
2722
2723 static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
2724 }
2725
2726 static {
2727 AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
2728 controlPermission);
2729 }
2730
2731 }
2732