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