1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2000, 2010, 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 27package java.util.logging; 28import dalvik.system.VMStack; 29import java.util.*; 30import java.util.concurrent.atomic.AtomicInteger; 31import java.util.concurrent.atomic.AtomicLong; 32import 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 70public 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 */ 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 */ 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 */ 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 */ 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 */ 214 public ResourceBundle getResourceBundle() { 215 return resourceBundle; 216 } 217 218 /** 219 * Set the localization resource bundle. 220 * 221 * @param bundle localization bundle (may be null) 222 */ 223 public void setResourceBundle(ResourceBundle bundle) { 224 resourceBundle = bundle; 225 } 226 227 /** 228 * Get the localization resource bundle name 229 * <p> 230 * This is the name for the ResourceBundle that should be 231 * used to localize the message string before formatting it. 232 * The result may be null if the message is not localizable. 233 */ 234 public String getResourceBundleName() { 235 return resourceBundleName; 236 } 237 238 /** 239 * Set the localization resource bundle name. 240 * 241 * @param name localization bundle name (may be null) 242 */ 243 public void setResourceBundleName(String name) { 244 resourceBundleName = name; 245 } 246 247 /** 248 * Get the logging message level, for example Level.SEVERE. 249 * @return the logging message level 250 */ 251 public Level getLevel() { 252 return level; 253 } 254 255 /** 256 * Set the logging message level, for example Level.SEVERE. 257 * @param level the logging message level 258 */ 259 public void setLevel(Level level) { 260 if (level == null) { 261 throw new NullPointerException(); 262 } 263 this.level = level; 264 } 265 266 /** 267 * Get the sequence number. 268 * <p> 269 * Sequence numbers are normally assigned in the LogRecord 270 * constructor, which assigns unique sequence numbers to 271 * each new LogRecord in increasing order. 272 * @return the sequence number 273 */ 274 public long getSequenceNumber() { 275 return sequenceNumber; 276 } 277 278 /** 279 * Set the sequence number. 280 * <p> 281 * Sequence numbers are normally assigned in the LogRecord constructor, 282 * so it should not normally be necessary to use this method. 283 */ 284 public void setSequenceNumber(long seq) { 285 sequenceNumber = seq; 286 } 287 288 /** 289 * Get the name of the class that (allegedly) issued the logging request. 290 * <p> 291 * Note that this sourceClassName is not verified and may be spoofed. 292 * This information may either have been provided as part of the 293 * logging call, or it may have been inferred automatically by the 294 * logging framework. In the latter case, the information may only 295 * be approximate and may in fact describe an earlier call on the 296 * stack frame. 297 * <p> 298 * May be null if no information could be obtained. 299 * 300 * @return the source class name 301 */ 302 public String getSourceClassName() { 303 if (needToInferCaller) { 304 inferCaller(); 305 } 306 return sourceClassName; 307 } 308 309 /** 310 * Set the name of the class that (allegedly) issued the logging request. 311 * 312 * @param sourceClassName the source class name (may be null) 313 */ 314 public void setSourceClassName(String sourceClassName) { 315 this.sourceClassName = sourceClassName; 316 needToInferCaller = false; 317 } 318 319 /** 320 * Get the name of the method that (allegedly) issued the logging request. 321 * <p> 322 * Note that this sourceMethodName is not verified and may be spoofed. 323 * This information may either have been provided as part of the 324 * logging call, or it may have been inferred automatically by the 325 * logging framework. In the latter case, the information may only 326 * be approximate and may in fact describe an earlier call on the 327 * stack frame. 328 * <p> 329 * May be null if no information could be obtained. 330 * 331 * @return the source method name 332 */ 333 public String getSourceMethodName() { 334 if (needToInferCaller) { 335 inferCaller(); 336 } 337 return sourceMethodName; 338 } 339 340 /** 341 * Set the name of the method that (allegedly) issued the logging request. 342 * 343 * @param sourceMethodName the source method name (may be null) 344 */ 345 public void setSourceMethodName(String sourceMethodName) { 346 this.sourceMethodName = sourceMethodName; 347 needToInferCaller = false; 348 } 349 350 /** 351 * Get the "raw" log message, before localization or formatting. 352 * <p> 353 * May be null, which is equivalent to the empty string "". 354 * <p> 355 * This message may be either the final text or a localization key. 356 * <p> 357 * During formatting, if the source logger has a localization 358 * ResourceBundle and if that ResourceBundle has an entry for 359 * this message string, then the message string is replaced 360 * with the localized value. 361 * 362 * @return the raw message string 363 */ 364 public String getMessage() { 365 return message; 366 } 367 368 /** 369 * Set the "raw" log message, before localization or formatting. 370 * 371 * @param message the raw message string (may be null) 372 */ 373 public void setMessage(String message) { 374 this.message = message; 375 } 376 377 /** 378 * Get the parameters to the log message. 379 * 380 * @return the log message parameters. May be null if 381 * there are no parameters. 382 */ 383 public Object[] getParameters() { 384 return parameters; 385 } 386 387 /** 388 * Set the parameters to the log message. 389 * 390 * @param parameters the log message parameters. (may be null) 391 */ 392 public void setParameters(Object parameters[]) { 393 this.parameters = parameters; 394 } 395 396 /** 397 * Get an identifier for the thread where the message originated. 398 * <p> 399 * This is a thread identifier within the Java VM and may or 400 * may not map to any operating system ID. 401 * 402 * @return thread ID 403 */ 404 public int getThreadID() { 405 return threadID; 406 } 407 408 /** 409 * Set an identifier for the thread where the message originated. 410 * @param threadID the thread ID 411 */ 412 public void setThreadID(int threadID) { 413 this.threadID = threadID; 414 } 415 416 /** 417 * Get event time in milliseconds since 1970. 418 * 419 * @return event time in millis since 1970 420 */ 421 public long getMillis() { 422 return millis; 423 } 424 425 /** 426 * Set event time. 427 * 428 * @param millis event time in millis since 1970 429 */ 430 public void setMillis(long millis) { 431 this.millis = millis; 432 } 433 434 /** 435 * Get any throwable associated with the log record. 436 * <p> 437 * If the event involved an exception, this will be the 438 * exception object. Otherwise null. 439 * 440 * @return a throwable 441 */ 442 public Throwable getThrown() { 443 return thrown; 444 } 445 446 /** 447 * Set a throwable associated with the log event. 448 * 449 * @param thrown a throwable (may be null) 450 */ 451 public void setThrown(Throwable thrown) { 452 this.thrown = thrown; 453 } 454 455 private static final long serialVersionUID = 5372048053134512534L; 456 457 /** 458 * @serialData Default fields, followed by a two byte version number 459 * (major byte, followed by minor byte), followed by information on 460 * the log record parameter array. If there is no parameter array, 461 * then -1 is written. If there is a parameter array (possible of zero 462 * length) then the array length is written as an integer, followed 463 * by String values for each parameter. If a parameter is null, then 464 * a null String is written. Otherwise the output of Object.toString() 465 * is written. 466 */ 467 private void writeObject(ObjectOutputStream out) throws IOException { 468 // We have to call defaultWriteObject first. 469 out.defaultWriteObject(); 470 471 // Write our version number. 472 out.writeByte(1); 473 out.writeByte(0); 474 if (parameters == null) { 475 out.writeInt(-1); 476 return; 477 } 478 out.writeInt(parameters.length); 479 // Write string values for the parameters. 480 for (int i = 0; i < parameters.length; i++) { 481 if (parameters[i] == null) { 482 out.writeObject(null); 483 } else { 484 out.writeObject(parameters[i].toString()); 485 } 486 } 487 } 488 489 private void readObject(ObjectInputStream in) 490 throws IOException, ClassNotFoundException { 491 // We have to call defaultReadObject first. 492 in.defaultReadObject(); 493 494 // Read version number. 495 byte major = in.readByte(); 496 byte minor = in.readByte(); 497 if (major != 1) { 498 throw new IOException("LogRecord: bad version: " + major + "." + minor); 499 } 500 int len = in.readInt(); 501 if (len == -1) { 502 parameters = null; 503 } else { 504 parameters = new Object[len]; 505 for (int i = 0; i < parameters.length; i++) { 506 parameters[i] = in.readObject(); 507 } 508 } 509 // If necessary, try to regenerate the resource bundle. 510 if (resourceBundleName != null) { 511 try { 512 resourceBundle = ResourceBundle.getBundle(resourceBundleName); 513 } catch (MissingResourceException ex) { 514 try { 515 resourceBundle = ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), 516 Thread.currentThread().getContextClassLoader()); 517 } catch (MissingResourceException innerE){ 518 // This is not a good place to throw an exception, 519 // so we simply leave the resourceBundle null. 520 resourceBundle = null; 521 } 522 } 523 } 524 525 needToInferCaller = false; 526 } 527 528 // Private method to infer the caller's class and method names 529 private void inferCaller() { 530 needToInferCaller = false; 531 // Android-changed: Use VMStack.getThreadStackTrace. 532 StackTraceElement[] stack = VMStack.getThreadStackTrace(Thread.currentThread()); 533 int depth = stack.length; 534 535 boolean lookingForLogger = true; 536 for (int ix = 0; ix < depth; ix++) { 537 // Calling getStackTraceElement directly prevents the VM 538 // from paying the cost of building the entire stack frame. 539 // 540 // Android-changed: Use value from getThreadStackTrace. 541 StackTraceElement frame = stack[ix]; 542 String cname = frame.getClassName(); 543 boolean isLoggerImpl = isLoggerImplFrame(cname); 544 if (lookingForLogger) { 545 // Skip all frames until we have found the first logger frame. 546 if (isLoggerImpl) { 547 lookingForLogger = false; 548 } 549 } else { 550 if (!isLoggerImpl) { 551 // skip reflection call 552 if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { 553 // We've found the relevant frame. 554 setSourceClassName(cname); 555 setSourceMethodName(frame.getMethodName()); 556 return; 557 } 558 } 559 } 560 } 561 // We haven't found a suitable frame, so just punt. This is 562 // OK as we are only committed to making a "best effort" here. 563 } 564 565 private boolean isLoggerImplFrame(String cname) { 566 // the log record could be created for a platform logger 567 return (cname.equals("java.util.logging.Logger") || 568 cname.startsWith("java.util.logging.LoggingProxyImpl") || 569 cname.startsWith("sun.util.logging.")); 570 } 571} 572