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