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.util.*;
  28 import java.util.concurrent.atomic.AtomicInteger;
  29 import java.util.concurrent.atomic.AtomicLong;
  30 import java.io.*;
  31 
  32 import sun.misc.JavaLangAccess;
  33 import sun.misc.SharedSecrets;
  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      * @serial Logging message level
  92      */
  93     private Level level;
  94 
  95     /**
  96      * @serial Sequence number
  97      */
  98     private long sequenceNumber;
  99 
 100     /**
 101      * @serial Class that issued logging call
 102      */
 103     private String sourceClassName;
 104 
 105     /**
 106      * @serial Method that issued logging call
 107      */
 108     private String sourceMethodName;
 109 
 110     /**
 111      * @serial Non-localized raw message text
 112      */
 113     private String message;
 114 
 115     /**
 116      * @serial Thread ID for thread that issued logging call.
 117      */
 118     private int threadID;
 119 
 120     /**
 121      * @serial Event time in milliseconds since 1970
 122      */
 123     private long millis;
 124 
 125     /**
 126      * @serial The Throwable (if any) associated with log message
 127      */
 128     private Throwable thrown;
 129 
 130     /**
 131      * @serial Name of the source Logger.
 132      */
 133     private String loggerName;
 134 
 135     /**
 136      * @serial Resource bundle name to localized log message.
 137      */
 138     private String resourceBundleName;
 139 
 140     private transient boolean needToInferCaller;
 141     private transient Object parameters[];
 142     private transient ResourceBundle resourceBundle;
 143 
 144     /**
 145      * Returns the default value for a new LogRecord's threadID.
 146      */
 147     private int defaultThreadID() {
 148         long tid = Thread.currentThread().getId();
 149         if (tid < MIN_SEQUENTIAL_THREAD_ID) {
 150             return (int) tid;
 151         } else {
 152             Integer id = threadIds.get();
 153             if (id == null) {
 154                 id = nextThreadId.getAndIncrement();
 155                 threadIds.set(id);
 156             }
 157             return id;
 158         }
 159     }
 160 
 161     /**
 162      * Construct a LogRecord with the given level and message values.
 163      * <p>
 164      * The sequence property will be initialized with a new unique value.
 165      * These sequence values are allocated in increasing order within a VM.
 166      * <p>
 167      * The millis property will be initialized to the current time.
 168      * <p>
 169      * The thread ID property will be initialized with a unique ID for
 170      * the current thread.
 171      * <p>
 172      * All other properties will be initialized to "null".
 173      *
 174      * @param level  a logging level value
 175      * @param msg  the raw non-localized logging message (may be null)
 176      */
 177     public LogRecord(Level level, String msg) {
 178         // Make sure level isn't null, by calling random method.
 179         level.getClass();
 180         this.level = level;
 181         message = msg;
 182         // Assign a thread ID and a unique sequence number.
 183         sequenceNumber = globalSequenceNumber.getAndIncrement();
 184         threadID = defaultThreadID();
 185         millis = System.currentTimeMillis();
 186         needToInferCaller = true;
 187    }
 188 
 189     /**
 190      * Get the source Logger's name.
 191      *
 192      * @return source logger name (may be null)
 193      */
 194     public String getLoggerName() {
 195         return loggerName;
 196     }
 197 
 198     /**
 199      * Set the source Logger's name.
 200      *
 201      * @param name   the source logger name (may be null)
 202      */
 203     public void setLoggerName(String name) {
 204         loggerName = name;
 205     }
 206 
 207     /**
 208      * Get the localization resource bundle
 209      * <p>
 210      * This is the ResourceBundle that should be used to localize
 211      * the message string before formatting it.  The result may
 212      * be null if the message is not localizable, or if no suitable
 213      * ResourceBundle is available.
 214      * @return the localization resource bundle
 215      */
 216     public ResourceBundle getResourceBundle() {
 217         return resourceBundle;
 218     }
 219 
 220     /**
 221      * Set the localization resource bundle.
 222      *
 223      * @param bundle  localization bundle (may be null)
 224      */
 225     public void setResourceBundle(ResourceBundle bundle) {
 226         resourceBundle = bundle;
 227     }
 228 
 229     /**
 230      * Get the localization resource bundle name
 231      * <p>
 232      * This is the name for the ResourceBundle that should be
 233      * used to localize the message string before formatting it.
 234      * The result may be null if the message is not localizable.
 235      * @return the localization resource bundle name
 236      */
 237     public String getResourceBundleName() {
 238         return resourceBundleName;
 239     }
 240 
 241     /**
 242      * Set the localization resource bundle name.
 243      *
 244      * @param name  localization bundle name (may be null)
 245      */
 246     public void setResourceBundleName(String name) {
 247         resourceBundleName = name;
 248     }
 249 
 250     /**
 251      * Get the logging message level, for example Level.SEVERE.
 252      * @return the logging message level
 253      */
 254     public Level getLevel() {
 255         return level;
 256     }
 257 
 258     /**
 259      * Set the logging message level, for example Level.SEVERE.
 260      * @param level the logging message level
 261      */
 262     public void setLevel(Level level) {
 263         if (level == null) {
 264             throw new NullPointerException();
 265         }
 266         this.level = level;
 267     }
 268 
 269     /**
 270      * Get the sequence number.
 271      * <p>
 272      * Sequence numbers are normally assigned in the LogRecord
 273      * constructor, which assigns unique sequence numbers to
 274      * each new LogRecord in increasing order.
 275      * @return the sequence number
 276      */
 277     public long getSequenceNumber() {
 278         return sequenceNumber;
 279     }
 280 
 281     /**
 282      * Set the sequence number.
 283      * <p>
 284      * Sequence numbers are normally assigned in the LogRecord constructor,
 285      * so it should not normally be necessary to use this method.
 286      * @param seq the sequence number
 287      */
 288     public void setSequenceNumber(long seq) {
 289         sequenceNumber = seq;
 290     }
 291 
 292     /**
 293      * Get the  name of the class that (allegedly) issued the logging request.
 294      * <p>
 295      * Note that this sourceClassName is not verified and may be spoofed.
 296      * This information may either have been provided as part of the
 297      * logging call, or it may have been inferred automatically by the
 298      * logging framework.  In the latter case, the information may only
 299      * be approximate and may in fact describe an earlier call on the
 300      * stack frame.
 301      * <p>
 302      * May be null if no information could be obtained.
 303      *
 304      * @return the source class name
 305      */
 306     public String getSourceClassName() {
 307         if (needToInferCaller) {
 308             inferCaller();
 309         }
 310         return sourceClassName;
 311     }
 312 
 313     /**
 314      * Set the name of the class that (allegedly) issued the logging request.
 315      *
 316      * @param sourceClassName the source class name (may be null)
 317      */
 318     public void setSourceClassName(String sourceClassName) {
 319         this.sourceClassName = sourceClassName;
 320         needToInferCaller = false;
 321     }
 322 
 323     /**
 324      * Get the  name of the method that (allegedly) issued the logging request.
 325      * <p>
 326      * Note that this sourceMethodName is not verified and may be spoofed.
 327      * This information may either have been provided as part of the
 328      * logging call, or it may have been inferred automatically by the
 329      * logging framework.  In the latter case, the information may only
 330      * be approximate and may in fact describe an earlier call on the
 331      * stack frame.
 332      * <p>
 333      * May be null if no information could be obtained.
 334      *
 335      * @return the source method name
 336      */
 337     public String getSourceMethodName() {
 338         if (needToInferCaller) {
 339             inferCaller();
 340         }
 341         return sourceMethodName;
 342     }
 343 
 344     /**
 345      * Set the name of the method that (allegedly) issued the logging request.
 346      *
 347      * @param sourceMethodName the source method name (may be null)
 348      */
 349     public void setSourceMethodName(String sourceMethodName) {
 350         this.sourceMethodName = sourceMethodName;
 351         needToInferCaller = false;
 352     }
 353 
 354     /**
 355      * Get the "raw" log message, before localization or formatting.
 356      * <p>
 357      * May be null, which is equivalent to the empty string "".
 358      * <p>
 359      * This message may be either the final text or a localization key.
 360      * <p>
 361      * During formatting, if the source logger has a localization
 362      * ResourceBundle and if that ResourceBundle has an entry for
 363      * this message string, then the message string is replaced
 364      * with the localized value.
 365      *
 366      * @return the raw message string
 367      */
 368     public String getMessage() {
 369         return message;
 370     }
 371 
 372     /**
 373      * Set the "raw" log message, before localization or formatting.
 374      *
 375      * @param message the raw message string (may be null)
 376      */
 377     public void setMessage(String message) {
 378         this.message = message;
 379     }
 380 
 381     /**
 382      * Get the parameters to the log message.
 383      *
 384      * @return the log message parameters.  May be null if
 385      *                  there are no parameters.
 386      */
 387     public Object[] getParameters() {
 388         return parameters;
 389     }
 390 
 391     /**
 392      * Set the parameters to the log message.
 393      *
 394      * @param parameters the log message parameters. (may be null)
 395      */
 396     public void setParameters(Object parameters[]) {
 397         this.parameters = parameters;
 398     }
 399 
 400     /**
 401      * Get an identifier for the thread where the message originated.
 402      * <p>
 403      * This is a thread identifier within the Java VM and may or
 404      * may not map to any operating system ID.
 405      *
 406      * @return thread ID
 407      */
 408     public int getThreadID() {
 409         return threadID;
 410     }
 411 
 412     /**
 413      * Set an identifier for the thread where the message originated.
 414      * @param threadID  the thread ID
 415      */
 416     public void setThreadID(int threadID) {
 417         this.threadID = threadID;
 418     }
 419 
 420     /**
 421      * Get event time in milliseconds since 1970.
 422      *
 423      * @return event time in millis since 1970
 424      */
 425     public long getMillis() {
 426         return millis;
 427     }
 428 
 429     /**
 430      * Set event time.
 431      *
 432      * @param millis event time in millis since 1970
 433      */
 434     public void setMillis(long millis) {
 435         this.millis = millis;
 436     }
 437 
 438     /**
 439      * Get any throwable associated with the log record.
 440      * <p>
 441      * If the event involved an exception, this will be the
 442      * exception object. Otherwise null.
 443      *
 444      * @return a throwable
 445      */
 446     public Throwable getThrown() {
 447         return thrown;
 448     }
 449 
 450     /**
 451      * Set a throwable associated with the log event.
 452      *
 453      * @param thrown  a throwable (may be null)
 454      */
 455     public void setThrown(Throwable thrown) {
 456         this.thrown = thrown;
 457     }
 458 
 459     private static final long serialVersionUID = 5372048053134512534L;
 460 
 461     /**
 462      * @serialData Default fields, followed by a two byte version number
 463      * (major byte, followed by minor byte), followed by information on
 464      * the log record parameter array.  If there is no parameter array,
 465      * then -1 is written.  If there is a parameter array (possible of zero
 466      * length) then the array length is written as an integer, followed
 467      * by String values for each parameter.  If a parameter is null, then
 468      * a null String is written.  Otherwise the output of Object.toString()
 469      * is written.
 470      */
 471     private void writeObject(ObjectOutputStream out) throws IOException {
 472         // We have to call defaultWriteObject first.
 473         out.defaultWriteObject();
 474 
 475         // Write our version number.
 476         out.writeByte(1);
 477         out.writeByte(0);
 478         if (parameters == null) {
 479             out.writeInt(-1);
 480             return;
 481         }
 482         out.writeInt(parameters.length);
 483         // Write string values for the parameters.
 484         for (int i = 0; i < parameters.length; i++) {
 485             if (parameters[i] == null) {
 486                 out.writeObject(null);
 487             } else {
 488                 out.writeObject(parameters[i].toString());
 489             }
 490         }
 491     }
 492 
 493     private void readObject(ObjectInputStream in)
 494                         throws IOException, ClassNotFoundException {
 495         // We have to call defaultReadObject first.
 496         in.defaultReadObject();
 497 
 498         // Read version number.
 499         byte major = in.readByte();
 500         byte minor = in.readByte();
 501         if (major != 1) {
 502             throw new IOException("LogRecord: bad version: " + major + "." + minor);
 503         }
 504         int len = in.readInt();
 505         if (len == -1) {
 506             parameters = null;
 507         } else {
 508             parameters = new Object[len];
 509             for (int i = 0; i < parameters.length; i++) {
 510                 parameters[i] = in.readObject();
 511             }
 512         }
 513         // If necessary, try to regenerate the resource bundle.
 514         if (resourceBundleName != null) {
 515             try {
 516                 resourceBundle = ResourceBundle.getBundle(resourceBundleName);
 517             } catch (MissingResourceException ex) {
 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         needToInferCaller = false;
 525     }
 526 
 527     // Private method to infer the caller's class and method names
 528     private void inferCaller() {
 529         needToInferCaller = false;
 530         JavaLangAccess access = SharedSecrets.getJavaLangAccess();
 531         Throwable throwable = new Throwable();
 532         int depth = access.getStackTraceDepth(throwable);
 533 
 534         boolean lookingForLogger = true;
 535         for (int ix = 0; ix < depth; ix++) {
 536             // Calling getStackTraceElement directly prevents the VM
 537             // from paying the cost of building the entire stack frame.
 538             StackTraceElement frame =
 539                 access.getStackTraceElement(throwable, ix);
 540             String cname = frame.getClassName();
 541             boolean isLoggerImpl = isLoggerImplFrame(cname);
 542             if (lookingForLogger) {
 543                 // Skip all frames until we have found the first logger frame.
 544                 if (isLoggerImpl) {
 545                     lookingForLogger = false;
 546                 }
 547             } else {
 548                 if (!isLoggerImpl) {
 549                     // skip reflection call
 550                     if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
 551                        // We've found the relevant frame.
 552                        setSourceClassName(cname);
 553                        setSourceMethodName(frame.getMethodName());
 554                        return;
 555                     }
 556                 }
 557             }
 558         }
 559         // We haven't found a suitable frame, so just punt.  This is
 560         // OK as we are only committed to making a "best effort" here.
 561     }
 562 
 563     private boolean isLoggerImplFrame(String cname) {
 564         // the log record could be created for a platform logger
 565         return (cname.equals("java.util.logging.Logger") ||
 566                 cname.startsWith("java.util.logging.LoggingProxyImpl") ||
 567                 cname.startsWith("sun.util.logging."));
 568     }
 569 }