1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2000, 2016, 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 package java.util.logging; 28 import dalvik.system.VMStack; 29 import java.util.*; 30 import java.util.concurrent.atomic.AtomicInteger; 31 import java.util.concurrent.atomic.AtomicLong; 32 import java.io.*; 33 34 /** 35 * LogRecord objects are used to pass logging requests between 36 * the logging framework and individual log Handlers. 37 * <p> 38 * When a LogRecord is passed into the logging framework it 39 * logically belongs to the framework and should no longer be 40 * used or updated by the client application. 41 * <p> 42 * Note that if the client application has not specified an 43 * explicit source method name and source class name, then the 44 * LogRecord class will infer them automatically when they are 45 * first accessed (due to a call on getSourceMethodName or 46 * getSourceClassName) by analyzing the call stack. Therefore, 47 * if a logging Handler wants to pass off a LogRecord to another 48 * thread, or to transmit it over RMI, and if it wishes to subsequently 49 * obtain method name or class name information it should call 50 * one of getSourceClassName or getSourceMethodName to force 51 * the values to be filled in. 52 * <p> 53 * <b> Serialization notes:</b> 54 * <ul> 55 * <li>The LogRecord class is serializable. 56 * 57 * <li> Because objects in the parameters array may not be serializable, 58 * during serialization all objects in the parameters array are 59 * written as the corresponding Strings (using Object.toString). 60 * 61 * <li> The ResourceBundle is not transmitted as part of the serialized 62 * form, but the resource bundle name is, and the recipient object's 63 * readObject method will attempt to locate a suitable resource bundle. 64 * 65 * </ul> 66 * 67 * @since 1.4 68 */ 69 70 public class LogRecord implements java.io.Serializable { 71 private static final AtomicLong globalSequenceNumber 72 = new AtomicLong(0); 73 74 /** 75 * The default value of threadID will be the current thread's 76 * thread id, for ease of correlation, unless it is greater than 77 * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep 78 * our promise to keep threadIDs unique by avoiding collisions due 79 * to 32-bit wraparound. Unfortunately, LogRecord.getThreadID() 80 * returns int, while Thread.getId() returns long. 81 */ 82 private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2; 83 84 private static final AtomicInteger nextThreadId 85 = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID); 86 87 private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>(); 88 89 /** 90 * @serial Logging message level 91 */ 92 private Level level; 93 94 /** 95 * @serial Sequence number 96 */ 97 private long sequenceNumber; 98 99 /** 100 * @serial Class that issued logging call 101 */ 102 private String sourceClassName; 103 104 /** 105 * @serial Method that issued logging call 106 */ 107 private String sourceMethodName; 108 109 /** 110 * @serial Non-localized raw message text 111 */ 112 private String message; 113 114 /** 115 * @serial Thread ID for thread that issued logging call. 116 */ 117 private int threadID; 118 119 /** 120 * @serial Event time in milliseconds since 1970 121 */ 122 private long millis; 123 124 /** 125 * @serial The Throwable (if any) associated with log message 126 */ 127 private Throwable thrown; 128 129 /** 130 * @serial Name of the source Logger. 131 */ 132 private String loggerName; 133 134 /** 135 * @serial Resource bundle name to localized log message. 136 */ 137 private String resourceBundleName; 138 139 private transient boolean needToInferCaller; 140 private transient Object parameters[]; 141 private transient ResourceBundle resourceBundle; 142 143 /** 144 * Returns the default value for a new LogRecord's threadID. 145 */ defaultThreadID()146 private int defaultThreadID() { 147 long tid = Thread.currentThread().getId(); 148 if (tid < MIN_SEQUENTIAL_THREAD_ID) { 149 return (int) tid; 150 } else { 151 Integer id = threadIds.get(); 152 if (id == null) { 153 id = nextThreadId.getAndIncrement(); 154 threadIds.set(id); 155 } 156 return id; 157 } 158 } 159 160 /** 161 * Construct a LogRecord with the given level and message values. 162 * <p> 163 * The sequence property will be initialized with a new unique value. 164 * These sequence values are allocated in increasing order within a VM. 165 * <p> 166 * The millis property will be initialized to the current time. 167 * <p> 168 * The thread ID property will be initialized with a unique ID for 169 * the current thread. 170 * <p> 171 * All other properties will be initialized to "null". 172 * 173 * @param level a logging level value 174 * @param msg the raw non-localized logging message (may be null) 175 */ LogRecord(Level level, String msg)176 public LogRecord(Level level, String msg) { 177 // Make sure level isn't null, by calling random method. 178 level.getClass(); 179 this.level = level; 180 message = msg; 181 // Assign a thread ID and a unique sequence number. 182 sequenceNumber = globalSequenceNumber.getAndIncrement(); 183 threadID = defaultThreadID(); 184 millis = System.currentTimeMillis(); 185 needToInferCaller = true; 186 } 187 188 /** 189 * Get the source Logger's name. 190 * 191 * @return source logger name (may be null) 192 */ getLoggerName()193 public String getLoggerName() { 194 return loggerName; 195 } 196 197 /** 198 * Set the source Logger's name. 199 * 200 * @param name the source logger name (may be null) 201 */ setLoggerName(String name)202 public void setLoggerName(String name) { 203 loggerName = name; 204 } 205 206 /** 207 * Get the localization resource bundle 208 * <p> 209 * This is the ResourceBundle that should be used to localize 210 * the message string before formatting it. The result may 211 * be null if the message is not localizable, or if no suitable 212 * ResourceBundle is available. 213 * @return the localization resource bundle 214 */ getResourceBundle()215 public ResourceBundle getResourceBundle() { 216 return resourceBundle; 217 } 218 219 /** 220 * Set the localization resource bundle. 221 * 222 * @param bundle localization bundle (may be null) 223 */ setResourceBundle(ResourceBundle bundle)224 public void setResourceBundle(ResourceBundle bundle) { 225 resourceBundle = bundle; 226 } 227 228 /** 229 * Get the localization resource bundle name 230 * <p> 231 * This is the name for the ResourceBundle that should be 232 * used to localize the message string before formatting it. 233 * The result may be null if the message is not localizable. 234 * @return the localization resource bundle name 235 */ getResourceBundleName()236 public String getResourceBundleName() { 237 return resourceBundleName; 238 } 239 240 /** 241 * Set the localization resource bundle name. 242 * 243 * @param name localization bundle name (may be null) 244 */ setResourceBundleName(String name)245 public void setResourceBundleName(String name) { 246 resourceBundleName = name; 247 } 248 249 /** 250 * Get the logging message level, for example Level.SEVERE. 251 * @return the logging message level 252 */ getLevel()253 public Level getLevel() { 254 return level; 255 } 256 257 /** 258 * Set the logging message level, for example Level.SEVERE. 259 * @param level the logging message level 260 */ setLevel(Level level)261 public void setLevel(Level level) { 262 if (level == null) { 263 throw new NullPointerException(); 264 } 265 this.level = level; 266 } 267 268 /** 269 * Get the sequence number. 270 * <p> 271 * Sequence numbers are normally assigned in the LogRecord 272 * constructor, which assigns unique sequence numbers to 273 * each new LogRecord in increasing order. 274 * @return the sequence number 275 */ getSequenceNumber()276 public long getSequenceNumber() { 277 return sequenceNumber; 278 } 279 280 /** 281 * Set the sequence number. 282 * <p> 283 * Sequence numbers are normally assigned in the LogRecord constructor, 284 * so it should not normally be necessary to use this method. 285 * @param seq the sequence number 286 */ setSequenceNumber(long seq)287 public void setSequenceNumber(long seq) { 288 sequenceNumber = seq; 289 } 290 291 /** 292 * Get the name of the class that (allegedly) issued the logging request. 293 * <p> 294 * Note that this sourceClassName is not verified and may be spoofed. 295 * This information may either have been provided as part of the 296 * logging call, or it may have been inferred automatically by the 297 * logging framework. In the latter case, the information may only 298 * be approximate and may in fact describe an earlier call on the 299 * stack frame. 300 * <p> 301 * May be null if no information could be obtained. 302 * 303 * @return the source class name 304 */ getSourceClassName()305 public String getSourceClassName() { 306 if (needToInferCaller) { 307 inferCaller(); 308 } 309 return sourceClassName; 310 } 311 312 /** 313 * Set the name of the class that (allegedly) issued the logging request. 314 * 315 * @param sourceClassName the source class name (may be null) 316 */ setSourceClassName(String sourceClassName)317 public void setSourceClassName(String sourceClassName) { 318 this.sourceClassName = sourceClassName; 319 needToInferCaller = false; 320 } 321 322 /** 323 * Get the name of the method that (allegedly) issued the logging request. 324 * <p> 325 * Note that this sourceMethodName is not verified and may be spoofed. 326 * This information may either have been provided as part of the 327 * logging call, or it may have been inferred automatically by the 328 * logging framework. In the latter case, the information may only 329 * be approximate and may in fact describe an earlier call on the 330 * stack frame. 331 * <p> 332 * May be null if no information could be obtained. 333 * 334 * @return the source method name 335 */ getSourceMethodName()336 public String getSourceMethodName() { 337 if (needToInferCaller) { 338 inferCaller(); 339 } 340 return sourceMethodName; 341 } 342 343 /** 344 * Set the name of the method that (allegedly) issued the logging request. 345 * 346 * @param sourceMethodName the source method name (may be null) 347 */ setSourceMethodName(String sourceMethodName)348 public void setSourceMethodName(String sourceMethodName) { 349 this.sourceMethodName = sourceMethodName; 350 needToInferCaller = false; 351 } 352 353 /** 354 * Get the "raw" log message, before localization or formatting. 355 * <p> 356 * May be null, which is equivalent to the empty string "". 357 * <p> 358 * This message may be either the final text or a localization key. 359 * <p> 360 * During formatting, if the source logger has a localization 361 * ResourceBundle and if that ResourceBundle has an entry for 362 * this message string, then the message string is replaced 363 * with the localized value. 364 * 365 * @return the raw message string 366 */ getMessage()367 public String getMessage() { 368 return message; 369 } 370 371 /** 372 * Set the "raw" log message, before localization or formatting. 373 * 374 * @param message the raw message string (may be null) 375 */ setMessage(String message)376 public void setMessage(String message) { 377 this.message = message; 378 } 379 380 /** 381 * Get the parameters to the log message. 382 * 383 * @return the log message parameters. May be null if 384 * there are no parameters. 385 */ getParameters()386 public Object[] getParameters() { 387 return parameters; 388 } 389 390 /** 391 * Set the parameters to the log message. 392 * 393 * @param parameters the log message parameters. (may be null) 394 */ setParameters(Object parameters[])395 public void setParameters(Object parameters[]) { 396 this.parameters = parameters; 397 } 398 399 /** 400 * Get an identifier for the thread where the message originated. 401 * <p> 402 * This is a thread identifier within the Java VM and may or 403 * may not map to any operating system ID. 404 * 405 * @return thread ID 406 */ getThreadID()407 public int getThreadID() { 408 return threadID; 409 } 410 411 /** 412 * Set an identifier for the thread where the message originated. 413 * @param threadID the thread ID 414 */ setThreadID(int threadID)415 public void setThreadID(int threadID) { 416 this.threadID = threadID; 417 } 418 419 /** 420 * Get event time in milliseconds since 1970. 421 * 422 * @return event time in millis since 1970 423 */ getMillis()424 public long getMillis() { 425 return millis; 426 } 427 428 /** 429 * Set event time. 430 * 431 * @param millis event time in millis since 1970 432 */ setMillis(long millis)433 public void setMillis(long millis) { 434 this.millis = millis; 435 } 436 437 /** 438 * Get any throwable associated with the log record. 439 * <p> 440 * If the event involved an exception, this will be the 441 * exception object. Otherwise null. 442 * 443 * @return a throwable 444 */ getThrown()445 public Throwable getThrown() { 446 return thrown; 447 } 448 449 /** 450 * Set a throwable associated with the log event. 451 * 452 * @param thrown a throwable (may be null) 453 */ setThrown(Throwable thrown)454 public void setThrown(Throwable thrown) { 455 this.thrown = thrown; 456 } 457 458 private static final long serialVersionUID = 5372048053134512534L; 459 460 /** 461 * @serialData Default fields, followed by a two byte version number 462 * (major byte, followed by minor byte), followed by information on 463 * the log record parameter array. If there is no parameter array, 464 * then -1 is written. If there is a parameter array (possible of zero 465 * length) then the array length is written as an integer, followed 466 * by String values for each parameter. If a parameter is null, then 467 * a null String is written. Otherwise the output of Object.toString() 468 * is written. 469 */ writeObject(ObjectOutputStream out)470 private void writeObject(ObjectOutputStream out) throws IOException { 471 // We have to call defaultWriteObject first. 472 out.defaultWriteObject(); 473 474 // Write our version number. 475 out.writeByte(1); 476 out.writeByte(0); 477 if (parameters == null) { 478 out.writeInt(-1); 479 return; 480 } 481 out.writeInt(parameters.length); 482 // Write string values for the parameters. 483 for (int i = 0; i < parameters.length; i++) { 484 if (parameters[i] == null) { 485 out.writeObject(null); 486 } else { 487 out.writeObject(parameters[i].toString()); 488 } 489 } 490 } 491 readObject(ObjectInputStream in)492 private void readObject(ObjectInputStream in) 493 throws IOException, ClassNotFoundException { 494 // We have to call defaultReadObject first. 495 in.defaultReadObject(); 496 497 // Read version number. 498 byte major = in.readByte(); 499 byte minor = in.readByte(); 500 if (major != 1) { 501 throw new IOException("LogRecord: bad version: " + major + "." + minor); 502 } 503 int len = in.readInt(); 504 if (len < -1) { 505 throw new NegativeArraySizeException(); 506 } else if (len == -1) { 507 parameters = null; 508 } else if (len < 255) { 509 parameters = new Object[len]; 510 for (int i = 0; i < parameters.length; i++) { 511 parameters[i] = in.readObject(); 512 } 513 } else { 514 List<Object> params = new ArrayList<>(Math.min(len, 1024)); 515 for (int i = 0; i < len; i++) { 516 params.add(in.readObject()); 517 } 518 parameters = params.toArray(new Object[params.size()]); 519 } 520 // If necessary, try to regenerate the resource bundle. 521 if (resourceBundleName != null) { 522 try { 523 // use system class loader to ensure the ResourceBundle 524 // instance is a different instance than null loader uses 525 final ResourceBundle bundle = 526 ResourceBundle.getBundle(resourceBundleName, 527 Locale.getDefault(), 528 ClassLoader.getSystemClassLoader()); 529 resourceBundle = bundle; 530 } catch (MissingResourceException ex) { 531 // Android-changed: Fall back to context classloader before giving up. 532 /* 533 // This is not a good place to throw an exception, 534 // so we simply leave the resourceBundle null. 535 resourceBundle = null; 536 */ 537 try { 538 resourceBundle = ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), 539 Thread.currentThread().getContextClassLoader()); 540 } catch (MissingResourceException innerE){ 541 // This is not a good place to throw an exception, 542 // so we simply leave the resourceBundle null. 543 resourceBundle = null; 544 } 545 } 546 } 547 548 needToInferCaller = false; 549 } 550 551 // Private method to infer the caller's class and method names inferCaller()552 private void inferCaller() { 553 needToInferCaller = false; 554 // BEGIN Android-changed: Use VMStack.getThreadStackTrace. 555 /* 556 JavaLangAccess access = SharedSecrets.getJavaLangAccess(); 557 Throwable throwable = new Throwable(); 558 int depth = access.getStackTraceDepth(throwable); 559 */ 560 StackTraceElement[] stack = VMStack.getThreadStackTrace(Thread.currentThread()); 561 int depth = stack.length; 562 // END Android-changed: Use VMStack.getThreadStackTrace. 563 564 boolean lookingForLogger = true; 565 for (int ix = 0; ix < depth; ix++) { 566 // Calling getStackTraceElement directly prevents the VM 567 // from paying the cost of building the entire stack frame. 568 // 569 // Android-changed: Use value from previous getThreadStackTrace call. 570 // StackTraceElement frame = 571 // access.getStackTraceElement(throwable, ix); 572 StackTraceElement frame = stack[ix]; 573 String cname = frame.getClassName(); 574 boolean isLoggerImpl = isLoggerImplFrame(cname); 575 if (lookingForLogger) { 576 // Skip all frames until we have found the first logger frame. 577 if (isLoggerImpl) { 578 lookingForLogger = false; 579 } 580 } else { 581 if (!isLoggerImpl) { 582 // skip reflection call 583 if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { 584 // We've found the relevant frame. 585 setSourceClassName(cname); 586 setSourceMethodName(frame.getMethodName()); 587 return; 588 } 589 } 590 } 591 } 592 // We haven't found a suitable frame, so just punt. This is 593 // OK as we are only committed to making a "best effort" here. 594 } 595 isLoggerImplFrame(String cname)596 private boolean isLoggerImplFrame(String cname) { 597 // the log record could be created for a platform logger 598 return (cname.equals("java.util.logging.Logger") || 599 cname.startsWith("java.util.logging.LoggingProxyImpl") || 600 cname.startsWith("sun.util.logging.")); 601 } 602 } 603