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