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 sun.misc.InnocuousThread; 51 import sun.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.log(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier); 393 } else { 394 logger.log(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 }