1 /* 2 * Copyright (c) 2009, 2010, 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.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.io.PrintStream; 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.text.DateFormat; 38 import java.text.MessageFormat; 39 import java.util.Date; 40 import java.util.HashMap; 41 import java.util.Map; 42 import sun.misc.JavaLangAccess; 43 import sun.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 * <JAVA_HOME>/lib/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 <JAVA_HOME>/lib/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 // Same values as java.util.logging.Level for easy mapping 92 public static final int OFF = Integer.MAX_VALUE; 93 public static final int SEVERE = 1000; 94 public static final int WARNING = 900; 95 public static final int INFO = 800; 96 public static final int CONFIG = 700; 97 public static final int FINE = 500; 98 public static final int FINER = 400; 99 public static final int FINEST = 300; 100 public static final int ALL = Integer.MIN_VALUE; 101 102 private static final int defaultLevel = INFO; 103 private static boolean loggingEnabled; 104 static { 105 loggingEnabled = AccessController.doPrivileged( 106 new PrivilegedAction<Boolean>() { 107 public Boolean run() { 108 String cname = System.getProperty("java.util.logging.config.class"); 109 String fname = System.getProperty("java.util.logging.config.file"); 110 return (cname != null || fname != null); 111 } 112 }); 113 } 114 115 // Table of known loggers. Maps names to PlatformLoggers. 116 private static Map<String,WeakReference<PlatformLogger>> loggers = 117 new HashMap<>(); 118 119 /** 120 * Returns a PlatformLogger of a given name. 121 */ 122 public static synchronized PlatformLogger getLogger(String name) { 123 PlatformLogger log = null; 124 WeakReference<PlatformLogger> ref = loggers.get(name); 125 if (ref != null) { 126 log = ref.get(); 127 } 128 if (log == null) { 129 log = new PlatformLogger(name); 130 loggers.put(name, new WeakReference<>(log)); 131 } 132 return log; 133 } 134 135 /** 136 * Initialize java.util.logging.Logger objects for all platform loggers. 137 * This method is called from LogManager.readPrimordialConfiguration(). 138 */ 139 public static synchronized void redirectPlatformLoggers() { 140 if (loggingEnabled || !LoggingSupport.isAvailable()) return; 141 142 loggingEnabled = true; 143 for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) { 144 WeakReference<PlatformLogger> ref = entry.getValue(); 145 PlatformLogger plog = ref.get(); 146 if (plog != null) { 147 plog.newJavaLogger(); 148 } 149 } 150 } 151 152 /** 153 * Creates a new JavaLogger that the platform logger uses 154 */ 155 private void newJavaLogger() { 156 logger = new JavaLogger(logger.name, logger.effectiveLevel); 157 } 158 159 // logger may be replaced with a JavaLogger object 160 // when the logging facility is enabled 161 private volatile LoggerProxy logger; 162 163 private PlatformLogger(String name) { 164 if (loggingEnabled) { 165 this.logger = new JavaLogger(name); 166 } else { 167 this.logger = new LoggerProxy(name); 168 } 169 } 170 171 /** 172 * A convenience method to test if the logger is turned off. 173 * (i.e. its level is OFF). 174 */ 175 public boolean isEnabled() { 176 return logger.isEnabled(); 177 } 178 179 /** 180 * Gets the name for this platform logger. 181 */ 182 public String getName() { 183 return logger.name; 184 } 185 186 /** 187 * Returns true if a message of the given level would actually 188 * be logged by this logger. 189 */ 190 public boolean isLoggable(int level) { 191 return logger.isLoggable(level); 192 } 193 194 /** 195 * Gets the current log level. Returns 0 if the current effective level 196 * is not set (equivalent to Logger.getLevel() returns null). 197 */ 198 public int getLevel() { 199 return logger.getLevel(); 200 } 201 202 /** 203 * Sets the log level. 204 */ 205 public void setLevel(int newLevel) { 206 logger.setLevel(newLevel); 207 } 208 209 /** 210 * Logs a SEVERE message. 211 */ 212 public void severe(String msg) { 213 logger.doLog(SEVERE, msg); 214 } 215 216 public void severe(String msg, Throwable t) { 217 logger.doLog(SEVERE, msg, t); 218 } 219 220 public void severe(String msg, Object... params) { 221 logger.doLog(SEVERE, msg, params); 222 } 223 224 /** 225 * Logs a WARNING message. 226 */ 227 public void warning(String msg) { 228 logger.doLog(WARNING, msg); 229 } 230 231 public void warning(String msg, Throwable t) { 232 logger.doLog(WARNING, msg, t); 233 } 234 235 public void warning(String msg, Object... params) { 236 logger.doLog(WARNING, msg, params); 237 } 238 239 /** 240 * Logs an INFO message. 241 */ 242 public void info(String msg) { 243 logger.doLog(INFO, msg); 244 } 245 246 public void info(String msg, Throwable t) { 247 logger.doLog(INFO, msg, t); 248 } 249 250 public void info(String msg, Object... params) { 251 logger.doLog(INFO, msg, params); 252 } 253 254 /** 255 * Logs a CONFIG message. 256 */ 257 public void config(String msg) { 258 logger.doLog(CONFIG, msg); 259 } 260 261 public void config(String msg, Throwable t) { 262 logger.doLog(CONFIG, msg, t); 263 } 264 265 public void config(String msg, Object... params) { 266 logger.doLog(CONFIG, msg, params); 267 } 268 269 /** 270 * Logs a FINE message. 271 */ 272 public void fine(String msg) { 273 logger.doLog(FINE, msg); 274 } 275 276 public void fine(String msg, Throwable t) { 277 logger.doLog(FINE, msg, t); 278 } 279 280 public void fine(String msg, Object... params) { 281 logger.doLog(FINE, msg, params); 282 } 283 284 /** 285 * Logs a FINER message. 286 */ 287 public void finer(String msg) { 288 logger.doLog(FINER, msg); 289 } 290 291 public void finer(String msg, Throwable t) { 292 logger.doLog(FINER, msg, t); 293 } 294 295 public void finer(String msg, Object... params) { 296 logger.doLog(FINER, msg, params); 297 } 298 299 /** 300 * Logs a FINEST message. 301 */ 302 public void finest(String msg) { 303 logger.doLog(FINEST, msg); 304 } 305 306 public void finest(String msg, Throwable t) { 307 logger.doLog(FINEST, msg, t); 308 } 309 310 public void finest(String msg, Object... params) { 311 logger.doLog(FINEST, msg, params); 312 } 313 314 /** 315 * Default platform logging support - output messages to 316 * System.err - equivalent to ConsoleHandler with SimpleFormatter. 317 */ 318 static class LoggerProxy { 319 private static final PrintStream defaultStream = System.err; 320 321 final String name; 322 volatile int levelValue; 323 volatile int effectiveLevel = 0; // current effective level value 324 325 LoggerProxy(String name) { 326 this(name, defaultLevel); 327 } 328 329 LoggerProxy(String name, int level) { 330 this.name = name; 331 this.levelValue = level == 0 ? defaultLevel : level; 332 } 333 334 boolean isEnabled() { 335 return levelValue != OFF; 336 } 337 338 int getLevel() { 339 return effectiveLevel; 340 } 341 342 void setLevel(int newLevel) { 343 levelValue = newLevel; 344 effectiveLevel = newLevel; 345 } 346 347 void doLog(int level, String msg) { 348 if (level < levelValue || levelValue == OFF) { 349 return; 350 } 351 defaultStream.println(format(level, msg, null)); 352 } 353 354 void doLog(int level, String msg, Throwable thrown) { 355 if (level < levelValue || levelValue == OFF) { 356 return; 357 } 358 defaultStream.println(format(level, msg, thrown)); 359 } 360 361 void doLog(int level, String msg, Object... params) { 362 if (level < levelValue || levelValue == OFF) { 363 return; 364 } 365 String newMsg = formatMessage(msg, params); 366 defaultStream.println(format(level, newMsg, null)); 367 } 368 369 public boolean isLoggable(int level) { 370 if (level < levelValue || levelValue == OFF) { 371 return false; 372 } 373 return true; 374 } 375 376 // Copied from java.util.logging.Formatter.formatMessage 377 private String formatMessage(String format, Object... parameters) { 378 // Do the formatting. 379 try { 380 if (parameters == null || parameters.length == 0) { 381 // No parameters. Just return format string. 382 return format; 383 } 384 // Is it a java.text style format? 385 // Ideally we could match with 386 // Pattern.compile("\\{\\d").matcher(format).find()) 387 // However the cost is 14% higher, so we cheaply check for 388 // 1 of the first 4 parameters 389 if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 || 390 format.indexOf("{2") >=0|| format.indexOf("{3") >=0) { 391 return java.text.MessageFormat.format(format, parameters); 392 } 393 return format; 394 } catch (Exception ex) { 395 // Formatting failed: use format string. 396 return format; 397 } 398 } 399 400 static class FormatHelper { 401 final static String lineSeparator = AccessController.doPrivileged( 402 new PrivilegedAction<String>() { 403 public String run() { 404 return System.getProperty("line.separator"); 405 } 406 }); 407 408 // this system property may be set programmatically 409 final static boolean oneLineFormat = AccessController.doPrivileged( 410 new PrivilegedAction<Boolean>() { 411 public Boolean run() { 412 return Boolean.getBoolean("sun.util.logging.oneLineFormat"); 413 } 414 }); 415 } 416 417 private Date dat; 418 private DateFormat formatter; 419 private synchronized String getCurrentTime() { 420 if (dat == null) { 421 dat = new Date(); 422 formatter = DateFormat.getDateTimeInstance(); 423 } 424 425 dat.setTime(System.currentTimeMillis()); 426 return formatter.format(dat); 427 } 428 429 private String format(int level, String msg, Throwable thrown) { 430 StringBuilder sb = new StringBuilder(); 431 if (FormatHelper.oneLineFormat) { 432 sb.append(PlatformLogger.getLevelName(level)); 433 sb.append(": "); 434 sb.append(getCallerInfo()); 435 sb.append(" "); 436 } else { 437 sb.append(getCurrentTime()); 438 sb.append(" "); 439 sb.append(getCallerInfo()); 440 sb.append(FormatHelper.lineSeparator); 441 sb.append(PlatformLogger.getLevelName(level)); 442 sb.append(": "); 443 } 444 sb.append(msg); 445 446 if (thrown != null) { 447 try { 448 StringWriter sw = new StringWriter(); 449 PrintWriter pw = new PrintWriter(sw); 450 thrown.printStackTrace(pw); 451 pw.close(); 452 sb.append(sw.toString()); 453 } catch (Exception ex) { 454 throw new AssertionError(ex); 455 } 456 } 457 458 return sb.toString(); 459 } 460 461 // Returns the caller's class and method's name; best effort 462 // if cannot infer, return the logger's name. 463 private String getCallerInfo() { 464 String sourceClassName = null; 465 String sourceMethodName = null; 466 467 JavaLangAccess access = SharedSecrets.getJavaLangAccess(); 468 Throwable throwable = new Throwable(); 469 int depth = access.getStackTraceDepth(throwable); 470 471 String logClassName = "sun.util.logging.PlatformLogger"; 472 boolean lookingForLogger = true; 473 for (int ix = 0; ix < depth; ix++) { 474 // Calling getStackTraceElement directly prevents the VM 475 // from paying the cost of building the entire stack frame. 476 StackTraceElement frame = 477 access.getStackTraceElement(throwable, ix); 478 String cname = frame.getClassName(); 479 if (lookingForLogger) { 480 // Skip all frames until we have found the first logger frame. 481 if (cname.equals(logClassName)) { 482 lookingForLogger = false; 483 } 484 } else { 485 if (!cname.equals(logClassName)) { 486 // We've found the relevant frame. 487 sourceClassName = cname; 488 sourceMethodName = frame.getMethodName(); 489 break; 490 } 491 } 492 } 493 494 if (sourceClassName != null) { 495 return sourceClassName + " " + sourceMethodName; 496 } else { 497 return name; 498 } 499 } 500 } 501 502 /** 503 * JavaLogger forwards all the calls to its corresponding 504 * java.util.logging.Logger object. 505 */ 506 static class JavaLogger extends LoggerProxy { 507 private static final Map<Integer, Object> levelObjects = 508 new HashMap<>(); 509 510 static { 511 if (LoggingSupport.isAvailable()) { 512 // initialize the map to Level objects 513 getLevelObjects(); 514 } 515 } 516 517 private static void getLevelObjects() { 518 // get all java.util.logging.Level objects 519 int[] levelArray = new int[] {OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL}; 520 for (int l : levelArray) { 521 Object level = LoggingSupport.parseLevel(getLevelName(l)); 522 levelObjects.put(l, level); 523 } 524 } 525 526 private final Object javaLogger; 527 JavaLogger(String name) { 528 this(name, 0); 529 } 530 531 JavaLogger(String name, int level) { 532 super(name, level); 533 this.javaLogger = LoggingSupport.getLogger(name); 534 if (level != 0) { 535 // level has been updated and so set the Logger's level 536 LoggingSupport.setLevel(javaLogger, levelObjects.get(level)); 537 } 538 } 539 540 /** 541 * Let Logger.log() do the filtering since if the level of a 542 * platform logger is altered directly from 543 * java.util.logging.Logger.setLevel(), the levelValue will 544 * not be updated. 545 */ 546 void doLog(int level, String msg) { 547 LoggingSupport.log(javaLogger, levelObjects.get(level), msg); 548 } 549 550 void doLog(int level, String msg, Throwable t) { 551 LoggingSupport.log(javaLogger, levelObjects.get(level), msg, t); 552 } 553 554 void doLog(int level, String msg, Object... params) { 555 LoggingSupport.log(javaLogger, levelObjects.get(level), msg, params); 556 } 557 558 boolean isEnabled() { 559 Object level = LoggingSupport.getLevel(javaLogger); 560 return level == null || level.equals(levelObjects.get(OFF)) == false; 561 } 562 563 int getLevel() { 564 Object level = LoggingSupport.getLevel(javaLogger); 565 if (level != null) { 566 for (Map.Entry<Integer, Object> l : levelObjects.entrySet()) { 567 if (level == l.getValue()) { 568 return l.getKey(); 569 } 570 } 571 } 572 return 0; 573 } 574 575 void setLevel(int newLevel) { 576 levelValue = newLevel; 577 LoggingSupport.setLevel(javaLogger, levelObjects.get(newLevel)); 578 } 579 580 public boolean isLoggable(int level) { 581 return LoggingSupport.isLoggable(javaLogger, levelObjects.get(level)); 582 } 583 } 584 585 private static String getLevelName(int level) { 586 switch (level) { 587 case OFF : return "OFF"; 588 case SEVERE : return "SEVERE"; 589 case WARNING : return "WARNING"; 590 case INFO : return "INFO"; 591 case CONFIG : return "CONFIG"; 592 case FINE : return "FINE"; 593 case FINER : return "FINER"; 594 case FINEST : return "FINEST"; 595 case ALL : return "ALL"; 596 default : return "UNKNOWN"; 597 } 598 } 599 600 }