Path: blob/master/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java
41159 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.logger;2627import java.security.AccessControlContext;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.util.HashMap;31import java.util.Iterator;32import java.util.Map;33import java.util.ResourceBundle;34import java.util.ServiceLoader;35import java.util.function.BooleanSupplier;36import java.util.function.Function;37import java.util.function.Supplier;38import java.lang.System.LoggerFinder;39import java.lang.System.Logger;40import java.lang.System.Logger.Level;41import java.lang.ref.WeakReference;42import java.util.Objects;43import java.util.concurrent.ExecutionException;44import java.util.concurrent.ExecutorService;45import java.util.concurrent.LinkedBlockingQueue;46import java.util.concurrent.ThreadFactory;47import java.util.concurrent.ThreadPoolExecutor;48import java.util.concurrent.TimeUnit;49import jdk.internal.misc.InnocuousThread;50import jdk.internal.misc.VM;51import sun.util.logging.PlatformLogger;52import jdk.internal.logger.LazyLoggers.LazyLoggerAccessor;5354/**55* The BootstrapLogger class handles all the logic needed by Lazy Loggers56* to delay the creation of System.Logger instances until the VM is booted.57* By extension - it also contains the logic that will delay the creation58* of JUL Loggers until the LogManager is initialized by the application, in59* the common case where JUL is the default and there is no custom JUL60* configuration.61*62* A BootstrapLogger instance is both a Logger and a63* PlatformLogger.Bridge instance, which will put all Log messages in a queue64* until the VM is booted.65* Once the VM is booted, it obtain the real System.Logger instance from the66* LoggerFinder and flushes the message to the queue.67*68* There are a few caveat:69* - the queue may not be flush until the next message is logged after70* the VM is booted71* - while the BootstrapLogger is active, the default implementation72* for all convenience methods is used73* - PlatformLogger.setLevel calls are ignored74*75*76*/77public final class BootstrapLogger implements Logger, PlatformLogger.Bridge,78PlatformLogger.ConfigurableBridge {7980// We use the BootstrapExecutors class to submit delayed messages81// to an independent InnocuousThread which will ensure that82// delayed log events will be clearly identified as messages that have83// been delayed during the boot sequence.84private static class BootstrapExecutors implements ThreadFactory {8586// Maybe that should be made configurable with system properties.87static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;8889// The BootstrapMessageLoggerTask is a Runnable which keeps90// a hard ref to the ExecutorService that owns it.91// This ensure that the ExecutorService is not gc'ed until the thread92// has stopped running.93private static class BootstrapMessageLoggerTask implements Runnable {94ExecutorService owner;95Runnable run;96public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {97this.owner = owner;98this.run = r;99}100@Override101public void run() {102try {103run.run();104} finally {105owner = null; // allow the ExecutorService to be gced.106}107}108}109110private static volatile WeakReference<ExecutorService> executorRef;111private static ExecutorService getExecutor() {112WeakReference<ExecutorService> ref = executorRef;113ExecutorService executor = ref == null ? null : ref.get();114if (executor != null) return executor;115synchronized (BootstrapExecutors.class) {116ref = executorRef;117executor = ref == null ? null : ref.get();118if (executor == null) {119executor = new ThreadPoolExecutor(0, 1,120KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,121new LinkedBlockingQueue<>(), new BootstrapExecutors());122}123// The executor service will be elligible for gc124// KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)125// after the execution of its last pending task.126executorRef = new WeakReference<>(executor);127return executorRef.get();128}129}130131@Override132public Thread newThread(Runnable r) {133ExecutorService owner = getExecutor();134@SuppressWarnings("removal")135Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {136@Override137public Thread run() {138Thread t = InnocuousThread.newThread(new BootstrapMessageLoggerTask(owner, r));139t.setName("BootstrapMessageLoggerTask-"+t.getName());140return t;141}142}, null, new RuntimePermission("enableContextClassLoaderOverride"));143thread.setDaemon(true);144return thread;145}146147static void submit(Runnable r) {148getExecutor().execute(r);149}150151// This is used by tests.152static void join(Runnable r) {153try {154getExecutor().submit(r).get();155} catch (InterruptedException | ExecutionException ex) {156// should not happen157throw new RuntimeException(ex);158}159}160161// This is used by tests.162static void awaitPendingTasks() {163WeakReference<ExecutorService> ref = executorRef;164ExecutorService executor = ref == null ? null : ref.get();165if (ref == null) {166synchronized(BootstrapExecutors.class) {167ref = executorRef;168executor = ref == null ? null : ref.get();169}170}171if (executor != null) {172// since our executor uses a FIFO and has a single thread173// then awaiting the execution of its pending tasks can be done174// simply by registering a new task and waiting until it175// completes. This of course would not work if we were using176// several threads, but we don't.177join(()->{});178}179}180181// This is used by tests.182static boolean isAlive() {183WeakReference<ExecutorService> ref = executorRef;184if (ref != null && !ref.refersTo(null)) return true;185synchronized (BootstrapExecutors.class) {186ref = executorRef;187return ref != null && !ref.refersTo(null);188}189}190191// The pending log event queue. The first event is the head, and192// new events are added at the tail193static LogEvent head, tail;194195static void enqueue(LogEvent event) {196if (event.next != null) return;197synchronized (BootstrapExecutors.class) {198if (event.next != null) return;199event.next = event;200if (tail == null) {201head = tail = event;202} else {203tail.next = event;204tail = event;205}206}207}208209static void flush() {210LogEvent event;211// drain the whole queue212synchronized(BootstrapExecutors.class) {213event = head;214head = tail = null;215}216while(event != null) {217LogEvent.log(event);218synchronized(BootstrapExecutors.class) {219LogEvent prev = event;220event = (event.next == event ? null : event.next);221prev.next = null;222}223}224}225}226227// The accessor in which this logger is temporarily set.228final LazyLoggerAccessor holder;229230BootstrapLogger(LazyLoggerAccessor holder) {231this.holder = holder;232}233234// Temporary data object storing log events235// It would be nice to use a Consumer<Logger> instead of a LogEvent.236// This way we could simply do things like:237// push((logger) -> logger.log(level, msg));238// Unfortunately, if we come to here it means we are in the bootsraping239// phase where using lambdas is not safe yet - so we have to use240// a data object instead...241//242static final class LogEvent {243// only one of these two levels should be non null244final Level level;245final PlatformLogger.Level platformLevel;246final BootstrapLogger bootstrap;247248final ResourceBundle bundle;249final String msg;250final Throwable thrown;251final Object[] params;252final Supplier<String> msgSupplier;253final String sourceClass;254final String sourceMethod;255final long timeMillis;256final long nanoAdjustment;257258// because logging a message may entail calling toString() on259// the parameters etc... we need to store the context of the260// caller who logged the message - so that we can reuse it when261// we finally log the message.262@SuppressWarnings("removal")263final AccessControlContext acc;264265// The next event in the queue266LogEvent next;267268@SuppressWarnings("removal")269private LogEvent(BootstrapLogger bootstrap, Level level,270ResourceBundle bundle, String msg,271Throwable thrown, Object[] params) {272this.acc = AccessController.getContext();273this.timeMillis = System.currentTimeMillis();274this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);275this.level = level;276this.platformLevel = null;277this.bundle = bundle;278this.msg = msg;279this.msgSupplier = null;280this.thrown = thrown;281this.params = params;282this.sourceClass = null;283this.sourceMethod = null;284this.bootstrap = bootstrap;285}286287@SuppressWarnings("removal")288private LogEvent(BootstrapLogger bootstrap, Level level,289Supplier<String> msgSupplier,290Throwable thrown, Object[] params) {291this.acc = AccessController.getContext();292this.timeMillis = System.currentTimeMillis();293this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);294this.level = level;295this.platformLevel = null;296this.bundle = null;297this.msg = null;298this.msgSupplier = msgSupplier;299this.thrown = thrown;300this.params = params;301this.sourceClass = null;302this.sourceMethod = null;303this.bootstrap = bootstrap;304}305306@SuppressWarnings("removal")307private LogEvent(BootstrapLogger bootstrap,308PlatformLogger.Level platformLevel,309String sourceClass, String sourceMethod,310ResourceBundle bundle, String msg,311Throwable thrown, Object[] params) {312this.acc = AccessController.getContext();313this.timeMillis = System.currentTimeMillis();314this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);315this.level = null;316this.platformLevel = platformLevel;317this.bundle = bundle;318this.msg = msg;319this.msgSupplier = null;320this.thrown = thrown;321this.params = params;322this.sourceClass = sourceClass;323this.sourceMethod = sourceMethod;324this.bootstrap = bootstrap;325}326327@SuppressWarnings("removal")328private LogEvent(BootstrapLogger bootstrap,329PlatformLogger.Level platformLevel,330String sourceClass, String sourceMethod,331Supplier<String> msgSupplier,332Throwable thrown, Object[] params) {333this.acc = AccessController.getContext();334this.timeMillis = System.currentTimeMillis();335this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);336this.level = null;337this.platformLevel = platformLevel;338this.bundle = null;339this.msg = null;340this.msgSupplier = msgSupplier;341this.thrown = thrown;342this.params = params;343this.sourceClass = sourceClass;344this.sourceMethod = sourceMethod;345this.bootstrap = bootstrap;346}347348// Log this message in the given logger. Do not call directly.349// Use LogEvent.log(LogEvent, logger) instead.350private void log(Logger logger) {351assert platformLevel == null && level != null;352//new Exception("logging delayed message").printStackTrace();353if (msgSupplier != null) {354if (thrown != null) {355logger.log(level, msgSupplier, thrown);356} else {357logger.log(level, msgSupplier);358}359} else {360// BootstrapLoggers are never localized so we can safely361// use the method that takes a ResourceBundle parameter362// even when that resource bundle is null.363if (thrown != null) {364logger.log(level, bundle, msg, thrown);365} else {366logger.log(level, bundle, msg, params);367}368}369}370371// Log this message in the given logger. Do not call directly.372// Use LogEvent.doLog(LogEvent, logger) instead.373private void log(PlatformLogger.Bridge logger) {374assert platformLevel != null && level == null;375if (sourceClass == null) {376if (msgSupplier != null) {377if (thrown != null) {378logger.log(platformLevel, thrown, msgSupplier);379} else {380logger.log(platformLevel, msgSupplier);381}382} else {383// BootstrapLoggers are never localized so we can safely384// use the method that takes a ResourceBundle parameter385// even when that resource bundle is null.386if (thrown != null) {387logger.logrb(platformLevel, bundle, msg, thrown);388} else {389logger.logrb(platformLevel, bundle, msg, params);390}391}392} else {393if (msgSupplier != null) {394if (thrown != null) {395logger.logp(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);396} else {397logger.logp(platformLevel, sourceClass, sourceMethod, msgSupplier);398}399} else {400// BootstrapLoggers are never localized so we can safely401// use the method that takes a ResourceBundle parameter402// even when that resource bundle is null.403if (thrown != null) {404logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);405} else {406logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);407}408}409}410}411412// non default methods from Logger interface413static LogEvent valueOf(BootstrapLogger bootstrap, Level level,414ResourceBundle bundle, String key, Throwable thrown) {415return new LogEvent(Objects.requireNonNull(bootstrap),416Objects.requireNonNull(level), bundle, key,417thrown, null);418}419static LogEvent valueOf(BootstrapLogger bootstrap, Level level,420ResourceBundle bundle, String format, Object[] params) {421return new LogEvent(Objects.requireNonNull(bootstrap),422Objects.requireNonNull(level), bundle, format,423null, params);424}425static LogEvent valueOf(BootstrapLogger bootstrap, Level level,426Supplier<String> msgSupplier, Throwable thrown) {427return new LogEvent(Objects.requireNonNull(bootstrap),428Objects.requireNonNull(level),429Objects.requireNonNull(msgSupplier), thrown, null);430}431static LogEvent valueOf(BootstrapLogger bootstrap, Level level,432Supplier<String> msgSupplier) {433return new LogEvent(Objects.requireNonNull(bootstrap),434Objects.requireNonNull(level),435Objects.requireNonNull(msgSupplier), null, null);436}437@SuppressWarnings("removal")438static void log(LogEvent log, Logger logger) {439final SecurityManager sm = System.getSecurityManager();440// not sure we can actually use lambda here. We may need to create441// an anonymous class. Although if we reach here, then it means442// the VM is booted.443if (sm == null || log.acc == null) {444BootstrapExecutors.submit(() -> log.log(logger));445} else {446BootstrapExecutors.submit(() ->447AccessController.doPrivileged((PrivilegedAction<Void>) () -> {448log.log(logger); return null;449}, log.acc));450}451}452453// non default methods from PlatformLogger.Bridge interface454static LogEvent valueOf(BootstrapLogger bootstrap,455PlatformLogger.Level level, String msg) {456return new LogEvent(Objects.requireNonNull(bootstrap),457Objects.requireNonNull(level), null, null, null,458msg, null, null);459}460static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,461String msg, Throwable thrown) {462return new LogEvent(Objects.requireNonNull(bootstrap),463Objects.requireNonNull(level), null, null, null, msg, thrown, null);464}465static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,466String msg, Object[] params) {467return new LogEvent(Objects.requireNonNull(bootstrap),468Objects.requireNonNull(level), null, null, null, msg, null, params);469}470static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,471Supplier<String> msgSupplier) {472return new LogEvent(Objects.requireNonNull(bootstrap),473Objects.requireNonNull(level), null, null, msgSupplier, null, null);474}475static LogEvent vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,476Supplier<String> msgSupplier,477Throwable thrown) {478return new LogEvent(Objects.requireNonNull(bootstrap),479Objects.requireNonNull(level), null, null,480msgSupplier, thrown, null);481}482static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,483String sourceClass, String sourceMethod,484ResourceBundle bundle, String msg, Object[] params) {485return new LogEvent(Objects.requireNonNull(bootstrap),486Objects.requireNonNull(level), sourceClass,487sourceMethod, bundle, msg, null, params);488}489static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,490String sourceClass, String sourceMethod,491ResourceBundle bundle, String msg, Throwable thrown) {492return new LogEvent(Objects.requireNonNull(bootstrap),493Objects.requireNonNull(level), sourceClass,494sourceMethod, bundle, msg, thrown, null);495}496static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,497String sourceClass, String sourceMethod,498Supplier<String> msgSupplier, Throwable thrown) {499return new LogEvent(Objects.requireNonNull(bootstrap),500Objects.requireNonNull(level), sourceClass,501sourceMethod, msgSupplier, thrown, null);502}503@SuppressWarnings("removal")504static void log(LogEvent log, PlatformLogger.Bridge logger) {505final SecurityManager sm = System.getSecurityManager();506if (sm == null || log.acc == null) {507log.log(logger);508} else {509// not sure we can actually use lambda here. We may need to create510// an anonymous class. Although if we reach here, then it means511// the VM is booted.512AccessController.doPrivileged((PrivilegedAction<Void>) () -> {513log.log(logger); return null;514}, log.acc);515}516}517518static void log(LogEvent event) {519event.bootstrap.flush(event);520}521522}523524// Push a log event at the end of the pending LogEvent queue.525void push(LogEvent log) {526BootstrapExecutors.enqueue(log);527// if the queue has been flushed just before we entered528// the synchronized block we need to flush it again.529checkBootstrapping();530}531532// Flushes the queue of pending LogEvents to the logger.533void flush(LogEvent event) {534assert event.bootstrap == this;535if (event.platformLevel != null) {536PlatformLogger.Bridge concrete = holder.getConcretePlatformLogger(this);537LogEvent.log(event, concrete);538} else {539Logger concrete = holder.getConcreteLogger(this);540LogEvent.log(event, concrete);541}542}543544/**545* The name of this logger. This is the name of the actual logger for which546* this logger acts as a temporary proxy.547* @return The logger name.548*/549@Override550public String getName() {551return holder.name;552}553554/**555* Check whether the VM is still bootstrapping, and if not, arranges556* for this logger's holder to create the real logger and flush the557* pending event queue.558* @return true if the VM is still bootstrapping.559*/560boolean checkBootstrapping() {561if (isBooted()) {562BootstrapExecutors.flush();563return false;564}565return true;566}567568// ----------------------------------569// Methods from Logger570// ----------------------------------571572@Override573public boolean isLoggable(Level level) {574if (checkBootstrapping()) {575return level.getSeverity() >= Level.INFO.getSeverity();576} else {577final Logger spi = holder.wrapped();578return spi.isLoggable(level);579}580}581582@Override583public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {584if (checkBootstrapping()) {585push(LogEvent.valueOf(this, level, bundle, key, thrown));586} else {587final Logger spi = holder.wrapped();588spi.log(level, bundle, key, thrown);589}590}591592@Override593public void log(Level level, ResourceBundle bundle, String format, Object... params) {594if (checkBootstrapping()) {595push(LogEvent.valueOf(this, level, bundle, format, params));596} else {597final Logger spi = holder.wrapped();598spi.log(level, bundle, format, params);599}600}601602@Override603public void log(Level level, String msg, Throwable thrown) {604if (checkBootstrapping()) {605push(LogEvent.valueOf(this, level, null, msg, thrown));606} else {607final Logger spi = holder.wrapped();608spi.log(level, msg, thrown);609}610}611612@Override613public void log(Level level, String format, Object... params) {614if (checkBootstrapping()) {615push(LogEvent.valueOf(this, level, null, format, params));616} else {617final Logger spi = holder.wrapped();618spi.log(level, format, params);619}620}621622@Override623public void log(Level level, Supplier<String> msgSupplier) {624if (checkBootstrapping()) {625push(LogEvent.valueOf(this, level, msgSupplier));626} else {627final Logger spi = holder.wrapped();628spi.log(level, msgSupplier);629}630}631632@Override633public void log(Level level, Object obj) {634if (checkBootstrapping()) {635Logger.super.log(level, obj);636} else {637final Logger spi = holder.wrapped();638spi.log(level, obj);639}640}641642@Override643public void log(Level level, String msg) {644if (checkBootstrapping()) {645push(LogEvent.valueOf(this, level, null, msg, (Object[])null));646} else {647final Logger spi = holder.wrapped();648spi.log(level, msg);649}650}651652@Override653public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {654if (checkBootstrapping()) {655push(LogEvent.valueOf(this, level, msgSupplier, thrown));656} else {657final Logger spi = holder.wrapped();658spi.log(level, msgSupplier, thrown);659}660}661662// ----------------------------------663// Methods from PlatformLogger.Bridge664// ----------------------------------665666@Override667public boolean isLoggable(PlatformLogger.Level level) {668if (checkBootstrapping()) {669return level.intValue() >= PlatformLogger.Level.INFO.intValue();670} else {671final PlatformLogger.Bridge spi = holder.platform();672return spi.isLoggable(level);673}674}675676@Override677public boolean isEnabled() {678if (checkBootstrapping()) {679return true;680} else {681final PlatformLogger.Bridge spi = holder.platform();682return spi.isEnabled();683}684}685686@Override687public void log(PlatformLogger.Level level, String msg) {688if (checkBootstrapping()) {689push(LogEvent.valueOf(this, level, msg));690} else {691final PlatformLogger.Bridge spi = holder.platform();692spi.log(level, msg);693}694}695696@Override697public void log(PlatformLogger.Level level, String msg, Throwable thrown) {698if (checkBootstrapping()) {699push(LogEvent.valueOf(this, level, msg, thrown));700} else {701final PlatformLogger.Bridge spi = holder.platform();702spi.log(level, msg, thrown);703}704}705706@Override707public void log(PlatformLogger.Level level, String msg, Object... params) {708if (checkBootstrapping()) {709push(LogEvent.valueOf(this, level, msg, params));710} else {711final PlatformLogger.Bridge spi = holder.platform();712spi.log(level, msg, params);713}714}715716@Override717public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {718if (checkBootstrapping()) {719push(LogEvent.valueOf(this, level, msgSupplier));720} else {721final PlatformLogger.Bridge spi = holder.platform();722spi.log(level, msgSupplier);723}724}725726@Override727public void log(PlatformLogger.Level level, Throwable thrown,728Supplier<String> msgSupplier) {729if (checkBootstrapping()) {730push(LogEvent.vaueOf(this, level, msgSupplier, thrown));731} else {732final PlatformLogger.Bridge spi = holder.platform();733spi.log(level, thrown, msgSupplier);734}735}736737@Override738public void logp(PlatformLogger.Level level, String sourceClass,739String sourceMethod, String msg) {740if (checkBootstrapping()) {741push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null,742msg, (Object[])null));743} else {744final PlatformLogger.Bridge spi = holder.platform();745spi.logp(level, sourceClass, sourceMethod, msg);746}747}748749@Override750public void logp(PlatformLogger.Level level, String sourceClass,751String sourceMethod, Supplier<String> msgSupplier) {752if (checkBootstrapping()) {753push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, null));754} else {755final PlatformLogger.Bridge spi = holder.platform();756spi.logp(level, sourceClass, sourceMethod, msgSupplier);757}758}759760@Override761public void logp(PlatformLogger.Level level, String sourceClass,762String sourceMethod, String msg, Object... params) {763if (checkBootstrapping()) {764push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, params));765} else {766final PlatformLogger.Bridge spi = holder.platform();767spi.logp(level, sourceClass, sourceMethod, msg, params);768}769}770771@Override772public void logp(PlatformLogger.Level level, String sourceClass,773String sourceMethod, String msg, Throwable thrown) {774if (checkBootstrapping()) {775push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, thrown));776} else {777final PlatformLogger.Bridge spi = holder.platform();778spi.logp(level, sourceClass, sourceMethod, msg, thrown);779}780}781782@Override783public void logp(PlatformLogger.Level level, String sourceClass,784String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {785if (checkBootstrapping()) {786push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, thrown));787} else {788final PlatformLogger.Bridge spi = holder.platform();789spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);790}791}792793@Override794public void logrb(PlatformLogger.Level level, String sourceClass,795String sourceMethod, ResourceBundle bundle, String msg, Object... params) {796if (checkBootstrapping()) {797push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, params));798} else {799final PlatformLogger.Bridge spi = holder.platform();800spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);801}802}803804@Override805public void logrb(PlatformLogger.Level level, String sourceClass,806String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {807if (checkBootstrapping()) {808push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, thrown));809} else {810final PlatformLogger.Bridge spi = holder.platform();811spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);812}813}814815@Override816public void logrb(PlatformLogger.Level level, ResourceBundle bundle,817String msg, Object... params) {818if (checkBootstrapping()) {819push(LogEvent.valueOf(this, level, null, null, bundle, msg, params));820} else {821final PlatformLogger.Bridge spi = holder.platform();822spi.logrb(level, bundle, msg, params);823}824}825826@Override827public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {828if (checkBootstrapping()) {829push(LogEvent.valueOf(this, level, null, null, bundle, msg, thrown));830} else {831final PlatformLogger.Bridge spi = holder.platform();832spi.logrb(level, bundle, msg, thrown);833}834}835836@Override837public LoggerConfiguration getLoggerConfiguration() {838if (checkBootstrapping()) {839// This practically means that PlatformLogger.setLevel()840// calls will be ignored if the VM is still bootstrapping. We could841// attempt to fix that but is it worth it?842return PlatformLogger.ConfigurableBridge.super.getLoggerConfiguration();843} else {844final PlatformLogger.Bridge spi = holder.platform();845return PlatformLogger.ConfigurableBridge.getLoggerConfiguration(spi);846}847}848849// This BooleanSupplier is a hook for tests - so that we can simulate850// what would happen before the VM is booted.851private static volatile BooleanSupplier isBooted;852public static boolean isBooted() {853if (isBooted != null) return isBooted.getAsBoolean();854else return VM.isBooted();855}856857// A bit of magic. We try to find out the nature of the logging858// backend without actually loading it.859private static enum LoggingBackend {860// There is no LoggerFinder and JUL is not present861NONE(true),862863// There is no LoggerFinder, but we have found a864// JdkLoggerFinder installed (which means JUL is present),865// and we haven't found any custom configuration for JUL.866// Until LogManager is initialized we can use a simple console867// logger.868JUL_DEFAULT(false),869870// Same as above, except that we have found a custom configuration871// for JUL. We cannot use the simple console logger in this case.872JUL_WITH_CONFIG(true),873874// We have found a custom LoggerFinder.875CUSTOM(true);876877final boolean useLoggerFinder;878private LoggingBackend(boolean useLoggerFinder) {879this.useLoggerFinder = useLoggerFinder;880}881};882883// The purpose of this class is to delay the initialization of884// the detectedBackend field until it is actually read.885// We do not want this field to get initialized if VM.isBooted() is false.886@SuppressWarnings("removal")887private static final class DetectBackend {888static final LoggingBackend detectedBackend;889static {890detectedBackend = AccessController.doPrivileged(new PrivilegedAction<LoggingBackend>() {891@Override892public LoggingBackend run() {893final Iterator<LoggerFinder> iterator =894ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())895.iterator();896if (iterator.hasNext()) {897return LoggingBackend.CUSTOM; // Custom Logger Provider is registered898}899// No custom logger provider: we will be using the default900// backend.901final Iterator<DefaultLoggerFinder> iterator2 =902ServiceLoader.loadInstalled(DefaultLoggerFinder.class)903.iterator();904if (iterator2.hasNext()) {905// LoggingProviderImpl is registered. The default906// implementation is java.util.logging907String cname = System.getProperty("java.util.logging.config.class");908String fname = System.getProperty("java.util.logging.config.file");909return (cname != null || fname != null)910? LoggingBackend.JUL_WITH_CONFIG911: LoggingBackend.JUL_DEFAULT;912} else {913// SimpleConsoleLogger is used914return LoggingBackend.NONE;915}916}917});918919}920}921922// We will use a temporary SurrogateLogger if923// the logging backend is JUL, there is no custom config,924// and the LogManager has not been initialized yet.925private static boolean useSurrogateLoggers() {926// being paranoid: this should already have been checked927if (!isBooted()) return true;928return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT929&& !logManagerConfigured;930}931932// We will use lazy loggers if:933// - the VM is not yet booted934// - the logging backend is a custom backend935// - the logging backend is JUL, there is no custom config,936// and the LogManager has not been initialized yet.937public static synchronized boolean useLazyLoggers() {938return !BootstrapLogger.isBooted()939|| DetectBackend.detectedBackend == LoggingBackend.CUSTOM940|| useSurrogateLoggers();941}942943// Called by LazyLoggerAccessor. This method will determine whether944// to create a BootstrapLogger (if the VM is not yet booted),945// a SurrogateLogger (if JUL is the default backend and there946// is no custom JUL configuration and LogManager is not yet initialized),947// or a logger returned by the loaded LoggerFinder (all other cases).948static Logger getLogger(LazyLoggerAccessor accessor) {949if (!BootstrapLogger.isBooted()) {950return new BootstrapLogger(accessor);951} else {952if (useSurrogateLoggers()) {953// JUL is the default backend, there is no custom configuration,954// LogManager has not been used.955synchronized(BootstrapLogger.class) {956if (useSurrogateLoggers()) {957return createSurrogateLogger(accessor);958}959}960}961// Already booted. Return the real logger.962return accessor.createLogger();963}964}965966967// If the backend is JUL, and there is no custom configuration, and968// nobody has attempted to call LogManager.getLogManager() yet, then969// we can temporarily substitute JUL Logger with SurrogateLoggers,970// which avoids the cost of actually loading up the LogManager...971// The RedirectedLoggers class has the logic to create such surrogate972// loggers, and to possibly replace them with real JUL loggers if973// someone calls LogManager.getLogManager().974static final class RedirectedLoggers implements975Function<LazyLoggerAccessor, SurrogateLogger> {976977// all accesses must be synchronized on the outer BootstrapLogger.class978final Map<LazyLoggerAccessor, SurrogateLogger> redirectedLoggers =979new HashMap<>();980981// all accesses must be synchronized on the outer BootstrapLogger.class982// The redirectLoggers map will be cleared when LogManager is initialized.983boolean cleared;984985@Override986// all accesses must be synchronized on the outer BootstrapLogger.class987public SurrogateLogger apply(LazyLoggerAccessor t) {988if (cleared) throw new IllegalStateException("LoggerFinder already initialized");989return SurrogateLogger.makeSurrogateLogger(t.getLoggerName());990}991992// all accesses must be synchronized on the outer BootstrapLogger.class993SurrogateLogger get(LazyLoggerAccessor a) {994if (cleared) throw new IllegalStateException("LoggerFinder already initialized");995return redirectedLoggers.computeIfAbsent(a, this);996}997998// all accesses must be synchronized on the outer BootstrapLogger.class999Map<LazyLoggerAccessor, SurrogateLogger> drainLoggersMap() {1000if (redirectedLoggers.isEmpty()) return null;1001if (cleared) throw new IllegalStateException("LoggerFinder already initialized");1002final Map<LazyLoggerAccessor, SurrogateLogger> accessors = new HashMap<>(redirectedLoggers);1003redirectedLoggers.clear();1004cleared = true;1005return accessors;1006}10071008static void replaceSurrogateLoggers(Map<LazyLoggerAccessor, SurrogateLogger> accessors) {1009// When the backend is JUL we want to force the creation of1010// JUL loggers here: some tests are expecting that the1011// PlatformLogger will create JUL loggers as soon as the1012// LogManager is initialized.1013//1014// If the backend is not JUL then we can delay the re-creation1015// of the wrapped logger until they are next accessed.1016//1017final LoggingBackend detectedBackend = DetectBackend.detectedBackend;1018final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT1019&& detectedBackend != LoggingBackend.JUL_WITH_CONFIG;1020for (Map.Entry<LazyLoggerAccessor, SurrogateLogger> a : accessors.entrySet()) {1021a.getKey().release(a.getValue(), !lazy);1022}1023}10241025// all accesses must be synchronized on the outer BootstrapLogger.class1026static final RedirectedLoggers INSTANCE = new RedirectedLoggers();1027}10281029static synchronized Logger createSurrogateLogger(LazyLoggerAccessor a) {1030// accesses to RedirectedLoggers is synchronized on BootstrapLogger.class1031return RedirectedLoggers.INSTANCE.get(a);1032}10331034private static volatile boolean logManagerConfigured;10351036private static synchronized Map<LazyLoggerAccessor, SurrogateLogger>1037releaseSurrogateLoggers() {1038// first check whether there's a chance that we have used1039// surrogate loggers; Will be false if logManagerConfigured is already1040// true.1041final boolean releaseSurrogateLoggers = useSurrogateLoggers();10421043// then sets the flag that tells that the log manager is configured1044logManagerConfigured = true;10451046// finally retrieves all surrogate loggers that should be replaced1047// by real JUL loggers, and return them in the form of a redirected1048// loggers map.1049if (releaseSurrogateLoggers) {1050// accesses to RedirectedLoggers is synchronized on BootstrapLogger.class1051return RedirectedLoggers.INSTANCE.drainLoggersMap();1052} else {1053return null;1054}1055}10561057public static void redirectTemporaryLoggers() {1058// This call is synchronized on BootstrapLogger.class.1059final Map<LazyLoggerAccessor, SurrogateLogger> accessors =1060releaseSurrogateLoggers();10611062// We will now reset the logger accessors, triggering the1063// (possibly lazy) replacement of any temporary surrogate logger by the1064// real logger returned from the loaded LoggerFinder.1065if (accessors != null) {1066RedirectedLoggers.replaceSurrogateLoggers(accessors);1067}10681069BootstrapExecutors.flush();1070}10711072// Hook for tests which need to wait until pending messages1073// are processed.1074static void awaitPendingTasks() {1075BootstrapExecutors.awaitPendingTasks();1076}1077static boolean isAlive() {1078return BootstrapExecutors.isAlive();1079}10801081}108210831084