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