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