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