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.io.PrintStream; 31 import java.io.PrintWriter; 32 import java.io.StringWriter; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.time.Clock; 36 import java.time.Instant; 37 import java.time.ZoneId; 38 import java.time.ZonedDateTime; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.Map; 42 import jdk.internal.misc.JavaLangAccess; 43 import jdk.internal.misc.SharedSecrets; 44 45 /** 46 * Platform logger provides an API for the JRE components to log 47 * messages. This enables the runtime components to eliminate the 48 * static dependency of the logging facility and also defers the 49 * java.util.logging initialization until it is enabled. 50 * In addition, the PlatformLogger API can be used if the logging 51 * module does not exist. 52 * 53 * If the logging facility is not enabled, the platform loggers 54 * will output log messages per the default logging configuration 55 * (see below). In this implementation, it does not log the 56 * the stack frame information issuing the log message. 57 * 58 * When the logging facility is enabled (at startup or runtime), 59 * the java.util.logging.Logger will be created for each platform 60 * logger and all log messages will be forwarded to the Logger 61 * to handle. 62 * 63 * Logging facility is "enabled" when one of the following 64 * conditions is met: 65 * 1) a system property "java.util.logging.config.class" or 66 * "java.util.logging.config.file" is set 67 * 2) java.util.logging.LogManager or java.util.logging.Logger 68 * is referenced that will trigger the logging initialization. 69 * 70 * Default logging configuration: 71 * global logging level = INFO 72 * handlers = java.util.logging.ConsoleHandler 73 * java.util.logging.ConsoleHandler.level = INFO 74 * java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 75 * 76 * Limitation: 77 * {@code <JAVA_HOME>/conf/logging.properties} is the system-wide logging 78 * configuration defined in the specification and read in the 79 * default case to configure any java.util.logging.Logger instances. 80 * Platform loggers will not detect if {@code <JAVA_HOME>/conf/logging.properties} 81 * is modified. In other words, unless the java.util.logging API 82 * is used at runtime or the logging system properties is set, 83 * the platform loggers will use the default setting described above. 84 * The platform loggers are designed for JDK developers use and 85 * this limitation can be workaround with setting 86 * -Djava.util.logging.config.file system property. 87 * 88 * @since 1.7 89 */ 90 public class PlatformLogger { 91 92 // The integer values must match that of {@code java.util.logging.Level} 93 // objects. 94 private static final int OFF = Integer.MAX_VALUE; 95 private static final int SEVERE = 1000; 96 private static final int WARNING = 900; 97 private static final int INFO = 800; 98 private static final int CONFIG = 700; 99 private static final int FINE = 500; 100 private static final int FINER = 400; 101 private static final int FINEST = 300; 102 private static final int ALL = Integer.MIN_VALUE; 103 104 /** 105 * PlatformLogger logging levels. 106 */ 107 public static enum Level { 108 // The name and value must match that of {@code java.util.logging.Level}s. 109 // Declare in ascending order of the given value for binary search. 110 ALL, 111 FINEST, 112 FINER, 113 FINE, 114 CONFIG, 115 INFO, 116 WARNING, 117 SEVERE, 118 OFF; 119 120 /** 121 * Associated java.util.logging.Level lazily initialized in 122 * JavaLoggerProxy's static initializer only once 123 * when java.util.logging is available and enabled. 124 * Only accessed by JavaLoggerProxy. 125 */ 126 /* java.util.logging.Level */ Object javaLevel; 127 128 // ascending order for binary search matching the list of enum constants 129 private static final int[] LEVEL_VALUES = new int[] { 130 PlatformLogger.ALL, PlatformLogger.FINEST, PlatformLogger.FINER, 131 PlatformLogger.FINE, PlatformLogger.CONFIG, PlatformLogger.INFO, 132 PlatformLogger.WARNING, PlatformLogger.SEVERE, PlatformLogger.OFF 133 }; 134 135 public int intValue() { 136 return LEVEL_VALUES[this.ordinal()]; 137 } 138 139 static Level valueOf(int level) { 140 switch (level) { 141 // ordering per the highest occurrences in the jdk source 142 // finest, fine, finer, info first 143 case PlatformLogger.FINEST : return Level.FINEST; 144 case PlatformLogger.FINE : return Level.FINE; 145 case PlatformLogger.FINER : return Level.FINER; 146 case PlatformLogger.INFO : return Level.INFO; 147 case PlatformLogger.WARNING : return Level.WARNING; 148 case PlatformLogger.CONFIG : return Level.CONFIG; 149 case PlatformLogger.SEVERE : return Level.SEVERE; 150 case PlatformLogger.OFF : return Level.OFF; 151 case PlatformLogger.ALL : return Level.ALL; 152 } 153 // return the nearest Level value >= the given level, 154 // for level > SEVERE, return SEVERE and exclude OFF 155 int i = Arrays.binarySearch(LEVEL_VALUES, 0, LEVEL_VALUES.length-2, level); 156 return values()[i >= 0 ? i : (-i-1)]; 157 } 158 } 159 160 private static final Level DEFAULT_LEVEL = Level.INFO; 161 private static boolean loggingEnabled; 162 static { 163 loggingEnabled = AccessController.doPrivileged( 164 new PrivilegedAction<>() { 165 public Boolean run() { 166 String cname = System.getProperty("java.util.logging.config.class"); 167 String fname = System.getProperty("java.util.logging.config.file"); 168 return (cname != null || fname != null); 169 } 170 }); 171 172 // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations 173 // less probable. Don't initialize JavaLoggerProxy class since 174 // java.util.logging may not be enabled. 175 try { 176 Class.forName("sun.util.logging.PlatformLogger$DefaultLoggerProxy", 177 false, 178 PlatformLogger.class.getClassLoader()); 179 Class.forName("sun.util.logging.PlatformLogger$JavaLoggerProxy", 180 false, // do not invoke class initializer 181 PlatformLogger.class.getClassLoader()); 182 } catch (ClassNotFoundException ex) { 183 throw new InternalError(ex); 184 } 185 } 186 187 // Table of known loggers. Maps names to PlatformLoggers. 188 private static Map<String,WeakReference<PlatformLogger>> loggers = 189 new HashMap<>(); 190 191 /** 192 * Returns a PlatformLogger of a given name. 193 */ 194 public static synchronized PlatformLogger getLogger(String name) { 195 PlatformLogger log = null; 196 WeakReference<PlatformLogger> ref = loggers.get(name); 197 if (ref != null) { 198 log = ref.get(); 199 } 200 if (log == null) { 201 log = new PlatformLogger(name); 202 loggers.put(name, new WeakReference<>(log)); 203 } 204 return log; 205 } 206 207 /** 208 * Initialize java.util.logging.Logger objects for all platform loggers. 209 * This method is called from LogManager.readPrimordialConfiguration(). 210 */ 211 public static synchronized void redirectPlatformLoggers() { 212 if (loggingEnabled || !LoggingSupport.isAvailable()) return; 213 214 loggingEnabled = true; 215 for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) { 216 WeakReference<PlatformLogger> ref = entry.getValue(); 217 PlatformLogger plog = ref.get(); 218 if (plog != null) { 219 plog.redirectToJavaLoggerProxy(); 220 } 221 } 222 } 223 224 /** 225 * Creates a new JavaLoggerProxy and redirects the platform logger to it 226 */ 227 private void redirectToJavaLoggerProxy() { 228 DefaultLoggerProxy lp = DefaultLoggerProxy.class.cast(this.loggerProxy); 229 JavaLoggerProxy jlp = new JavaLoggerProxy(lp.name, lp.level); 230 // the order of assignments is important 231 this.javaLoggerProxy = jlp; // isLoggable checks javaLoggerProxy if set 232 this.loggerProxy = jlp; 233 } 234 235 // DefaultLoggerProxy may be replaced with a JavaLoggerProxy object 236 // when the java.util.logging facility is enabled 237 private volatile LoggerProxy loggerProxy; 238 // javaLoggerProxy is only set when the java.util.logging facility is enabled 239 private volatile JavaLoggerProxy javaLoggerProxy; 240 private PlatformLogger(String name) { 241 if (loggingEnabled) { 242 this.loggerProxy = this.javaLoggerProxy = new JavaLoggerProxy(name); 243 } else { 244 this.loggerProxy = new DefaultLoggerProxy(name); 245 } 246 } 247 248 /** 249 * A convenience method to test if the logger is turned off. 250 * (i.e. its level is OFF). 251 */ 252 public boolean isEnabled() { 253 return loggerProxy.isEnabled(); 254 } 255 256 /** 257 * Gets the name for this platform logger. 258 */ 259 public String getName() { 260 return loggerProxy.name; 261 } 262 263 /** 264 * Returns true if a message of the given level would actually 265 * be logged by this logger. 266 */ 267 public boolean isLoggable(Level level) { 268 if (level == null) { 269 throw new NullPointerException(); 270 } 271 // performance-sensitive method: use two monomorphic call-sites 272 JavaLoggerProxy jlp = javaLoggerProxy; 273 return jlp != null ? jlp.isLoggable(level) : loggerProxy.isLoggable(level); 274 } 275 276 /** 277 * Get the log level that has been specified for this PlatformLogger. 278 * The result may be null, which means that this logger's 279 * effective level will be inherited from its parent. 280 * 281 * @return this PlatformLogger's level 282 */ 283 public Level level() { 284 return loggerProxy.getLevel(); 285 } 286 287 /** 288 * Set the log level specifying which message levels will be 289 * logged by this logger. Message levels lower than this 290 * value will be discarded. The level value {@link #OFF} 291 * can be used to turn off logging. 292 * <p> 293 * If the new level is null, it means that this node should 294 * inherit its level from its nearest ancestor with a specific 295 * (non-null) level value. 296 * 297 * @param newLevel the new value for the log level (may be null) 298 */ 299 public void setLevel(Level newLevel) { 300 loggerProxy.setLevel(newLevel); 301 } 302 303 /** 304 * Logs a SEVERE message. 305 */ 306 public void severe(String msg) { 307 loggerProxy.doLog(Level.SEVERE, msg); 308 } 309 310 public void severe(String msg, Throwable t) { 311 loggerProxy.doLog(Level.SEVERE, msg, t); 312 } 313 314 public void severe(String msg, Object... params) { 315 loggerProxy.doLog(Level.SEVERE, msg, params); 316 } 317 318 /** 319 * Logs a WARNING message. 320 */ 321 public void warning(String msg) { 322 loggerProxy.doLog(Level.WARNING, msg); 323 } 324 325 public void warning(String msg, Throwable t) { 326 loggerProxy.doLog(Level.WARNING, msg, t); 327 } 328 329 public void warning(String msg, Object... params) { 330 loggerProxy.doLog(Level.WARNING, msg, params); 331 } 332 333 /** 334 * Logs an INFO message. 335 */ 336 public void info(String msg) { 337 loggerProxy.doLog(Level.INFO, msg); 338 } 339 340 public void info(String msg, Throwable t) { 341 loggerProxy.doLog(Level.INFO, msg, t); 342 } 343 344 public void info(String msg, Object... params) { 345 loggerProxy.doLog(Level.INFO, msg, params); 346 } 347 348 /** 349 * Logs a CONFIG message. 350 */ 351 public void config(String msg) { 352 loggerProxy.doLog(Level.CONFIG, msg); 353 } 354 355 public void config(String msg, Throwable t) { 356 loggerProxy.doLog(Level.CONFIG, msg, t); 357 } 358 359 public void config(String msg, Object... params) { 360 loggerProxy.doLog(Level.CONFIG, msg, params); 361 } 362 363 /** 364 * Logs a FINE message. 365 */ 366 public void fine(String msg) { 367 loggerProxy.doLog(Level.FINE, msg); 368 } 369 370 public void fine(String msg, Throwable t) { 371 loggerProxy.doLog(Level.FINE, msg, t); 372 } 373 374 public void fine(String msg, Object... params) { 375 loggerProxy.doLog(Level.FINE, msg, params); 376 } 377 378 /** 379 * Logs a FINER message. 380 */ 381 public void finer(String msg) { 382 loggerProxy.doLog(Level.FINER, msg); 383 } 384 385 public void finer(String msg, Throwable t) { 386 loggerProxy.doLog(Level.FINER, msg, t); 387 } 388 389 public void finer(String msg, Object... params) { 390 loggerProxy.doLog(Level.FINER, msg, params); 391 } 392 393 /** 394 * Logs a FINEST message. 395 */ 396 public void finest(String msg) { 397 loggerProxy.doLog(Level.FINEST, msg); 398 } 399 400 public void finest(String msg, Throwable t) { 401 loggerProxy.doLog(Level.FINEST, msg, t); 402 } 403 404 public void finest(String msg, Object... params) { 405 loggerProxy.doLog(Level.FINEST, msg, params); 406 } 407 408 /** 409 * Abstract base class for logging support, defining the API and common field. 410 */ 411 private abstract static class LoggerProxy { 412 final String name; 413 414 protected LoggerProxy(String name) { 415 this.name = name; 416 } 417 418 abstract boolean isEnabled(); 419 420 abstract Level getLevel(); 421 abstract void setLevel(Level newLevel); 422 423 abstract void doLog(Level level, String msg); 424 abstract void doLog(Level level, String msg, Throwable thrown); 425 abstract void doLog(Level level, String msg, Object... params); 426 427 abstract boolean isLoggable(Level level); 428 } 429 430 431 private static final class DefaultLoggerProxy extends LoggerProxy { 432 /** 433 * Default platform logging support - output messages to System.err - 434 * equivalent to ConsoleHandler with SimpleFormatter. 435 */ 436 private static PrintStream outputStream() { 437 return System.err; 438 } 439 440 volatile Level effectiveLevel; // effective level (never null) 441 volatile Level level; // current level set for this node (may be null) 442 443 DefaultLoggerProxy(String name) { 444 super(name); 445 this.effectiveLevel = deriveEffectiveLevel(null); 446 this.level = null; 447 } 448 449 boolean isEnabled() { 450 return effectiveLevel != Level.OFF; 451 } 452 453 Level getLevel() { 454 return level; 455 } 456 457 void setLevel(Level newLevel) { 458 Level oldLevel = level; 459 if (oldLevel != newLevel) { 460 level = newLevel; 461 effectiveLevel = deriveEffectiveLevel(newLevel); 462 } 463 } 464 465 void doLog(Level level, String msg) { 466 if (isLoggable(level)) { 467 outputStream().print(format(level, msg, null)); 468 } 469 } 470 471 void doLog(Level level, String msg, Throwable thrown) { 472 if (isLoggable(level)) { 473 outputStream().print(format(level, msg, thrown)); 474 } 475 } 476 477 void doLog(Level level, String msg, Object... params) { 478 if (isLoggable(level)) { 479 String newMsg = formatMessage(msg, params); 480 outputStream().print(format(level, newMsg, null)); 481 } 482 } 483 484 boolean isLoggable(Level level) { 485 Level effectiveLevel = this.effectiveLevel; 486 return level.intValue() >= effectiveLevel.intValue() && effectiveLevel != Level.OFF; 487 } 488 489 // derive effective level (could do inheritance search like j.u.l.Logger) 490 private Level deriveEffectiveLevel(Level level) { 491 return level == null ? DEFAULT_LEVEL : level; 492 } 493 494 // Copied from java.util.logging.Formatter.formatMessage 495 private String formatMessage(String format, Object... parameters) { 496 // Do the formatting. 497 try { 498 if (parameters == null || parameters.length == 0) { 499 // No parameters. Just return format string. 500 return format; 501 } 502 // Is it a java.text style format? 503 // Ideally we could match with 504 // Pattern.compile("\\{\\d").matcher(format).find()) 505 // However the cost is 14% higher, so we cheaply check for 506 // 1 of the first 4 parameters 507 if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 || 508 format.indexOf("{2") >=0|| format.indexOf("{3") >=0) { 509 return java.text.MessageFormat.format(format, parameters); 510 } 511 return format; 512 } catch (Exception ex) { 513 // Formatting failed: use format string. 514 return format; 515 } 516 } 517 518 private static final String formatString = 519 LoggingSupport.getSimpleFormat(false); // don't check logging.properties 520 private final ZoneId zoneId = ZoneId.systemDefault(); 521 private synchronized String format(Level level, String msg, Throwable thrown) { 522 ZonedDateTime zdt = ZonedDateTime.now(zoneId); 523 String throwable = ""; 524 if (thrown != null) { 525 StringWriter sw = new StringWriter(); 526 PrintWriter pw = new PrintWriter(sw); 527 pw.println(); 528 thrown.printStackTrace(pw); 529 pw.close(); 530 throwable = sw.toString(); 531 } 532 533 return String.format(formatString, 534 zdt, 535 getCallerInfo(), 536 name, 537 level.name(), 538 msg, 539 throwable); 540 } 541 542 // Returns the caller's class and method's name; best effort 543 // if cannot infer, return the logger's name. 544 private String getCallerInfo() { 545 String sourceClassName = null; 546 String sourceMethodName = null; 547 548 JavaLangAccess access = SharedSecrets.getJavaLangAccess(); 549 Throwable throwable = new Throwable(); 550 int depth = access.getStackTraceDepth(throwable); 551 552 String logClassName = "sun.util.logging.PlatformLogger"; 553 boolean lookingForLogger = true; 554 for (int ix = 0; ix < depth; ix++) { 555 // Calling getStackTraceElement directly prevents the VM 556 // from paying the cost of building the entire stack frame. 557 StackTraceElement frame = 558 access.getStackTraceElement(throwable, ix); 559 String cname = frame.getClassName(); 560 if (lookingForLogger) { 561 // Skip all frames until we have found the first logger frame. 562 if (cname.equals(logClassName)) { 563 lookingForLogger = false; 564 } 565 } else { 566 if (!cname.equals(logClassName)) { 567 // We've found the relevant frame. 568 sourceClassName = cname; 569 sourceMethodName = frame.getMethodName(); 570 break; 571 } 572 } 573 } 574 575 if (sourceClassName != null) { 576 return sourceClassName + " " + sourceMethodName; 577 } else { 578 return name; 579 } 580 } 581 } 582 583 /** 584 * JavaLoggerProxy forwards all the calls to its corresponding 585 * java.util.logging.Logger object. 586 */ 587 private static final class JavaLoggerProxy extends LoggerProxy { 588 // initialize javaLevel fields for mapping from Level enum -> j.u.l.Level object 589 static { 590 for (Level level : Level.values()) { 591 level.javaLevel = LoggingSupport.parseLevel(level.name()); 592 } 593 } 594 595 private final /* java.util.logging.Logger */ Object javaLogger; 596 597 JavaLoggerProxy(String name) { 598 this(name, null); 599 } 600 601 JavaLoggerProxy(String name, Level level) { 602 super(name); 603 this.javaLogger = LoggingSupport.getLogger(name); 604 if (level != null) { 605 // level has been updated and so set the Logger's level 606 LoggingSupport.setLevel(javaLogger, level.javaLevel); 607 } 608 } 609 610 void doLog(Level level, String msg) { 611 LoggingSupport.log(javaLogger, level.javaLevel, msg); 612 } 613 614 void doLog(Level level, String msg, Throwable t) { 615 LoggingSupport.log(javaLogger, level.javaLevel, msg, t); 616 } 617 618 void doLog(Level level, String msg, Object... params) { 619 if (!isLoggable(level)) { 620 return; 621 } 622 // only pass String objects to the j.u.l.Logger which may 623 // be created by untrusted code 624 int len = (params != null) ? params.length : 0; 625 Object[] sparams = new String[len]; 626 for (int i = 0; i < len; i++) { 627 sparams [i] = String.valueOf(params[i]); 628 } 629 LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams); 630 } 631 632 boolean isEnabled() { 633 return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel); 634 } 635 636 /** 637 * Returns the PlatformLogger.Level mapped from j.u.l.Level 638 * set in the logger. If the j.u.l.Logger is set to a custom Level, 639 * this method will return the nearest Level. 640 */ 641 Level getLevel() { 642 Object javaLevel = LoggingSupport.getLevel(javaLogger); 643 if (javaLevel == null) return null; 644 645 try { 646 return Level.valueOf(LoggingSupport.getLevelName(javaLevel)); 647 } catch (IllegalArgumentException e) { 648 return Level.valueOf(LoggingSupport.getLevelValue(javaLevel)); 649 } 650 } 651 652 void setLevel(Level level) { 653 LoggingSupport.setLevel(javaLogger, level == null ? null : level.javaLevel); 654 } 655 656 boolean isLoggable(Level level) { 657 return LoggingSupport.isLoggable(javaLogger, level.javaLevel); 658 } 659 } 660 } | 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 } |