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