Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java
41159 views
1
/*
2
* Copyright (c) 2015, 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 jdk.internal.logger;
27
28
import java.security.AccessControlContext;
29
import java.security.AccessController;
30
import java.security.PrivilegedAction;
31
import java.util.HashMap;
32
import java.util.Iterator;
33
import java.util.Map;
34
import java.util.ResourceBundle;
35
import java.util.ServiceLoader;
36
import java.util.function.BooleanSupplier;
37
import java.util.function.Function;
38
import java.util.function.Supplier;
39
import java.lang.System.LoggerFinder;
40
import java.lang.System.Logger;
41
import java.lang.System.Logger.Level;
42
import java.lang.ref.WeakReference;
43
import java.util.Objects;
44
import java.util.concurrent.ExecutionException;
45
import java.util.concurrent.ExecutorService;
46
import java.util.concurrent.LinkedBlockingQueue;
47
import java.util.concurrent.ThreadFactory;
48
import java.util.concurrent.ThreadPoolExecutor;
49
import java.util.concurrent.TimeUnit;
50
import jdk.internal.misc.InnocuousThread;
51
import jdk.internal.misc.VM;
52
import sun.util.logging.PlatformLogger;
53
import jdk.internal.logger.LazyLoggers.LazyLoggerAccessor;
54
55
/**
56
* The BootstrapLogger class handles all the logic needed by Lazy Loggers
57
* to delay the creation of System.Logger instances until the VM is booted.
58
* By extension - it also contains the logic that will delay the creation
59
* of JUL Loggers until the LogManager is initialized by the application, in
60
* the common case where JUL is the default and there is no custom JUL
61
* configuration.
62
*
63
* A BootstrapLogger instance is both a Logger and a
64
* PlatformLogger.Bridge instance, which will put all Log messages in a queue
65
* until the VM is booted.
66
* Once the VM is booted, it obtain the real System.Logger instance from the
67
* LoggerFinder and flushes the message to the queue.
68
*
69
* There are a few caveat:
70
* - the queue may not be flush until the next message is logged after
71
* the VM is booted
72
* - while the BootstrapLogger is active, the default implementation
73
* for all convenience methods is used
74
* - PlatformLogger.setLevel calls are ignored
75
*
76
*
77
*/
78
public final class BootstrapLogger implements Logger, PlatformLogger.Bridge,
79
PlatformLogger.ConfigurableBridge {
80
81
// We use the BootstrapExecutors class to submit delayed messages
82
// to an independent InnocuousThread which will ensure that
83
// delayed log events will be clearly identified as messages that have
84
// been delayed during the boot sequence.
85
private static class BootstrapExecutors implements ThreadFactory {
86
87
// Maybe that should be made configurable with system properties.
88
static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;
89
90
// The BootstrapMessageLoggerTask is a Runnable which keeps
91
// a hard ref to the ExecutorService that owns it.
92
// This ensure that the ExecutorService is not gc'ed until the thread
93
// has stopped running.
94
private static class BootstrapMessageLoggerTask implements Runnable {
95
ExecutorService owner;
96
Runnable run;
97
public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
98
this.owner = owner;
99
this.run = r;
100
}
101
@Override
102
public void run() {
103
try {
104
run.run();
105
} finally {
106
owner = null; // allow the ExecutorService to be gced.
107
}
108
}
109
}
110
111
private static volatile WeakReference<ExecutorService> executorRef;
112
private static ExecutorService getExecutor() {
113
WeakReference<ExecutorService> ref = executorRef;
114
ExecutorService executor = ref == null ? null : ref.get();
115
if (executor != null) return executor;
116
synchronized (BootstrapExecutors.class) {
117
ref = executorRef;
118
executor = ref == null ? null : ref.get();
119
if (executor == null) {
120
executor = new ThreadPoolExecutor(0, 1,
121
KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,
122
new LinkedBlockingQueue<>(), new BootstrapExecutors());
123
}
124
// The executor service will be elligible for gc
125
// KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)
126
// after the execution of its last pending task.
127
executorRef = new WeakReference<>(executor);
128
return executorRef.get();
129
}
130
}
131
132
@Override
133
public Thread newThread(Runnable r) {
134
ExecutorService owner = getExecutor();
135
@SuppressWarnings("removal")
136
Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {
137
@Override
138
public Thread run() {
139
Thread t = InnocuousThread.newThread(new BootstrapMessageLoggerTask(owner, r));
140
t.setName("BootstrapMessageLoggerTask-"+t.getName());
141
return t;
142
}
143
}, null, new RuntimePermission("enableContextClassLoaderOverride"));
144
thread.setDaemon(true);
145
return thread;
146
}
147
148
static void submit(Runnable r) {
149
getExecutor().execute(r);
150
}
151
152
// This is used by tests.
153
static void join(Runnable r) {
154
try {
155
getExecutor().submit(r).get();
156
} catch (InterruptedException | ExecutionException ex) {
157
// should not happen
158
throw new RuntimeException(ex);
159
}
160
}
161
162
// This is used by tests.
163
static void awaitPendingTasks() {
164
WeakReference<ExecutorService> ref = executorRef;
165
ExecutorService executor = ref == null ? null : ref.get();
166
if (ref == null) {
167
synchronized(BootstrapExecutors.class) {
168
ref = executorRef;
169
executor = ref == null ? null : ref.get();
170
}
171
}
172
if (executor != null) {
173
// since our executor uses a FIFO and has a single thread
174
// then awaiting the execution of its pending tasks can be done
175
// simply by registering a new task and waiting until it
176
// completes. This of course would not work if we were using
177
// several threads, but we don't.
178
join(()->{});
179
}
180
}
181
182
// This is used by tests.
183
static boolean isAlive() {
184
WeakReference<ExecutorService> ref = executorRef;
185
if (ref != null && !ref.refersTo(null)) return true;
186
synchronized (BootstrapExecutors.class) {
187
ref = executorRef;
188
return ref != null && !ref.refersTo(null);
189
}
190
}
191
192
// The pending log event queue. The first event is the head, and
193
// new events are added at the tail
194
static LogEvent head, tail;
195
196
static void enqueue(LogEvent event) {
197
if (event.next != null) return;
198
synchronized (BootstrapExecutors.class) {
199
if (event.next != null) return;
200
event.next = event;
201
if (tail == null) {
202
head = tail = event;
203
} else {
204
tail.next = event;
205
tail = event;
206
}
207
}
208
}
209
210
static void flush() {
211
LogEvent event;
212
// drain the whole queue
213
synchronized(BootstrapExecutors.class) {
214
event = head;
215
head = tail = null;
216
}
217
while(event != null) {
218
LogEvent.log(event);
219
synchronized(BootstrapExecutors.class) {
220
LogEvent prev = event;
221
event = (event.next == event ? null : event.next);
222
prev.next = null;
223
}
224
}
225
}
226
}
227
228
// The accessor in which this logger is temporarily set.
229
final LazyLoggerAccessor holder;
230
231
BootstrapLogger(LazyLoggerAccessor holder) {
232
this.holder = holder;
233
}
234
235
// Temporary data object storing log events
236
// It would be nice to use a Consumer<Logger> instead of a LogEvent.
237
// This way we could simply do things like:
238
// push((logger) -> logger.log(level, msg));
239
// Unfortunately, if we come to here it means we are in the bootsraping
240
// phase where using lambdas is not safe yet - so we have to use
241
// a data object instead...
242
//
243
static final class LogEvent {
244
// only one of these two levels should be non null
245
final Level level;
246
final PlatformLogger.Level platformLevel;
247
final BootstrapLogger bootstrap;
248
249
final ResourceBundle bundle;
250
final String msg;
251
final Throwable thrown;
252
final Object[] params;
253
final Supplier<String> msgSupplier;
254
final String sourceClass;
255
final String sourceMethod;
256
final long timeMillis;
257
final long nanoAdjustment;
258
259
// because logging a message may entail calling toString() on
260
// the parameters etc... we need to store the context of the
261
// caller who logged the message - so that we can reuse it when
262
// we finally log the message.
263
@SuppressWarnings("removal")
264
final AccessControlContext acc;
265
266
// The next event in the queue
267
LogEvent next;
268
269
@SuppressWarnings("removal")
270
private LogEvent(BootstrapLogger bootstrap, Level level,
271
ResourceBundle bundle, String msg,
272
Throwable thrown, Object[] params) {
273
this.acc = AccessController.getContext();
274
this.timeMillis = System.currentTimeMillis();
275
this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
276
this.level = level;
277
this.platformLevel = null;
278
this.bundle = bundle;
279
this.msg = msg;
280
this.msgSupplier = null;
281
this.thrown = thrown;
282
this.params = params;
283
this.sourceClass = null;
284
this.sourceMethod = null;
285
this.bootstrap = bootstrap;
286
}
287
288
@SuppressWarnings("removal")
289
private LogEvent(BootstrapLogger bootstrap, Level level,
290
Supplier<String> msgSupplier,
291
Throwable thrown, Object[] params) {
292
this.acc = AccessController.getContext();
293
this.timeMillis = System.currentTimeMillis();
294
this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
295
this.level = level;
296
this.platformLevel = null;
297
this.bundle = null;
298
this.msg = null;
299
this.msgSupplier = msgSupplier;
300
this.thrown = thrown;
301
this.params = params;
302
this.sourceClass = null;
303
this.sourceMethod = null;
304
this.bootstrap = bootstrap;
305
}
306
307
@SuppressWarnings("removal")
308
private LogEvent(BootstrapLogger bootstrap,
309
PlatformLogger.Level platformLevel,
310
String sourceClass, String sourceMethod,
311
ResourceBundle bundle, String msg,
312
Throwable thrown, Object[] params) {
313
this.acc = AccessController.getContext();
314
this.timeMillis = System.currentTimeMillis();
315
this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
316
this.level = null;
317
this.platformLevel = platformLevel;
318
this.bundle = bundle;
319
this.msg = msg;
320
this.msgSupplier = null;
321
this.thrown = thrown;
322
this.params = params;
323
this.sourceClass = sourceClass;
324
this.sourceMethod = sourceMethod;
325
this.bootstrap = bootstrap;
326
}
327
328
@SuppressWarnings("removal")
329
private LogEvent(BootstrapLogger bootstrap,
330
PlatformLogger.Level platformLevel,
331
String sourceClass, String sourceMethod,
332
Supplier<String> msgSupplier,
333
Throwable thrown, Object[] params) {
334
this.acc = AccessController.getContext();
335
this.timeMillis = System.currentTimeMillis();
336
this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
337
this.level = null;
338
this.platformLevel = platformLevel;
339
this.bundle = null;
340
this.msg = null;
341
this.msgSupplier = msgSupplier;
342
this.thrown = thrown;
343
this.params = params;
344
this.sourceClass = sourceClass;
345
this.sourceMethod = sourceMethod;
346
this.bootstrap = bootstrap;
347
}
348
349
// Log this message in the given logger. Do not call directly.
350
// Use LogEvent.log(LogEvent, logger) instead.
351
private void log(Logger logger) {
352
assert platformLevel == null && level != null;
353
//new Exception("logging delayed message").printStackTrace();
354
if (msgSupplier != null) {
355
if (thrown != null) {
356
logger.log(level, msgSupplier, thrown);
357
} else {
358
logger.log(level, msgSupplier);
359
}
360
} else {
361
// BootstrapLoggers are never localized so we can safely
362
// use the method that takes a ResourceBundle parameter
363
// even when that resource bundle is null.
364
if (thrown != null) {
365
logger.log(level, bundle, msg, thrown);
366
} else {
367
logger.log(level, bundle, msg, params);
368
}
369
}
370
}
371
372
// Log this message in the given logger. Do not call directly.
373
// Use LogEvent.doLog(LogEvent, logger) instead.
374
private void log(PlatformLogger.Bridge logger) {
375
assert platformLevel != null && level == null;
376
if (sourceClass == null) {
377
if (msgSupplier != null) {
378
if (thrown != null) {
379
logger.log(platformLevel, thrown, msgSupplier);
380
} else {
381
logger.log(platformLevel, msgSupplier);
382
}
383
} else {
384
// BootstrapLoggers are never localized so we can safely
385
// use the method that takes a ResourceBundle parameter
386
// even when that resource bundle is null.
387
if (thrown != null) {
388
logger.logrb(platformLevel, bundle, msg, thrown);
389
} else {
390
logger.logrb(platformLevel, bundle, msg, params);
391
}
392
}
393
} else {
394
if (msgSupplier != null) {
395
if (thrown != null) {
396
logger.logp(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);
397
} else {
398
logger.logp(platformLevel, sourceClass, sourceMethod, msgSupplier);
399
}
400
} else {
401
// BootstrapLoggers are never localized so we can safely
402
// use the method that takes a ResourceBundle parameter
403
// even when that resource bundle is null.
404
if (thrown != null) {
405
logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);
406
} else {
407
logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);
408
}
409
}
410
}
411
}
412
413
// non default methods from Logger interface
414
static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
415
ResourceBundle bundle, String key, Throwable thrown) {
416
return new LogEvent(Objects.requireNonNull(bootstrap),
417
Objects.requireNonNull(level), bundle, key,
418
thrown, null);
419
}
420
static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
421
ResourceBundle bundle, String format, Object[] params) {
422
return new LogEvent(Objects.requireNonNull(bootstrap),
423
Objects.requireNonNull(level), bundle, format,
424
null, params);
425
}
426
static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
427
Supplier<String> msgSupplier, Throwable thrown) {
428
return new LogEvent(Objects.requireNonNull(bootstrap),
429
Objects.requireNonNull(level),
430
Objects.requireNonNull(msgSupplier), thrown, null);
431
}
432
static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
433
Supplier<String> msgSupplier) {
434
return new LogEvent(Objects.requireNonNull(bootstrap),
435
Objects.requireNonNull(level),
436
Objects.requireNonNull(msgSupplier), null, null);
437
}
438
@SuppressWarnings("removal")
439
static void log(LogEvent log, Logger logger) {
440
final SecurityManager sm = System.getSecurityManager();
441
// not sure we can actually use lambda here. We may need to create
442
// an anonymous class. Although if we reach here, then it means
443
// the VM is booted.
444
if (sm == null || log.acc == null) {
445
BootstrapExecutors.submit(() -> log.log(logger));
446
} else {
447
BootstrapExecutors.submit(() ->
448
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
449
log.log(logger); return null;
450
}, log.acc));
451
}
452
}
453
454
// non default methods from PlatformLogger.Bridge interface
455
static LogEvent valueOf(BootstrapLogger bootstrap,
456
PlatformLogger.Level level, String msg) {
457
return new LogEvent(Objects.requireNonNull(bootstrap),
458
Objects.requireNonNull(level), null, null, null,
459
msg, null, null);
460
}
461
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
462
String msg, Throwable thrown) {
463
return new LogEvent(Objects.requireNonNull(bootstrap),
464
Objects.requireNonNull(level), null, null, null, msg, thrown, null);
465
}
466
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
467
String msg, Object[] params) {
468
return new LogEvent(Objects.requireNonNull(bootstrap),
469
Objects.requireNonNull(level), null, null, null, msg, null, params);
470
}
471
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
472
Supplier<String> msgSupplier) {
473
return new LogEvent(Objects.requireNonNull(bootstrap),
474
Objects.requireNonNull(level), null, null, msgSupplier, null, null);
475
}
476
static LogEvent vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
477
Supplier<String> msgSupplier,
478
Throwable thrown) {
479
return new LogEvent(Objects.requireNonNull(bootstrap),
480
Objects.requireNonNull(level), null, null,
481
msgSupplier, thrown, null);
482
}
483
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
484
String sourceClass, String sourceMethod,
485
ResourceBundle bundle, String msg, Object[] params) {
486
return new LogEvent(Objects.requireNonNull(bootstrap),
487
Objects.requireNonNull(level), sourceClass,
488
sourceMethod, bundle, msg, null, params);
489
}
490
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
491
String sourceClass, String sourceMethod,
492
ResourceBundle bundle, String msg, Throwable thrown) {
493
return new LogEvent(Objects.requireNonNull(bootstrap),
494
Objects.requireNonNull(level), sourceClass,
495
sourceMethod, bundle, msg, thrown, null);
496
}
497
static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
498
String sourceClass, String sourceMethod,
499
Supplier<String> msgSupplier, Throwable thrown) {
500
return new LogEvent(Objects.requireNonNull(bootstrap),
501
Objects.requireNonNull(level), sourceClass,
502
sourceMethod, msgSupplier, thrown, null);
503
}
504
@SuppressWarnings("removal")
505
static void log(LogEvent log, PlatformLogger.Bridge logger) {
506
final SecurityManager sm = System.getSecurityManager();
507
if (sm == null || log.acc == null) {
508
log.log(logger);
509
} else {
510
// not sure we can actually use lambda here. We may need to create
511
// an anonymous class. Although if we reach here, then it means
512
// the VM is booted.
513
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
514
log.log(logger); return null;
515
}, log.acc);
516
}
517
}
518
519
static void log(LogEvent event) {
520
event.bootstrap.flush(event);
521
}
522
523
}
524
525
// Push a log event at the end of the pending LogEvent queue.
526
void push(LogEvent log) {
527
BootstrapExecutors.enqueue(log);
528
// if the queue has been flushed just before we entered
529
// the synchronized block we need to flush it again.
530
checkBootstrapping();
531
}
532
533
// Flushes the queue of pending LogEvents to the logger.
534
void flush(LogEvent event) {
535
assert event.bootstrap == this;
536
if (event.platformLevel != null) {
537
PlatformLogger.Bridge concrete = holder.getConcretePlatformLogger(this);
538
LogEvent.log(event, concrete);
539
} else {
540
Logger concrete = holder.getConcreteLogger(this);
541
LogEvent.log(event, concrete);
542
}
543
}
544
545
/**
546
* The name of this logger. This is the name of the actual logger for which
547
* this logger acts as a temporary proxy.
548
* @return The logger name.
549
*/
550
@Override
551
public String getName() {
552
return holder.name;
553
}
554
555
/**
556
* Check whether the VM is still bootstrapping, and if not, arranges
557
* for this logger's holder to create the real logger and flush the
558
* pending event queue.
559
* @return true if the VM is still bootstrapping.
560
*/
561
boolean checkBootstrapping() {
562
if (isBooted()) {
563
BootstrapExecutors.flush();
564
return false;
565
}
566
return true;
567
}
568
569
// ----------------------------------
570
// Methods from Logger
571
// ----------------------------------
572
573
@Override
574
public boolean isLoggable(Level level) {
575
if (checkBootstrapping()) {
576
return level.getSeverity() >= Level.INFO.getSeverity();
577
} else {
578
final Logger spi = holder.wrapped();
579
return spi.isLoggable(level);
580
}
581
}
582
583
@Override
584
public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
585
if (checkBootstrapping()) {
586
push(LogEvent.valueOf(this, level, bundle, key, thrown));
587
} else {
588
final Logger spi = holder.wrapped();
589
spi.log(level, bundle, key, thrown);
590
}
591
}
592
593
@Override
594
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
595
if (checkBootstrapping()) {
596
push(LogEvent.valueOf(this, level, bundle, format, params));
597
} else {
598
final Logger spi = holder.wrapped();
599
spi.log(level, bundle, format, params);
600
}
601
}
602
603
@Override
604
public void log(Level level, String msg, Throwable thrown) {
605
if (checkBootstrapping()) {
606
push(LogEvent.valueOf(this, level, null, msg, thrown));
607
} else {
608
final Logger spi = holder.wrapped();
609
spi.log(level, msg, thrown);
610
}
611
}
612
613
@Override
614
public void log(Level level, String format, Object... params) {
615
if (checkBootstrapping()) {
616
push(LogEvent.valueOf(this, level, null, format, params));
617
} else {
618
final Logger spi = holder.wrapped();
619
spi.log(level, format, params);
620
}
621
}
622
623
@Override
624
public void log(Level level, Supplier<String> msgSupplier) {
625
if (checkBootstrapping()) {
626
push(LogEvent.valueOf(this, level, msgSupplier));
627
} else {
628
final Logger spi = holder.wrapped();
629
spi.log(level, msgSupplier);
630
}
631
}
632
633
@Override
634
public void log(Level level, Object obj) {
635
if (checkBootstrapping()) {
636
Logger.super.log(level, obj);
637
} else {
638
final Logger spi = holder.wrapped();
639
spi.log(level, obj);
640
}
641
}
642
643
@Override
644
public void log(Level level, String msg) {
645
if (checkBootstrapping()) {
646
push(LogEvent.valueOf(this, level, null, msg, (Object[])null));
647
} else {
648
final Logger spi = holder.wrapped();
649
spi.log(level, msg);
650
}
651
}
652
653
@Override
654
public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
655
if (checkBootstrapping()) {
656
push(LogEvent.valueOf(this, level, msgSupplier, thrown));
657
} else {
658
final Logger spi = holder.wrapped();
659
spi.log(level, msgSupplier, thrown);
660
}
661
}
662
663
// ----------------------------------
664
// Methods from PlatformLogger.Bridge
665
// ----------------------------------
666
667
@Override
668
public boolean isLoggable(PlatformLogger.Level level) {
669
if (checkBootstrapping()) {
670
return level.intValue() >= PlatformLogger.Level.INFO.intValue();
671
} else {
672
final PlatformLogger.Bridge spi = holder.platform();
673
return spi.isLoggable(level);
674
}
675
}
676
677
@Override
678
public boolean isEnabled() {
679
if (checkBootstrapping()) {
680
return true;
681
} else {
682
final PlatformLogger.Bridge spi = holder.platform();
683
return spi.isEnabled();
684
}
685
}
686
687
@Override
688
public void log(PlatformLogger.Level level, String msg) {
689
if (checkBootstrapping()) {
690
push(LogEvent.valueOf(this, level, msg));
691
} else {
692
final PlatformLogger.Bridge spi = holder.platform();
693
spi.log(level, msg);
694
}
695
}
696
697
@Override
698
public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
699
if (checkBootstrapping()) {
700
push(LogEvent.valueOf(this, level, msg, thrown));
701
} else {
702
final PlatformLogger.Bridge spi = holder.platform();
703
spi.log(level, msg, thrown);
704
}
705
}
706
707
@Override
708
public void log(PlatformLogger.Level level, String msg, Object... params) {
709
if (checkBootstrapping()) {
710
push(LogEvent.valueOf(this, level, msg, params));
711
} else {
712
final PlatformLogger.Bridge spi = holder.platform();
713
spi.log(level, msg, params);
714
}
715
}
716
717
@Override
718
public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
719
if (checkBootstrapping()) {
720
push(LogEvent.valueOf(this, level, msgSupplier));
721
} else {
722
final PlatformLogger.Bridge spi = holder.platform();
723
spi.log(level, msgSupplier);
724
}
725
}
726
727
@Override
728
public void log(PlatformLogger.Level level, Throwable thrown,
729
Supplier<String> msgSupplier) {
730
if (checkBootstrapping()) {
731
push(LogEvent.vaueOf(this, level, msgSupplier, thrown));
732
} else {
733
final PlatformLogger.Bridge spi = holder.platform();
734
spi.log(level, thrown, msgSupplier);
735
}
736
}
737
738
@Override
739
public void logp(PlatformLogger.Level level, String sourceClass,
740
String sourceMethod, String msg) {
741
if (checkBootstrapping()) {
742
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null,
743
msg, (Object[])null));
744
} else {
745
final PlatformLogger.Bridge spi = holder.platform();
746
spi.logp(level, sourceClass, sourceMethod, msg);
747
}
748
}
749
750
@Override
751
public void logp(PlatformLogger.Level level, String sourceClass,
752
String sourceMethod, Supplier<String> msgSupplier) {
753
if (checkBootstrapping()) {
754
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, null));
755
} else {
756
final PlatformLogger.Bridge spi = holder.platform();
757
spi.logp(level, sourceClass, sourceMethod, msgSupplier);
758
}
759
}
760
761
@Override
762
public void logp(PlatformLogger.Level level, String sourceClass,
763
String sourceMethod, String msg, Object... params) {
764
if (checkBootstrapping()) {
765
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, params));
766
} else {
767
final PlatformLogger.Bridge spi = holder.platform();
768
spi.logp(level, sourceClass, sourceMethod, msg, params);
769
}
770
}
771
772
@Override
773
public void logp(PlatformLogger.Level level, String sourceClass,
774
String sourceMethod, String msg, Throwable thrown) {
775
if (checkBootstrapping()) {
776
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, thrown));
777
} else {
778
final PlatformLogger.Bridge spi = holder.platform();
779
spi.logp(level, sourceClass, sourceMethod, msg, thrown);
780
}
781
}
782
783
@Override
784
public void logp(PlatformLogger.Level level, String sourceClass,
785
String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
786
if (checkBootstrapping()) {
787
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, thrown));
788
} else {
789
final PlatformLogger.Bridge spi = holder.platform();
790
spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
791
}
792
}
793
794
@Override
795
public void logrb(PlatformLogger.Level level, String sourceClass,
796
String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
797
if (checkBootstrapping()) {
798
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, params));
799
} else {
800
final PlatformLogger.Bridge spi = holder.platform();
801
spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
802
}
803
}
804
805
@Override
806
public void logrb(PlatformLogger.Level level, String sourceClass,
807
String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
808
if (checkBootstrapping()) {
809
push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, thrown));
810
} else {
811
final PlatformLogger.Bridge spi = holder.platform();
812
spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
813
}
814
}
815
816
@Override
817
public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
818
String msg, Object... params) {
819
if (checkBootstrapping()) {
820
push(LogEvent.valueOf(this, level, null, null, bundle, msg, params));
821
} else {
822
final PlatformLogger.Bridge spi = holder.platform();
823
spi.logrb(level, bundle, msg, params);
824
}
825
}
826
827
@Override
828
public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
829
if (checkBootstrapping()) {
830
push(LogEvent.valueOf(this, level, null, null, bundle, msg, thrown));
831
} else {
832
final PlatformLogger.Bridge spi = holder.platform();
833
spi.logrb(level, bundle, msg, thrown);
834
}
835
}
836
837
@Override
838
public LoggerConfiguration getLoggerConfiguration() {
839
if (checkBootstrapping()) {
840
// This practically means that PlatformLogger.setLevel()
841
// calls will be ignored if the VM is still bootstrapping. We could
842
// attempt to fix that but is it worth it?
843
return PlatformLogger.ConfigurableBridge.super.getLoggerConfiguration();
844
} else {
845
final PlatformLogger.Bridge spi = holder.platform();
846
return PlatformLogger.ConfigurableBridge.getLoggerConfiguration(spi);
847
}
848
}
849
850
// This BooleanSupplier is a hook for tests - so that we can simulate
851
// what would happen before the VM is booted.
852
private static volatile BooleanSupplier isBooted;
853
public static boolean isBooted() {
854
if (isBooted != null) return isBooted.getAsBoolean();
855
else return VM.isBooted();
856
}
857
858
// A bit of magic. We try to find out the nature of the logging
859
// backend without actually loading it.
860
private static enum LoggingBackend {
861
// There is no LoggerFinder and JUL is not present
862
NONE(true),
863
864
// There is no LoggerFinder, but we have found a
865
// JdkLoggerFinder installed (which means JUL is present),
866
// and we haven't found any custom configuration for JUL.
867
// Until LogManager is initialized we can use a simple console
868
// logger.
869
JUL_DEFAULT(false),
870
871
// Same as above, except that we have found a custom configuration
872
// for JUL. We cannot use the simple console logger in this case.
873
JUL_WITH_CONFIG(true),
874
875
// We have found a custom LoggerFinder.
876
CUSTOM(true);
877
878
final boolean useLoggerFinder;
879
private LoggingBackend(boolean useLoggerFinder) {
880
this.useLoggerFinder = useLoggerFinder;
881
}
882
};
883
884
// The purpose of this class is to delay the initialization of
885
// the detectedBackend field until it is actually read.
886
// We do not want this field to get initialized if VM.isBooted() is false.
887
@SuppressWarnings("removal")
888
private static final class DetectBackend {
889
static final LoggingBackend detectedBackend;
890
static {
891
detectedBackend = AccessController.doPrivileged(new PrivilegedAction<LoggingBackend>() {
892
@Override
893
public LoggingBackend run() {
894
final Iterator<LoggerFinder> iterator =
895
ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())
896
.iterator();
897
if (iterator.hasNext()) {
898
return LoggingBackend.CUSTOM; // Custom Logger Provider is registered
899
}
900
// No custom logger provider: we will be using the default
901
// backend.
902
final Iterator<DefaultLoggerFinder> iterator2 =
903
ServiceLoader.loadInstalled(DefaultLoggerFinder.class)
904
.iterator();
905
if (iterator2.hasNext()) {
906
// LoggingProviderImpl is registered. The default
907
// implementation is java.util.logging
908
String cname = System.getProperty("java.util.logging.config.class");
909
String fname = System.getProperty("java.util.logging.config.file");
910
return (cname != null || fname != null)
911
? LoggingBackend.JUL_WITH_CONFIG
912
: LoggingBackend.JUL_DEFAULT;
913
} else {
914
// SimpleConsoleLogger is used
915
return LoggingBackend.NONE;
916
}
917
}
918
});
919
920
}
921
}
922
923
// We will use a temporary SurrogateLogger if
924
// the logging backend is JUL, there is no custom config,
925
// and the LogManager has not been initialized yet.
926
private static boolean useSurrogateLoggers() {
927
// being paranoid: this should already have been checked
928
if (!isBooted()) return true;
929
return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT
930
&& !logManagerConfigured;
931
}
932
933
// We will use lazy loggers if:
934
// - the VM is not yet booted
935
// - the logging backend is a custom backend
936
// - the logging backend is JUL, there is no custom config,
937
// and the LogManager has not been initialized yet.
938
public static synchronized boolean useLazyLoggers() {
939
return !BootstrapLogger.isBooted()
940
|| DetectBackend.detectedBackend == LoggingBackend.CUSTOM
941
|| useSurrogateLoggers();
942
}
943
944
// Called by LazyLoggerAccessor. This method will determine whether
945
// to create a BootstrapLogger (if the VM is not yet booted),
946
// a SurrogateLogger (if JUL is the default backend and there
947
// is no custom JUL configuration and LogManager is not yet initialized),
948
// or a logger returned by the loaded LoggerFinder (all other cases).
949
static Logger getLogger(LazyLoggerAccessor accessor) {
950
if (!BootstrapLogger.isBooted()) {
951
return new BootstrapLogger(accessor);
952
} else {
953
if (useSurrogateLoggers()) {
954
// JUL is the default backend, there is no custom configuration,
955
// LogManager has not been used.
956
synchronized(BootstrapLogger.class) {
957
if (useSurrogateLoggers()) {
958
return createSurrogateLogger(accessor);
959
}
960
}
961
}
962
// Already booted. Return the real logger.
963
return accessor.createLogger();
964
}
965
}
966
967
968
// If the backend is JUL, and there is no custom configuration, and
969
// nobody has attempted to call LogManager.getLogManager() yet, then
970
// we can temporarily substitute JUL Logger with SurrogateLoggers,
971
// which avoids the cost of actually loading up the LogManager...
972
// The RedirectedLoggers class has the logic to create such surrogate
973
// loggers, and to possibly replace them with real JUL loggers if
974
// someone calls LogManager.getLogManager().
975
static final class RedirectedLoggers implements
976
Function<LazyLoggerAccessor, SurrogateLogger> {
977
978
// all accesses must be synchronized on the outer BootstrapLogger.class
979
final Map<LazyLoggerAccessor, SurrogateLogger> redirectedLoggers =
980
new HashMap<>();
981
982
// all accesses must be synchronized on the outer BootstrapLogger.class
983
// The redirectLoggers map will be cleared when LogManager is initialized.
984
boolean cleared;
985
986
@Override
987
// all accesses must be synchronized on the outer BootstrapLogger.class
988
public SurrogateLogger apply(LazyLoggerAccessor t) {
989
if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
990
return SurrogateLogger.makeSurrogateLogger(t.getLoggerName());
991
}
992
993
// all accesses must be synchronized on the outer BootstrapLogger.class
994
SurrogateLogger get(LazyLoggerAccessor a) {
995
if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
996
return redirectedLoggers.computeIfAbsent(a, this);
997
}
998
999
// all accesses must be synchronized on the outer BootstrapLogger.class
1000
Map<LazyLoggerAccessor, SurrogateLogger> drainLoggersMap() {
1001
if (redirectedLoggers.isEmpty()) return null;
1002
if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
1003
final Map<LazyLoggerAccessor, SurrogateLogger> accessors = new HashMap<>(redirectedLoggers);
1004
redirectedLoggers.clear();
1005
cleared = true;
1006
return accessors;
1007
}
1008
1009
static void replaceSurrogateLoggers(Map<LazyLoggerAccessor, SurrogateLogger> accessors) {
1010
// When the backend is JUL we want to force the creation of
1011
// JUL loggers here: some tests are expecting that the
1012
// PlatformLogger will create JUL loggers as soon as the
1013
// LogManager is initialized.
1014
//
1015
// If the backend is not JUL then we can delay the re-creation
1016
// of the wrapped logger until they are next accessed.
1017
//
1018
final LoggingBackend detectedBackend = DetectBackend.detectedBackend;
1019
final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT
1020
&& detectedBackend != LoggingBackend.JUL_WITH_CONFIG;
1021
for (Map.Entry<LazyLoggerAccessor, SurrogateLogger> a : accessors.entrySet()) {
1022
a.getKey().release(a.getValue(), !lazy);
1023
}
1024
}
1025
1026
// all accesses must be synchronized on the outer BootstrapLogger.class
1027
static final RedirectedLoggers INSTANCE = new RedirectedLoggers();
1028
}
1029
1030
static synchronized Logger createSurrogateLogger(LazyLoggerAccessor a) {
1031
// accesses to RedirectedLoggers is synchronized on BootstrapLogger.class
1032
return RedirectedLoggers.INSTANCE.get(a);
1033
}
1034
1035
private static volatile boolean logManagerConfigured;
1036
1037
private static synchronized Map<LazyLoggerAccessor, SurrogateLogger>
1038
releaseSurrogateLoggers() {
1039
// first check whether there's a chance that we have used
1040
// surrogate loggers; Will be false if logManagerConfigured is already
1041
// true.
1042
final boolean releaseSurrogateLoggers = useSurrogateLoggers();
1043
1044
// then sets the flag that tells that the log manager is configured
1045
logManagerConfigured = true;
1046
1047
// finally retrieves all surrogate loggers that should be replaced
1048
// by real JUL loggers, and return them in the form of a redirected
1049
// loggers map.
1050
if (releaseSurrogateLoggers) {
1051
// accesses to RedirectedLoggers is synchronized on BootstrapLogger.class
1052
return RedirectedLoggers.INSTANCE.drainLoggersMap();
1053
} else {
1054
return null;
1055
}
1056
}
1057
1058
public static void redirectTemporaryLoggers() {
1059
// This call is synchronized on BootstrapLogger.class.
1060
final Map<LazyLoggerAccessor, SurrogateLogger> accessors =
1061
releaseSurrogateLoggers();
1062
1063
// We will now reset the logger accessors, triggering the
1064
// (possibly lazy) replacement of any temporary surrogate logger by the
1065
// real logger returned from the loaded LoggerFinder.
1066
if (accessors != null) {
1067
RedirectedLoggers.replaceSurrogateLoggers(accessors);
1068
}
1069
1070
BootstrapExecutors.flush();
1071
}
1072
1073
// Hook for tests which need to wait until pending messages
1074
// are processed.
1075
static void awaitPendingTasks() {
1076
BootstrapExecutors.awaitPendingTasks();
1077
}
1078
static boolean isAlive() {
1079
return BootstrapExecutors.isAlive();
1080
}
1081
1082
}
1083
1084