1 /* 2 * Copyright (c) 2000, 2021, 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.AtomicLong; 30 import java.io.*; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.time.Clock; 34 import java.util.function.Predicate; 35 import static jdk.internal.logger.SurrogateLogger.isFilteredFrame; 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(); 76 77 /** 78 * Logging message level 79 */ 80 private Level level; 81 82 /** 83 * Sequence number 84 */ 85 private long sequenceNumber; 86 87 /** 88 * Class that issued logging call 89 */ 90 private String sourceClassName; 91 92 /** 93 * Method that issued logging call 94 */ 95 private String sourceMethodName; 96 97 /** 98 * Non-localized raw message text 99 */ 100 private String message; 101 102 /** 103 * Thread ID for thread that issued logging call. 104 */ 105 private int threadID; 106 107 /** 108 * long value of Thread ID for thread that issued logging call. 109 */ 110 private long longThreadID; 111 112 /** 113 * The Throwable (if any) associated with log message 114 */ 115 private Throwable thrown; 116 117 /** 118 * Name of the source Logger. 119 */ 120 private String loggerName; 121 122 /** 123 * Resource bundle name to localized log message. 124 */ 125 private String resourceBundleName; 126 127 /** 128 * Event time. 129 * @since 9 130 */ 131 private Instant instant; 132 133 /** 134 * @serialField level Level Logging message level 135 * @serialField sequenceNumber long Sequence number 136 * @serialField sourceClassName String Class that issued logging call 137 * @serialField sourceMethodName String Method that issued logging call 138 * @serialField message String Non-localized raw message text 139 * @serialField threadID int this is deprecated and is available for backward compatibility. 140 * Values may have been synthesized. If present, {@code longThreadID} represents 141 * the actual thread id. 142 * @serialField longThreadID long Thread ID for thread that issued logging call 143 * @serialField millis long Truncated event time in milliseconds since 1970 144 * - calculated as getInstant().toEpochMilli(). 145 * The event time instant can be reconstructed using 146 * <code>Instant.ofEpochSecond(millis/1000, (millis % 1000) * 1000_000 + nanoAdjustment)</code> 147 * @serialField nanoAdjustment int Nanoseconds adjustment to the millisecond of 148 * event time - calculated as getInstant().getNano() % 1000_000 149 * The event time instant can be reconstructed using 150 * <code>Instant.ofEpochSecond(millis/1000, (millis % 1000) * 1000_000 + nanoAdjustment)</code> 151 * <p> 152 * Since: 9 153 * @serialField thrown Throwable The Throwable (if any) associated with log 154 * message 155 * @serialField loggerName String Name of the source Logger 156 * @serialField resourceBundleName String Resource bundle name to localized 157 * log message 158 */ 159 @Serial 160 private static final ObjectStreamField[] serialPersistentFields = 161 new ObjectStreamField[] { 162 new ObjectStreamField("level", Level.class), 163 new ObjectStreamField("sequenceNumber", long.class), 164 new ObjectStreamField("sourceClassName", String.class), 165 new ObjectStreamField("sourceMethodName", String.class), 166 new ObjectStreamField("message", String.class), 167 new ObjectStreamField("threadID", int.class), 168 new ObjectStreamField("longThreadID", long.class), 169 new ObjectStreamField("millis", long.class), 170 new ObjectStreamField("nanoAdjustment", int.class), 171 new ObjectStreamField("thrown", Throwable.class), 172 new ObjectStreamField("loggerName", String.class), 173 new ObjectStreamField("resourceBundleName", String.class), 174 }; 175 176 private transient boolean needToInferCaller; 177 private transient Object parameters[]; 178 private transient ResourceBundle resourceBundle; 179 180 /** 181 * Synthesizes a pseudo unique integer value from a long {@code id} value. 182 * For backward compatibility with previous releases,the returned integer is 183 * such that for any positive long less than or equals to {@code Integer.MAX_VALUE}, 184 * the returned integer is equal to the original value. 185 * Otherwise - it is synthesized with a best effort hashing algorithm, 186 * and the returned value is negative. 187 * Calling this method multiple times with the same value always yields the same result. 188 * 189 * @return thread id 190 */ 191 shortThreadID(long id)192 private int shortThreadID(long id) { 193 if (id >= 0 && id <= Integer.MAX_VALUE) 194 return (int) id; 195 int hash = Long.hashCode(id); 196 return hash < 0 ? hash : (-1 - hash); 197 } 198 199 /** 200 * Construct a LogRecord with the given level and message values. 201 * <p> 202 * The sequence property will be initialized with a new unique value. 203 * These sequence values are allocated in increasing order within a VM. 204 * <p> 205 * Since JDK 9, the event time is represented by an {@link Instant}. 206 * The instant property will be initialized to the {@linkplain 207 * Instant#now() current instant}, using the best available 208 * {@linkplain Clock#systemUTC() clock} on the system. 209 * <p> 210 * The thread ID property will be initialized with a unique ID for 211 * the current thread. 212 * <p> 213 * All other properties will be initialized to "null". 214 * 215 * @param level a logging level value 216 * @param msg the raw non-localized logging message (may be null) 217 * @see java.time.Clock#systemUTC() 218 */ LogRecord(Level level, String msg)219 public LogRecord(Level level, String msg) { 220 this.level = Objects.requireNonNull(level); 221 message = msg; 222 // Assign a thread ID and a unique sequence number. 223 sequenceNumber = globalSequenceNumber.getAndIncrement(); 224 long id = Thread.currentThread().getId(); 225 // threadID is deprecated and this value is synthesised for backward compatibility 226 threadID = shortThreadID(id); 227 longThreadID = id; 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 */ getLoggerName()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 */ setLoggerName(String name)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 */ getResourceBundle()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 */ setResourceBundle(ResourceBundle bundle)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 */ getResourceBundleName()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 */ setResourceBundleName(String name)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 */ getLevel()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 */ setLevel(Level level)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 */ getSequenceNumber()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 */ setSequenceNumber(long seq)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 */ getSourceClassName()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 */ setSourceClassName(String sourceClassName)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 */ getSourceMethodName()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 */ setSourceMethodName(String sourceMethodName)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 */ getMessage()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 */ setMessage(String message)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 */ getParameters()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 */ setParameters(Object parameters[])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 * @deprecated Values returned by this method may be synthesized, 450 * and may not correspond to the actual {@linkplain Thread#getId() thread id}, 451 * use {@link #getLongThreadID()} instead. 452 * @return thread ID 453 */ 454 @Deprecated(since = "16") getThreadID()455 public int getThreadID() { 456 return threadID; 457 } 458 459 /** 460 * Set an identifier for the thread where the message originated. 461 * @param threadID the thread ID 462 * 463 * @deprecated This method doesn't allow to pass a long {@linkplain Thread#getId() thread id}, 464 * use {@link #setLongThreadID(long)} instead. 465 */ 466 @Deprecated(since = "16") setThreadID(int threadID)467 public void setThreadID(int threadID) { 468 this.threadID = threadID; 469 this.longThreadID = threadID; 470 } 471 472 /** 473 * Get a thread identifier for the thread where message originated 474 * 475 * <p> 476 * This is a thread identifier within the Java VM and may or 477 * may not map to any operating system ID. 478 * 479 * @return thread ID 480 * @since 16 481 */ getLongThreadID()482 public long getLongThreadID() { 483 return longThreadID; 484 } 485 486 /** 487 * Set an identifier for the thread where the message originated. 488 * 489 * @param longThreadID the thread ID 490 * @return this LogRecord 491 * @since 16 492 */ setLongThreadID(long longThreadID)493 public LogRecord setLongThreadID(long longThreadID) { 494 this.threadID = shortThreadID(longThreadID); 495 this.longThreadID = longThreadID; 496 return this; 497 } 498 499 /** 500 * Get truncated event time in milliseconds since 1970. 501 * 502 * @return truncated event time in millis since 1970 503 * 504 * @implSpec This is equivalent to calling 505 * {@link #getInstant() getInstant().toEpochMilli()}. 506 * 507 * @apiNote To get the full nanosecond resolution event time, 508 * use {@link #getInstant()}. 509 * 510 * @see #getInstant() 511 */ getMillis()512 public long getMillis() { 513 return instant.toEpochMilli(); 514 } 515 516 /** 517 * Set event time. 518 * 519 * @param millis event time in millis since 1970. 520 * 521 * @implSpec This is equivalent to calling 522 * {@link #setInstant(java.time.Instant) 523 * setInstant(Instant.ofEpochMilli(millis))}. 524 * 525 * @deprecated LogRecord maintains timestamps with nanosecond resolution, 526 * using {@link Instant} values. For this reason, 527 * {@link #setInstant(java.time.Instant) setInstant()} 528 * should be used in preference to {@code setMillis()}. 529 * 530 * @see #setInstant(java.time.Instant) 531 */ 532 @Deprecated setMillis(long millis)533 public void setMillis(long millis) { 534 this.instant = Instant.ofEpochMilli(millis); 535 } 536 537 /** 538 * Gets the instant that the event occurred. 539 * 540 * @return the instant that the event occurred. 541 * 542 * @since 9 543 */ getInstant()544 public Instant getInstant() { 545 return instant; 546 } 547 548 /** 549 * Sets the instant that the event occurred. 550 * <p> 551 * If the given {@code instant} represents a point on the time-line too 552 * far in the future or past to fit in a {@code long} milliseconds and 553 * nanoseconds adjustment, then an {@code ArithmeticException} will be 554 * thrown. 555 * 556 * @param instant the instant that the event occurred. 557 * 558 * @throws NullPointerException if {@code instant} is null. 559 * @throws ArithmeticException if numeric overflow would occur while 560 * calling {@link Instant#toEpochMilli() instant.toEpochMilli()}. 561 * 562 * @since 9 563 */ setInstant(Instant instant)564 public void setInstant(Instant instant) { 565 instant.toEpochMilli(); 566 this.instant = instant; 567 } 568 569 /** 570 * Get any throwable associated with the log record. 571 * <p> 572 * If the event involved an exception, this will be the 573 * exception object. Otherwise null. 574 * 575 * @return a throwable 576 */ getThrown()577 public Throwable getThrown() { 578 return thrown; 579 } 580 581 /** 582 * Set a throwable associated with the log event. 583 * 584 * @param thrown a throwable (may be null) 585 */ setThrown(Throwable thrown)586 public void setThrown(Throwable thrown) { 587 this.thrown = thrown; 588 } 589 590 @Serial 591 private static final long serialVersionUID = 5372048053134512534L; 592 593 /** 594 * @serialData Serialized fields, followed by a two byte version number 595 * (major byte, followed by minor byte), followed by information on 596 * the log record parameter array. If there is no parameter array, 597 * then -1 is written. If there is a parameter array (possible of zero 598 * length) then the array length is written as an integer, followed 599 * by String values for each parameter. If a parameter is null, then 600 * a null String is written. Otherwise the output of Object.toString() 601 * is written. 602 * 603 * @param out the {@code ObjectOutputStream} to write to 604 * 605 * @throws IOException if I/O errors occur 606 */ 607 @Serial writeObject(ObjectOutputStream out)608 private void writeObject(ObjectOutputStream out) throws IOException { 609 // We have to write serialized fields first. 610 ObjectOutputStream.PutField pf = out.putFields(); 611 pf.put("level", level); 612 pf.put("sequenceNumber", sequenceNumber); 613 pf.put("sourceClassName", sourceClassName); 614 pf.put("sourceMethodName", sourceMethodName); 615 pf.put("message", message); 616 pf.put("threadID", threadID); 617 pf.put("longThreadID", longThreadID); 618 pf.put("millis", instant.toEpochMilli()); 619 pf.put("nanoAdjustment", instant.getNano() % 1000_000); 620 pf.put("thrown", thrown); 621 pf.put("loggerName", loggerName); 622 pf.put("resourceBundleName", resourceBundleName); 623 out.writeFields(); 624 625 // Write our version number. 626 out.writeByte(1); 627 out.writeByte(0); 628 if (parameters == null) { 629 out.writeInt(-1); 630 return; 631 } 632 out.writeInt(parameters.length); 633 // Write string values for the parameters. 634 for (Object parameter : parameters) { 635 out.writeObject(Objects.toString(parameter, null)); 636 } 637 } 638 639 /** 640 * Initializes the LogRecord from deserialized data. 641 * <ul> 642 * <li>If {@code longThreadID} is present in the serial form, its value 643 * takes precedence over {@code threadID} and a value for {@code threadID} 644 * is synthesized from it, such that for {@code longThreadID} values between 645 * {@code 0} and {@code Integer.MAX_VALUE} inclusive, {@code longThreadID} 646 * and {@code threadID} will have the same value. For values outside of this 647 * range a negative synthesized value will be deterministically derived 648 * from {@code longThreadID}. 649 * <li>Otherwise, when only {@code threadID} is 650 * present, {@code longThreadID} is initialized with the value of 651 * {@code threadID} which may be anything between {@code Integer.MIN_VALUE} 652 * and {Integer.MAX_VALUE}. 653 * </ul> 654 * 655 * See {@code writeObject} for a description of the serial form. 656 * 657 * @param in the {@code ObjectInputStream} to read from 658 * 659 * @throws ClassNotFoundException if the class of a serialized object 660 * could not be found. 661 * @throws IOException if an I/O error occurs. 662 */ 663 @Serial readObject(ObjectInputStream in)664 private void readObject(ObjectInputStream in) 665 throws IOException, ClassNotFoundException { 666 // We have to read serialized fields first. 667 ObjectInputStream.GetField gf = in.readFields(); 668 level = (Level) gf.get("level", null); 669 sequenceNumber = gf.get("sequenceNumber", 0L); 670 sourceClassName = (String) gf.get("sourceClassName", null); 671 sourceMethodName = (String) gf.get("sourceMethodName", null); 672 message = (String) gf.get("message", null); 673 // If longthreadID is not present, it will be initialised with threadID value 674 // If longthreadID is present, threadID might have a synthesized value 675 int threadID = gf.get("threadID", 0); 676 long longThreadID = gf.get("longThreadID", (long)threadID); 677 if (threadID != longThreadID) 678 threadID = shortThreadID(longThreadID); 679 this.threadID = threadID; 680 this.longThreadID = longThreadID; 681 long millis = gf.get("millis", 0L); 682 int nanoOfMilli = gf.get("nanoAdjustment", 0); 683 instant = Instant.ofEpochSecond( 684 millis / 1000L, (millis % 1000L) * 1000_000L + nanoOfMilli); 685 thrown = (Throwable) gf.get("thrown", null); 686 loggerName = (String) gf.get("loggerName", null); 687 resourceBundleName = (String) gf.get("resourceBundleName", null); 688 689 // Read version number. 690 byte major = in.readByte(); 691 byte minor = in.readByte(); 692 if (major != 1) { 693 throw new IOException("LogRecord: bad version: " + major + "." + minor); 694 } 695 int len = in.readInt(); 696 if (len < -1) { 697 throw new NegativeArraySizeException(); 698 } else if (len == -1) { 699 parameters = null; 700 } else if (len < 255) { 701 parameters = new Object[len]; 702 for (int i = 0; i < parameters.length; i++) { 703 parameters[i] = in.readObject(); 704 } 705 } else { 706 List<Object> params = new ArrayList<>(Math.min(len, 1024)); 707 for (int i = 0; i < len; i++) { 708 params.add(in.readObject()); 709 } 710 parameters = params.toArray(new Object[params.size()]); 711 } 712 // If necessary, try to regenerate the resource bundle. 713 if (resourceBundleName != null) { 714 try { 715 // use system class loader to ensure the ResourceBundle 716 // instance is a different instance than null loader uses 717 final ResourceBundle bundle = 718 ResourceBundle.getBundle(resourceBundleName, 719 Locale.getDefault(), 720 ClassLoader.getSystemClassLoader()); 721 resourceBundle = bundle; 722 } catch (MissingResourceException ex) { 723 // This is not a good place to throw an exception, 724 // so we simply leave the resourceBundle null. 725 resourceBundle = null; 726 } 727 } 728 729 needToInferCaller = false; 730 } 731 732 // Private method to infer the caller's class and method names 733 // 734 // Note: 735 // For testing purposes - it is possible to customize the process 736 // by which LogRecord will infer the source class name and source method name 737 // when analyzing the call stack. 738 // <p> 739 // The system property {@code jdk.logger.packages} can define a comma separated 740 // list of strings corresponding to additional package name prefixes that 741 // should be ignored when trying to infer the source caller class name. 742 // Those stack frames whose {@linkplain StackTraceElement#getClassName() 743 // declaring class name} start with one such prefix will be ignored. 744 // <p> 745 // This is primarily useful when providing utility logging classes wrapping 746 // a logger instance, as it makes it possible to instruct LogRecord to skip 747 // those utility frames when inferring the caller source class name. 748 // <p> 749 // The {@code jdk.logger.packages} system property is consulted only once. 750 // <p> 751 // This property is not standard, implementation specific, and yet 752 // undocumented (and thus subject to changes without notice). 753 // inferCaller()754 private void inferCaller() { 755 needToInferCaller = false; 756 // Skip all frames until we have found the first logger frame. 757 Optional<StackWalker.StackFrame> frame = new CallerFinder().get(); 758 frame.ifPresent(f -> { 759 setSourceClassName(f.getClassName()); 760 setSourceMethodName(f.getMethodName()); 761 }); 762 763 // We haven't found a suitable frame, so just punt. This is 764 // OK as we are only committed to making a "best effort" here. 765 } 766 767 /* 768 * CallerFinder is a stateful predicate. 769 */ 770 @SuppressWarnings("removal") 771 static final class CallerFinder implements Predicate<StackWalker.StackFrame> { 772 private static final StackWalker WALKER; 773 static { 774 final PrivilegedAction<StackWalker> action = 775 () -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 776 WALKER = AccessController.doPrivileged(action); 777 } 778 779 /** 780 * Returns StackFrame of the caller's frame. 781 * @return StackFrame of the caller's frame. 782 */ get()783 Optional<StackWalker.StackFrame> get() { 784 return WALKER.walk((s) -> s.filter(this).findFirst()); 785 } 786 787 private boolean lookingForLogger = true; 788 /** 789 * Returns true if we have found the caller's frame, false if the frame 790 * must be skipped. 791 * 792 * @param t The frame info. 793 * @return true if we have found the caller's frame, false if the frame 794 * must be skipped. 795 */ 796 @Override test(StackWalker.StackFrame t)797 public boolean test(StackWalker.StackFrame t) { 798 final String cname = t.getClassName(); 799 // We should skip all frames until we have found the logger, 800 // because these frames could be frames introduced by e.g. custom 801 // sub classes of Handler. 802 if (lookingForLogger) { 803 // the log record could be created for a platform logger 804 lookingForLogger = !isLoggerImplFrame(cname); 805 return false; 806 } 807 // Continue walking until we've found the relevant calling frame. 808 // Skips logging/logger infrastructure. 809 return !isFilteredFrame(t); 810 } 811 isLoggerImplFrame(String cname)812 private boolean isLoggerImplFrame(String cname) { 813 return (cname.equals("java.util.logging.Logger") || 814 cname.startsWith("sun.util.logging.PlatformLogger")); 815 } 816 } 817 } 818