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.AccessController; 29 import java.security.PrivilegedAction; 30 import java.util.function.BiFunction; 31 import java.lang.System.LoggerFinder; 32 import java.lang.System.Logger; 33 import java.lang.ref.WeakReference; 34 import java.util.Objects; 35 import jdk.internal.misc.VM; 36 import sun.util.logging.PlatformLogger; 37 38 /** 39 * This class is a factory for Lazy Loggers; only system loggers can be 40 * Lazy Loggers. 41 */ 42 public final class LazyLoggers { 43 44 static final RuntimePermission LOGGERFINDER_PERMISSION = 45 new RuntimePermission("loggerFinder"); 46 LazyLoggers()47 private LazyLoggers() { 48 throw new InternalError(); 49 } 50 51 /** 52 * This class is used to hold the factories that a Lazy Logger will use 53 * to create (or map) its wrapped logger. 54 * @param <L> {@link Logger} or a subclass of {@link Logger}. 55 */ 56 private static final class LazyLoggerFactories<L extends Logger> { 57 58 /** 59 * A factory method to create an SPI logger. 60 * Usually, this will be something like LazyLoggers::getSystemLogger. 61 */ 62 final BiFunction<String, Module, L> loggerSupplier; 63 64 LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier)65 public LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier) { 66 this(Objects.requireNonNull(loggerSupplier), 67 (Void)null); 68 } 69 LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier, Void unused)70 private LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier, 71 Void unused) { 72 this.loggerSupplier = loggerSupplier; 73 } 74 75 } 76 77 static interface LoggerAccessor { 78 /** 79 * The logger name. 80 * @return The name of the logger that is / will be lazily created. 81 */ getLoggerName()82 public String getLoggerName(); 83 84 /** 85 * Returns the wrapped logger object. 86 * @return the wrapped logger object. 87 */ wrapped()88 public Logger wrapped(); 89 90 /** 91 * A PlatformLogger.Bridge view of the wrapped logger object. 92 * @return A PlatformLogger.Bridge view of the wrapped logger object. 93 */ platform()94 public PlatformLogger.Bridge platform(); 95 } 96 97 /** 98 * The LazyLoggerAccessor class holds all the logic that delays the creation 99 * of the SPI logger until such a time that the VM is booted and the logger 100 * is actually used for logging. 101 * 102 * This class uses the services of the BootstrapLogger class to instantiate 103 * temporary loggers if appropriate. 104 */ 105 static final class LazyLoggerAccessor implements LoggerAccessor { 106 107 // The factories that will be used to create the logger lazyly 108 final LazyLoggerFactories<? extends Logger> factories; 109 110 // We need to pass the actual caller module when creating the logger. 111 private final WeakReference<Module> moduleRef; 112 113 // The name of the logger that will be created lazyly 114 final String name; 115 // The plain logger SPI object - null until it is accessed for the 116 // first time. 117 private volatile Logger w; 118 // A PlatformLogger.Bridge view of w. 119 private volatile PlatformLogger.Bridge p; 120 121 LazyLoggerAccessor(String name, LazyLoggerFactories<? extends Logger> factories, Module module)122 private LazyLoggerAccessor(String name, 123 LazyLoggerFactories<? extends Logger> factories, 124 Module module) { 125 this(Objects.requireNonNull(name), Objects.requireNonNull(factories), 126 Objects.requireNonNull(module), null); 127 } 128 LazyLoggerAccessor(String name, LazyLoggerFactories<? extends Logger> factories, Module module, Void unused)129 private LazyLoggerAccessor(String name, 130 LazyLoggerFactories<? extends Logger> factories, 131 Module module, Void unused) { 132 this.name = name; 133 this.factories = factories; 134 this.moduleRef = new WeakReference<>(module); 135 } 136 137 /** 138 * The logger name. 139 * @return The name of the logger that is / will be lazily created. 140 */ 141 @Override getLoggerName()142 public String getLoggerName() { 143 return name; 144 } 145 146 // must be called in synchronized block 147 // set wrapped logger if not set setWrappedIfNotSet(Logger wrapped)148 private void setWrappedIfNotSet(Logger wrapped) { 149 if (w == null) { 150 w = wrapped; 151 } 152 } 153 154 /** 155 * Returns the logger SPI object, creating it if 'w' is still null. 156 * @return the logger SPI object. 157 */ wrapped()158 public Logger wrapped() { 159 Logger wrapped = w; 160 if (wrapped != null) return wrapped; 161 // Wrapped logger not created yet: create it. 162 // BootstrapLogger has the logic to decide whether to invoke the 163 // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) 164 // logger. 165 wrapped = BootstrapLogger.getLogger(this); 166 synchronized(this) { 167 // if w has already been in between, simply drop 'wrapped'. 168 setWrappedIfNotSet(wrapped); 169 return w; 170 } 171 } 172 173 /** 174 * A PlatformLogger.Bridge view of the wrapped logger. 175 * @return A PlatformLogger.Bridge view of the wrapped logger. 176 */ platform()177 public PlatformLogger.Bridge platform() { 178 // We can afford to return the platform view of the previous 179 // logger - if that view is not null. 180 // Because that view will either be the BootstrapLogger, which 181 // will redirect to the new wrapper properly, or the temporary 182 // logger - which in effect is equivalent to logging something 183 // just before the application initialized LogManager. 184 PlatformLogger.Bridge platform = p; 185 if (platform != null) return platform; 186 synchronized (this) { 187 if (w != null) { 188 if (p == null) p = PlatformLogger.Bridge.convert(w); 189 return p; 190 } 191 } 192 // If we reach here it means that the wrapped logger may not 193 // have been created yet: attempt to create it. 194 // BootstrapLogger has the logic to decide whether to invoke the 195 // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) 196 // logger. 197 final Logger wrapped = BootstrapLogger.getLogger(this); 198 synchronized(this) { 199 // if w has already been set, simply drop 'wrapped'. 200 setWrappedIfNotSet(wrapped); 201 if (p == null) p = PlatformLogger.Bridge.convert(w); 202 return p; 203 } 204 } 205 206 /** 207 * Makes this accessor release a temporary logger. 208 * This method is called 209 * by BootstrapLogger when JUL is the default backend and LogManager 210 * is initialized, in order to replace temporary SimpleConsoleLoggers by 211 * real JUL loggers. See BootstrapLogger for more details. 212 * If {@code replace} is {@code true}, then this method will force 213 * the accessor to eagerly recreate its wrapped logger. 214 * Note: passing {@code replace=false} is no guarantee that the 215 * method will not actually replace the released logger. 216 * @param temporary The temporary logger too be released. 217 * @param replace Whether the released logger should be eagerly 218 * replaced. 219 */ release(SimpleConsoleLogger temporary, boolean replace)220 void release(SimpleConsoleLogger temporary, boolean replace) { 221 PlatformLogger.ConfigurableBridge.LoggerConfiguration conf = 222 PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary); 223 PlatformLogger.Level level = conf != null 224 ? conf.getPlatformLevel() 225 : null; 226 synchronized (this) { 227 if (this.w == temporary) { 228 this.w = null; this.p = null; 229 } 230 } 231 PlatformLogger.Bridge platform = replace || level != null 232 ? this.platform() : null; 233 234 if (level != null) { 235 conf = (platform != null && platform != temporary) 236 ? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform) 237 : null; 238 if (conf != null) conf.setPlatformLevel(level); 239 } 240 } 241 242 /** 243 * Replace 'w' by the real SPI logger and flush the log messages pending 244 * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when 245 * this accessor's bootstrap logger is accessed and BootstrapLogger 246 * notices that the VM is no longer booting. 247 * @param bootstrap This accessor's bootstrap logger (usually this is 'w'). 248 */ getConcreteLogger(BootstrapLogger bootstrap)249 Logger getConcreteLogger(BootstrapLogger bootstrap) { 250 assert VM.isBooted(); 251 synchronized(this) { 252 // another thread may have already invoked flush() 253 if (this.w == bootstrap) { 254 this.w = null; this.p = null; 255 } 256 } 257 return this.wrapped(); 258 } 259 getConcretePlatformLogger(BootstrapLogger bootstrap)260 PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) { 261 assert VM.isBooted(); 262 synchronized(this) { 263 // another thread may have already invoked flush() 264 if (this.w == bootstrap) { 265 this.w = null; this.p = null; 266 } 267 } 268 return this.platform(); 269 } 270 271 // Creates the wrapped logger by invoking the SPI. createLogger()272 Logger createLogger() { 273 final Module module = moduleRef.get(); 274 if (module == null) { 275 throw new IllegalStateException("The module for which this logger" 276 + " was created has been garbage collected"); 277 } 278 return this.factories.loggerSupplier.apply(name, module); 279 } 280 281 /** 282 * Creates a new lazy logger accessor for the named logger. The given 283 * factories will be use when it becomes necessary to actually create 284 * the logger. 285 * @param <T> An interface that extends {@link Logger}. 286 * @param name The logger name. 287 * @param factories The factories that should be used to create the 288 * wrapped logger. 289 * @return A new LazyLoggerAccessor. 290 */ makeAccessor(String name, LazyLoggerFactories<? extends Logger> factories, Module module)291 public static LazyLoggerAccessor makeAccessor(String name, 292 LazyLoggerFactories<? extends Logger> factories, Module module) { 293 return new LazyLoggerAccessor(name, factories, module); 294 } 295 296 } 297 298 /** 299 * An implementation of {@link Logger} that redirects all calls to a wrapped 300 * instance of {@code Logger}. 301 */ 302 private static class LazyLoggerWrapper 303 extends AbstractLoggerWrapper<Logger> { 304 305 final LoggerAccessor loggerAccessor; 306 LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier)307 public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) { 308 this(Objects.requireNonNull(loggerSinkSupplier), (Void)null); 309 } 310 LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, Void unused)311 private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, 312 Void unused) { 313 this.loggerAccessor = loggerSinkSupplier; 314 } 315 316 @Override wrapped()317 final Logger wrapped() { 318 return loggerAccessor.wrapped(); 319 } 320 321 @Override platformProxy()322 PlatformLogger.Bridge platformProxy() { 323 return loggerAccessor.platform(); 324 } 325 326 } 327 328 // Do not expose this outside of this package. 329 private static volatile LoggerFinder provider; 330 @SuppressWarnings("removal") accessLoggerFinder()331 private static LoggerFinder accessLoggerFinder() { 332 LoggerFinder prov = provider; 333 if (prov == null) { 334 // no need to lock: it doesn't matter if we call 335 // getLoggerFinder() twice - since LoggerFinder already caches 336 // the result. 337 // This is just an optimization to avoid the cost of calling 338 // doPrivileged every time. 339 final SecurityManager sm = System.getSecurityManager(); 340 prov = sm == null ? LoggerFinder.getLoggerFinder() : 341 AccessController.doPrivileged( 342 (PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder); 343 provider = prov; 344 } 345 return prov; 346 } 347 348 // Avoid using lambda here as lazy loggers could be created early 349 // in the bootstrap sequence... 350 private static final BiFunction<String, Module, Logger> loggerSupplier = 351 new BiFunction<>() { 352 @Override 353 public Logger apply(String name, Module module) { 354 return LazyLoggers.getLoggerFromFinder(name, module); 355 } 356 }; 357 358 private static final LazyLoggerFactories<Logger> factories = 359 new LazyLoggerFactories<>(loggerSupplier); 360 361 362 363 // A concrete implementation of Logger that delegates to a System.Logger, 364 // but only creates the System.Logger instance lazily when it's used for 365 // the first time. 366 // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies 367 // on the logic embedded in BootstrapLogger to avoid loading the concrete 368 // logger provider until the VM has finished booting. 369 // 370 private static final class JdkLazyLogger extends LazyLoggerWrapper { JdkLazyLogger(String name, Module module)371 JdkLazyLogger(String name, Module module) { 372 this(LazyLoggerAccessor.makeAccessor(name, factories, module), 373 (Void)null); 374 } JdkLazyLogger(LazyLoggerAccessor holder, Void unused)375 private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) { 376 super(holder); 377 } 378 } 379 380 /** 381 * Gets a logger from the LoggerFinder. Creates the actual concrete 382 * logger. 383 * @param name name of the logger 384 * @param module module on behalf of which the logger is created 385 * @return The logger returned by the LoggerFinder. 386 */ 387 @SuppressWarnings("removal") getLoggerFromFinder(String name, Module module)388 static Logger getLoggerFromFinder(String name, Module module) { 389 final SecurityManager sm = System.getSecurityManager(); 390 if (sm == null) { 391 return accessLoggerFinder().getLogger(name, module); 392 } else { 393 return AccessController.doPrivileged((PrivilegedAction<Logger>) 394 () -> {return accessLoggerFinder().getLogger(name, module);}, 395 null, LOGGERFINDER_PERMISSION); 396 } 397 } 398 399 /** 400 * Returns a (possibly lazy) Logger for the caller. 401 * 402 * @param name the logger name 403 * @param module The module on behalf of which the logger is created. 404 * If the module is not loaded from the Boot ClassLoader, 405 * the LoggerFinder is accessed and the logger returned 406 * by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Module)} 407 * is returned to the caller directly. 408 * Otherwise, the logger returned by 409 * {@link #getLazyLogger(java.lang.String, java.lang.Module)} 410 * is returned to the caller. 411 * 412 * @return a (possibly lazy) Logger instance. 413 */ getLogger(String name, Module module)414 public static final Logger getLogger(String name, Module module) { 415 if (DefaultLoggerFinder.isSystem(module)) { 416 return getLazyLogger(name, module); 417 } else { 418 return getLoggerFromFinder(name, module); 419 } 420 } 421 422 /** 423 * Returns a (possibly lazy) Logger suitable for system classes. 424 * Whether the returned logger is lazy or not depend on the result 425 * returned by {@link BootstrapLogger#useLazyLoggers()}. 426 * 427 * @param name the logger name 428 * @param module the module on behalf of which the logger is created. 429 * @return a (possibly lazy) Logger instance. 430 */ getLazyLogger(String name, Module module)431 public static final Logger getLazyLogger(String name, Module module) { 432 433 // BootstrapLogger has the logic to determine whether a LazyLogger 434 // should be used. Usually, it is worth it only if: 435 // - the VM is not yet booted 436 // - or, the backend is JUL and there is no configuration 437 // - or, the backend is a custom backend, as we don't know what 438 // that is going to load... 439 // So if for instance the VM is booted and we use JUL with a custom 440 // configuration, we're not going to delay the creation of loggers... 441 final boolean useLazyLogger = BootstrapLogger.useLazyLoggers(); 442 if (useLazyLogger) { 443 return new JdkLazyLogger(name, module); 444 } else { 445 // Directly invoke the LoggerFinder. 446 return getLoggerFromFinder(name, module); 447 } 448 } 449 450 } 451