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