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