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