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.util.Objects; 35 import sun.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 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, Class<?>, L> loggerSupplier; 63 64 65 public LazyLoggerFactories(BiFunction<String, Class<?>, L> loggerSupplier) { 66 this(Objects.requireNonNull(loggerSupplier), 67 (Void)null); 68 } 69 70 private LazyLoggerFactories(BiFunction<String, Class<?>, 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 */ 82 public String getLoggerName(); 83 84 /** 85 * Returns the wrapped logger object. 86 * @return the wrapped logger object. 87 */ 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 */ 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 when creating the logger. 111 private final WeakReference<Class<?>> callerRef; 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 122 private LazyLoggerAccessor(String name, 123 LazyLoggerFactories<? extends Logger> factories, 124 Class<?> caller) { 125 this(Objects.requireNonNull(name), Objects.requireNonNull(factories), 126 Objects.requireNonNull(caller), null); 127 } 128 129 private LazyLoggerAccessor(String name, 130 LazyLoggerFactories<? extends Logger> factories, 131 Class<?> caller, Void unused) { 132 this.name = name; 133 this.factories = factories; 134 this.callerRef = new WeakReference<Class<?>>(caller); 135 } 136 137 /** 138 * The logger name. 139 * @return The name of the logger that is / will be lazily created. 140 */ 141 @Override 142 public String getLoggerName() { 143 return name; 144 } 145 146 // must be called in synchronized block 147 // set wrapped logger if not set 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 */ 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 */ 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 */ 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 */ 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 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. 272 Logger createLogger() { 273 final Class<?> caller = callerRef.get(); 274 if (caller == null) { 275 throw new IllegalStateException("The class for which this logger" 276 + " was created has been garbage collected"); 277 } 278 return this.factories.loggerSupplier.apply(name, caller); 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 */ 291 public static LazyLoggerAccessor makeAccessor(String name, 292 LazyLoggerFactories<? extends Logger> factories, Class<?> caller) { 293 return new LazyLoggerAccessor(name, factories, caller); 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 307 public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) { 308 this(Objects.requireNonNull(loggerSinkSupplier), (Void)null); 309 } 310 311 private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, 312 Void unused) { 313 this.loggerAccessor = loggerSinkSupplier; 314 } 315 316 @Override 317 final Logger wrapped() { 318 return loggerAccessor.wrapped(); 319 } 320 321 @Override 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 = null; 330 private static LoggerFinder accessLoggerFinder() { 331 if (provider == null) { 332 // no need to lock: it doesn't matter if we call 333 // getLoggerFinder() twice - since LoggerFinder already caches 334 // the result. 335 // This is just an optimization to avoid the cost of calling 336 // doPrivileged every time. 337 final SecurityManager sm = System.getSecurityManager(); 338 provider = sm == null ? LoggerFinder.getLoggerFinder() : 339 AccessController.doPrivileged( 340 (PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder); 341 } 342 return provider; 343 } 344 345 // Avoid using lambda here as lazy loggers could be created early 346 // in the bootstrap sequence... 347 private static final BiFunction<String, Class<?>, Logger> loggerSupplier = 348 new BiFunction<>() { 349 @Override 350 public Logger apply(String name, Class<?> caller) { 351 return LazyLoggers.getLoggerFromFinder(name, caller); 352 } 353 }; 354 355 private static final LazyLoggerFactories<Logger> factories = 356 new LazyLoggerFactories<>(loggerSupplier); 357 358 359 360 // A concrete implementation of Logger that delegates to a System.Logger, 361 // but only creates the System.Logger instance lazily when it's used for 362 // the first time. 363 // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies 364 // on the logic embedded in BootstrapLogger to avoid loading the concrete 365 // logger provider until the VM has finished booting. 366 // 367 private static final class JdkLazyLogger extends LazyLoggerWrapper { 368 JdkLazyLogger(String name, Class<?> caller) { 369 this(LazyLoggerAccessor.makeAccessor(name, factories, caller), 370 (Void)null); 371 } 372 private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) { 373 super(holder); 374 } 375 } 376 377 /** 378 * Gets a logger from the LoggerFinder. Creates the actual concrete 379 * logger. 380 * @param name name of the logger 381 * @param caller class on behalf of which the logger is created 382 * @return The logger returned by the LoggerFinder. 383 */ 384 static Logger getLoggerFromFinder(String name, Class<?> caller) { 385 final SecurityManager sm = System.getSecurityManager(); 386 if (sm == null) { 387 return accessLoggerFinder().getLogger(name, caller); 388 } else { 389 return AccessController.doPrivileged((PrivilegedAction<Logger>) 390 () -> {return accessLoggerFinder().getLogger(name, caller);}, 391 null, LOGGERFINDER_PERMISSION); 392 } 393 } 394 395 /** 396 * Returns a (possibly lazy) Logger for the caller. 397 * 398 * @param name the logger name 399 * @param caller The class on behalf of which the logger is created. 400 * If the caller is not loaded from the Boot ClassLoader, 401 * the LoggerFinder is accessed and the logger returned 402 * by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)} 403 * is returned to the caller directly. 404 * Otherwise, the logger returned by 405 * {@link #getLazyLogger(java.lang.String, java.lang.Class)} 406 * is returned to the caller. 407 * 408 * @return a (possibly lazy) Logger instance. 409 */ 410 public static final Logger getLogger(String name, Class<?> caller) { 411 if (caller.getClassLoader() == null) { 412 return getLazyLogger(name, caller); 413 } else { 414 return getLoggerFromFinder(name, caller); 415 } 416 } 417 418 /** 419 * Returns a (possibly lazy) Logger suitable for system classes. 420 * Whether the returned logger is lazy or not depend on the result 421 * returned by {@link BootstrapLogger#useLazyLoggers()}. 422 * 423 * @param name the logger name 424 * @param caller the class on behalf of which the logger is created. 425 * @return a (possibly lazy) Logger instance. 426 */ 427 public static final Logger getLazyLogger(String name, Class<?> caller) { 428 429 // BootstrapLogger has the logic to determine whether a LazyLogger 430 // should be used. Usually, it is worth it only if: 431 // - the VM is not yet booted 432 // - or, the backend is JUL and there is no configuration 433 // - or, the backend is a custom backend, as we don't know what 434 // that is going to load... 435 // So if for instance the VM is booted and we use JUL with a custom 436 // configuration, we're not going to delay the creation of loggers... 437 final boolean useLazyLogger = BootstrapLogger.useLazyLoggers(); 438 if (useLazyLogger) { 439 return new JdkLazyLogger(name, caller); 440 } else { 441 // Directly invoke the LoggerFinder. 442 return getLoggerFromFinder(name, caller); 443 } 444 } 445 446 }