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