1 /*
   2  * Copyright (c) 2009, 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 
  27 package sun.util.logging;
  28 
  29 import java.lang.ref.WeakReference;
  30 import java.util.Arrays;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 import java.util.ResourceBundle;
  34 import java.util.function.Supplier;
  35 import jdk.internal.logger.LazyLoggers;
  36 import jdk.internal.logger.LoggerWrapper;
  37 
  38 /**
  39  * Platform logger provides an API for the JRE components to log
  40  * messages.  This enables the runtime components to eliminate the
  41  * static dependency of the logging facility and also defers the
  42  * java.util.logging initialization until it is enabled.
  43  * In addition, the PlatformLogger API can be used if the logging
  44  * module does not exist.
  45  *
  46  * If the logging facility is not enabled, the platform loggers
  47  * will output log messages per the default logging configuration
  48  * (see below). In this implementation, it does not log the
  49  * the stack frame information issuing the log message.
  50  *
  51  * When the logging facility is enabled (at startup or runtime),
  52  * the backend logger will be created for each platform
  53  * logger and all log messages will be forwarded to the Logger
  54  * to handle.
  55  *
  56  * The PlatformLogger uses an underlying PlatformLogger.Bridge instance
  57  * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(}
  58  * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class)
  59  * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}.
  60  *
  61  * Logging facility is "enabled" when one of the following
  62  * conditions is met:
  63  * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class},
  64  *    ClassLoader.getSystemClassLoader()).iterator().hasNext().
  65  * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(),
  66  *    and 2.1) a system property "java.util.logging.config.class" or
  67  *             "java.util.logging.config.file" is set
  68  *     or  2.2) java.util.logging.LogManager or java.util.logging.Logger
  69  *              is referenced that will trigger the logging initialization.
  70  *
  71  * Default logging configuration:
  72  *
  73  *   No LoggerFinder service implementation declared
  74  *   global logging level = INFO
  75  *   handlers = java.util.logging.ConsoleHandler
  76  *   java.util.logging.ConsoleHandler.level = INFO
  77  *   java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
  78  *
  79  * Limitation:
  80  * {@code <JAVA_HOME>/conf/logging.properties} is the system-wide logging
  81  * configuration defined in the specification and read in the
  82  * default case to configure any java.util.logging.Logger instances.
  83  * Platform loggers will not detect if {@code <JAVA_HOME>/conf/logging.properties}
  84  * is modified. In other words, unless the java.util.logging API
  85  * is used at runtime or the logging system properties is set,
  86  * the platform loggers will use the default setting described above.
  87  * The platform loggers are designed for JDK developers use and
  88  * this limitation can be workaround with setting
  89  * -Djava.util.logging.config.file system property.
  90  * <br>
  91  * Calling PlatformLogger.setLevel will not work when there is a custom
  92  * LoggerFinder installed - and as a consequence {@link #setLevel setLevel}
  93  * is now deprecated.
  94  *
  95  * @since 1.7
  96  */
  97 public class PlatformLogger {
  98 
  99     /**
 100      * PlatformLogger logging levels.
 101      */
 102     public static enum Level {
 103         // The name and value must match that of {@code java.util.logging.Level}s.
 104         // Declare in ascending order of the given value for binary search.
 105         ALL(System.Logger.Level.ALL),
 106         FINEST(System.Logger.Level.TRACE),
 107         FINER(System.Logger.Level.TRACE),
 108         FINE(System.Logger.Level.DEBUG),
 109         CONFIG(System.Logger.Level.DEBUG),
 110         INFO(System.Logger.Level.INFO),
 111         WARNING(System.Logger.Level.WARNING),
 112         SEVERE(System.Logger.Level.ERROR),
 113         OFF(System.Logger.Level.OFF);
 114 
 115         final System.Logger.Level systemLevel;
 116         Level(System.Logger.Level systemLevel) {
 117             this.systemLevel = systemLevel;
 118         }
 119 
 120         // The integer values must match that of {@code java.util.logging.Level}
 121         // objects.
 122         private static final int SEVERITY_OFF     = Integer.MAX_VALUE;
 123         private static final int SEVERITY_SEVERE  = 1000;
 124         private static final int SEVERITY_WARNING = 900;
 125         private static final int SEVERITY_INFO    = 800;
 126         private static final int SEVERITY_CONFIG  = 700;
 127         private static final int SEVERITY_FINE    = 500;
 128         private static final int SEVERITY_FINER   = 400;
 129         private static final int SEVERITY_FINEST  = 300;
 130         private static final int SEVERITY_ALL     = Integer.MIN_VALUE;
 131 
 132         // ascending order for binary search matching the list of enum constants
 133         private static final int[] LEVEL_VALUES = new int[] {
 134             SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER,
 135             SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO,
 136             SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF
 137         };
 138 
 139         public System.Logger.Level systemLevel() {
 140             return systemLevel;
 141         }
 142 
 143         public int intValue() {
 144             return LEVEL_VALUES[this.ordinal()];
 145         }
 146 
 147         /**
 148          * Maps a severity value to an effective logger level.
 149          * @param level The severity of the messages that should be
 150          *        logged with a logger set to the returned level.
 151          * @return The effective logger level, which is the nearest Level value
 152          *         whose severity is greater or equal to the given level.
 153          *         For level > SEVERE (OFF excluded), return SEVERE.
 154          */
 155         public static Level valueOf(int level) {
 156             switch (level) {
 157                 // ordering per the highest occurrences in the jdk source
 158                 // finest, fine, finer, info first
 159                 case SEVERITY_FINEST  : return Level.FINEST;
 160                 case SEVERITY_FINE    : return Level.FINE;
 161                 case SEVERITY_FINER   : return Level.FINER;
 162                 case SEVERITY_INFO    : return Level.INFO;
 163                 case SEVERITY_WARNING : return Level.WARNING;
 164                 case SEVERITY_CONFIG  : return Level.CONFIG;
 165                 case SEVERITY_SEVERE  : return Level.SEVERE;
 166                 case SEVERITY_OFF     : return Level.OFF;
 167                 case SEVERITY_ALL     : return Level.ALL;
 168             }
 169             // return the nearest Level value >= the given level,
 170             // for level > SEVERE, return SEVERE and exclude OFF
 171             int i = Arrays.binarySearch(LEVEL_VALUES, 0, LEVEL_VALUES.length-2, level);
 172             return values()[i >= 0 ? i : (-i-1)];
 173         }
 174     }
 175 
 176     /**
 177      *
 178      * The PlatformLogger.Bridge interface is implemented by the System.Logger
 179      * objects returned by our default JUL provider - so that JRE classes using
 180      * PlatformLogger see no difference when JUL is the actual backend.
 181      *
 182      * PlatformLogger is now only a thin adaptation layer over the same
 183      * loggers than returned by java.lang.System.getLogger(String name).
 184      *
 185      * The recommendation for JRE classes going forward is to use
 186      * java.lang.System.getLogger(String name), which will
 187      * use Lazy Loggers when possible and necessary.
 188      *
 189      */
 190     public static interface Bridge {
 191 
 192         /**
 193          * Gets the name for this platform logger.
 194          * @return the name of the platform logger.
 195          */
 196         public String getName();
 197 
 198         /**
 199          * Returns true if a message of the given level would actually
 200          * be logged by this logger.
 201          * @param level the level
 202          * @return whether a message of that level would be logged
 203          */
 204         public boolean isLoggable(Level level);
 205         public boolean isEnabled();
 206 
 207         public void log(Level level, String msg);
 208         public void log(Level level, String msg, Throwable thrown);
 209         public void log(Level level, String msg, Object... params);
 210         public void log(Level level, Supplier<String> msgSupplier);
 211         public void log(Level level, Throwable thrown, Supplier<String> msgSupplier);
 212         public void logp(Level level, String sourceClass, String sourceMethod, String msg);
 213         public void logp(Level level, String sourceClass, String sourceMethod,
 214                          Supplier<String> msgSupplier);
 215         public void logp(Level level, String sourceClass, String sourceMethod,
 216                                                     String msg, Object... params);
 217         public void logp(Level level, String sourceClass, String sourceMethod,
 218                          String msg, Throwable thrown);
 219         public void logp(Level level, String sourceClass, String sourceMethod,
 220                          Throwable thrown, Supplier<String> msgSupplier);
 221         public void logrb(Level level, String sourceClass, String sourceMethod,
 222                           ResourceBundle bundle, String msg, Object... params);
 223         public void logrb(Level level, String sourceClass, String sourceMethod,
 224                           ResourceBundle bundle, String msg, Throwable thrown);
 225         public void logrb(Level level, ResourceBundle bundle, String msg,
 226                 Object... params);
 227         public void logrb(Level level, ResourceBundle bundle, String msg,
 228                 Throwable thrown);
 229 
 230 
 231         public static Bridge convert(System.Logger logger) {
 232             if (logger instanceof PlatformLogger.Bridge) {
 233                 return (Bridge) logger;
 234             } else {
 235                 return new LoggerWrapper<>(logger);
 236             }
 237         }
 238     }
 239 
 240     /**
 241      * The {@code PlatformLogger.ConfigurableBridge} interface is used to
 242      * implement the deprecated {@link PlatformLogger#setLevel} method.
 243      *
 244      * PlatformLogger is now only a thin adaptation layer over the same
 245      * loggers than returned by java.lang.System.getLogger(String name).
 246      *
 247      * The recommendation for JRE classes going forward is to use
 248      * java.lang.System.getLogger(String name), which will
 249      * use Lazy Loggers when possible and necessary.
 250      *
 251      */
 252     public static interface ConfigurableBridge {
 253 
 254         public abstract class LoggerConfiguration {
 255             public abstract Level getPlatformLevel();
 256             public abstract void setPlatformLevel(Level level);
 257         }
 258 
 259         public default LoggerConfiguration getLoggerConfiguration() {
 260             return null;
 261         }
 262 
 263         public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) {
 264             if (logger instanceof PlatformLogger.ConfigurableBridge) {
 265                 return ((ConfigurableBridge) logger).getLoggerConfiguration();
 266             } else {
 267                 return null;
 268             }
 269         }
 270     }
 271 
 272     // Table of known loggers.  Maps names to PlatformLoggers.
 273     private static final Map<String,WeakReference<PlatformLogger>> loggers =
 274         new HashMap<>();
 275 
 276     /**
 277      * Returns a PlatformLogger of a given name.
 278      * @param name the name of the logger
 279      * @return a PlatformLogger
 280      */
 281     public static synchronized PlatformLogger getLogger(String name) {
 282         PlatformLogger log = null;
 283         WeakReference<PlatformLogger> ref = loggers.get(name);
 284         if (ref != null) {
 285             log = ref.get();
 286         }
 287         if (log == null) {
 288             log = new PlatformLogger(PlatformLogger.Bridge.convert(
 289                     // We pass PlatformLogger.class rather than the actual caller
 290                     // because we want PlatformLoggers to be system loggers: we
 291                     // won't need to resolve any resource bundles anyway.
 292                     // Note: Many unit tests depend on the fact that
 293                     //       PlatformLogger.getLoggerFromFinder is not caller sensitive.
 294                     LazyLoggers.getLazyLogger(name, PlatformLogger.class)));
 295             loggers.put(name, new WeakReference<>(log));
 296         }
 297         return log;
 298     }
 299 
 300     // The system loggerProxy returned by LazyLoggers
 301     // This may be a lazy logger - see jdk.internal.logger.LazyLoggers,
 302     // or may be a Logger instance (or a wrapper thereof).
 303     //
 304     private final PlatformLogger.Bridge loggerProxy;
 305     private PlatformLogger(PlatformLogger.Bridge loggerProxy) {
 306         this.loggerProxy = loggerProxy;
 307     }
 308 
 309     /**
 310      * A convenience method to test if the logger is turned off.
 311      * (i.e. its level is OFF).
 312      * @return whether the logger is turned off.
 313      */
 314     public boolean isEnabled() {
 315         return loggerProxy.isEnabled();
 316     }
 317 
 318     /**
 319      * Gets the name for this platform logger.
 320      * @return the name of the platform logger.
 321      */
 322     public String getName() {
 323         return loggerProxy.getName();
 324     }
 325 
 326     /**
 327      * Returns true if a message of the given level would actually
 328      * be logged by this logger.
 329      * @param level the level
 330      * @return whether a message of that level would be logged
 331      */
 332     public boolean isLoggable(Level level) {
 333         if (level == null) {
 334             throw new NullPointerException();
 335         }
 336 
 337         return loggerProxy.isLoggable(level);
 338     }
 339 
 340     /**
 341      * Get the log level that has been specified for this PlatformLogger.
 342      * The result may be null, which means that this logger's
 343      * effective level will be inherited from its parent.
 344      *
 345      * @return  this PlatformLogger's level
 346      */
 347     public Level level() {
 348         final ConfigurableBridge.LoggerConfiguration spi =
 349                 PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);
 350         return spi == null ? null : spi.getPlatformLevel();
 351     }
 352 
 353     /**
 354      * Set the log level specifying which message levels will be
 355      * logged by this logger.  Message levels lower than this
 356      * value will be discarded.  The level value {@link Level#OFF}
 357      * can be used to turn off logging.
 358      * <p>
 359      * If the new level is null, it means that this node should
 360      * inherit its level from its nearest ancestor with a specific
 361      * (non-null) level value.
 362      *
 363      * @param newLevel the new value for the log level (may be null)
 364      * @deprecated Platform Loggers should not be configured programmatically.
 365      *             This method will not work if a custom {@link
 366      *             java.lang.System.LoggerFinder} is installed.
 367      */
 368     @Deprecated
 369     public void setLevel(Level newLevel) {
 370         final ConfigurableBridge.LoggerConfiguration spi =
 371                 PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);;
 372         if (spi != null) {
 373             spi.setPlatformLevel(newLevel);
 374         }
 375     }
 376 
 377     /**
 378      * Logs a SEVERE message.
 379      * @param msg the message
 380      */
 381     public void severe(String msg) {
 382         loggerProxy.log(Level.SEVERE, msg, (Object[])null);
 383     }
 384 
 385     public void severe(String msg, Throwable t) {
 386         loggerProxy.log(Level.SEVERE, msg, t);
 387     }
 388 
 389     public void severe(String msg, Object... params) {
 390         loggerProxy.log(Level.SEVERE, msg, params);
 391     }
 392 
 393     /**
 394      * Logs a WARNING message.
 395      * @param msg the message
 396      */
 397     public void warning(String msg) {
 398         loggerProxy.log(Level.WARNING, msg, (Object[])null);
 399     }
 400 
 401     public void warning(String msg, Throwable t) {
 402         loggerProxy.log(Level.WARNING, msg, t);
 403     }
 404 
 405     public void warning(String msg, Object... params) {
 406         loggerProxy.log(Level.WARNING, msg, params);
 407     }
 408 
 409     /**
 410      * Logs an INFO message.
 411      * @param msg the message
 412      */
 413     public void info(String msg) {
 414         loggerProxy.log(Level.INFO, msg, (Object[])null);
 415     }
 416 
 417     public void info(String msg, Throwable t) {
 418         loggerProxy.log(Level.INFO, msg, t);
 419     }
 420 
 421     public void info(String msg, Object... params) {
 422         loggerProxy.log(Level.INFO, msg, params);
 423     }
 424 
 425     /**
 426      * Logs a CONFIG message.
 427      * @param msg the message
 428      */
 429     public void config(String msg) {
 430         loggerProxy.log(Level.CONFIG, msg, (Object[])null);
 431     }
 432 
 433     public void config(String msg, Throwable t) {
 434         loggerProxy.log(Level.CONFIG, msg, t);
 435     }
 436 
 437     public void config(String msg, Object... params) {
 438         loggerProxy.log(Level.CONFIG, msg, params);
 439     }
 440 
 441     /**
 442      * Logs a FINE message.
 443      * @param msg the message
 444      */
 445     public void fine(String msg) {
 446         loggerProxy.log(Level.FINE, msg, (Object[])null);
 447     }
 448 
 449     public void fine(String msg, Throwable t) {
 450         loggerProxy.log(Level.FINE, msg, t);
 451     }
 452 
 453     public void fine(String msg, Object... params) {
 454         loggerProxy.log(Level.FINE, msg, params);
 455     }
 456 
 457     /**
 458      * Logs a FINER message.
 459      * @param msg the message
 460      */
 461     public void finer(String msg) {
 462         loggerProxy.log(Level.FINER, msg, (Object[])null);
 463     }
 464 
 465     public void finer(String msg, Throwable t) {
 466         loggerProxy.log(Level.FINER, msg, t);
 467     }
 468 
 469     public void finer(String msg, Object... params) {
 470         loggerProxy.log(Level.FINER, msg, params);
 471     }
 472 
 473     /**
 474      * Logs a FINEST message.
 475      * @param msg the message
 476      */
 477     public void finest(String msg) {
 478         loggerProxy.log(Level.FINEST, msg, (Object[])null);
 479     }
 480 
 481     public void finest(String msg, Throwable t) {
 482         loggerProxy.log(Level.FINEST, msg, t);
 483     }
 484 
 485     public void finest(String msg, Object... params) {
 486         loggerProxy.log(Level.FINEST, msg, params);
 487     }
 488 
 489     // ------------------------------------
 490     // Maps used for Level conversion
 491     // ------------------------------------
 492 
 493     // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns
 494     // a PlatformLogger.Level
 495     //
 496     // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF
 497     private static final Level[] spi2platformLevelMapping = {
 498             Level.ALL,     // mapped from ALL
 499             Level.FINER,   // mapped from TRACE
 500             Level.FINE,    // mapped from DEBUG
 501             Level.INFO,    // mapped from INFO
 502             Level.WARNING, // mapped from WARNING
 503             Level.SEVERE,  // mapped from ERROR
 504             Level.OFF      // mapped from OFF
 505     };
 506 
 507     public static Level toPlatformLevel(java.lang.System.Logger.Level level) {
 508         if (level == null) return null;
 509         assert level.ordinal() < spi2platformLevelMapping.length;
 510         return spi2platformLevelMapping[level.ordinal()];
 511     }
 512 
 513 }