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;
BootstrapMessageLoggerTask(ExecutorService owner, Runnable r)97             public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
98                 this.owner = owner;
99                 this.run = r;
100             }
101             @Override
run()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;
getExecutor()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
newThread(Runnable r)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 
submit(Runnable r)148         static void submit(Runnable r) {
149             getExecutor().execute(r);
150         }
151 
152         // This is used by tests.
join(Runnable r)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.
awaitPendingTasks()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.
isAlive()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 
enqueue(LogEvent event)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 
flush()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 
BootstrapLogger(LazyLoggerAccessor holder)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")
LogEvent(BootstrapLogger bootstrap, Level level, ResourceBundle bundle, String msg, Throwable thrown, Object[] params)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")
LogEvent(BootstrapLogger bootstrap, Level level, Supplier<String> msgSupplier, Throwable thrown, Object[] params)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")
LogEvent(BootstrapLogger bootstrap, PlatformLogger.Level platformLevel, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown, Object[] params)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")
LogEvent(BootstrapLogger bootstrap, PlatformLogger.Level platformLevel, String sourceClass, String sourceMethod, Supplier<String> msgSupplier, Throwable thrown, Object[] params)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.
log(Logger logger)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.
log(PlatformLogger.Bridge logger)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
valueOf(BootstrapLogger bootstrap, Level level, ResourceBundle bundle, String key, Throwable thrown)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         }
valueOf(BootstrapLogger bootstrap, Level level, ResourceBundle bundle, String format, Object[] params)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         }
valueOf(BootstrapLogger bootstrap, Level level, Supplier<String> msgSupplier, Throwable thrown)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         }
valueOf(BootstrapLogger bootstrap, Level level, Supplier<String> msgSupplier)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")
log(LogEvent log, Logger logger)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
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String msg)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String msg, Throwable thrown)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String msg, Object[] params)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, Supplier<String> msgSupplier)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         }
vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, Supplier<String> msgSupplier, Throwable thrown)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Object[] params)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown)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         }
valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level, String sourceClass, String sourceMethod, Supplier<String> msgSupplier, Throwable thrown)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")
log(LogEvent log, PlatformLogger.Bridge logger)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 
log(LogEvent event)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.
push(LogEvent log)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.
flush(LogEvent event)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
getName()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      */
checkBootstrapping()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
isLoggable(Level level)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
log(Level level, ResourceBundle bundle, String key, Throwable thrown)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
log(Level level, ResourceBundle bundle, String format, Object... params)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
log(Level level, String msg, Throwable thrown)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
log(Level level, String format, Object... params)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
log(Level level, Supplier<String> msgSupplier)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
log(Level level, Object obj)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
log(Level level, String msg)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
log(Level level, Supplier<String> msgSupplier, Throwable thrown)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
isLoggable(PlatformLogger.Level level)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
isEnabled()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
log(PlatformLogger.Level level, String msg)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
log(PlatformLogger.Level level, String msg, Throwable thrown)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
log(PlatformLogger.Level level, String msg, Object... params)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
log(PlatformLogger.Level level, Supplier<String> msgSupplier)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
log(PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSupplier)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
logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg)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
logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, Supplier<String> msgSupplier)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
logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg, Object... params)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
logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown)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
logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, Throwable thrown, Supplier<String> msgSupplier)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
logrb(PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Object... params)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
logrb(PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown)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
logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Object... params)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
logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown)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
getLoggerConfiguration()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;
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;
LoggingBackend(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.
useSurrogateLoggers()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.
useLazyLoggers()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).
getLogger(LazyLoggerAccessor accessor)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
apply(LazyLoggerAccessor t)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
get(LazyLoggerAccessor a)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
drainLoggersMap()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 
replaceSurrogateLoggers(Map<LazyLoggerAccessor, SurrogateLogger> accessors)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 
createSurrogateLogger(LazyLoggerAccessor a)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>
releaseSurrogateLoggers()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 
redirectTemporaryLoggers()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.
awaitPendingTasks()1075     static void awaitPendingTasks() {
1076         BootstrapExecutors.awaitPendingTasks();
1077     }
isAlive()1078     static boolean isAlive() {
1079         return BootstrapExecutors.isAlive();
1080     }
1081 
1082 }
1083