2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
26 package java.util.logging;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.concurrent.atomic.AtomicLong;
32 import sun.misc.JavaLangAccess;
33 import sun.misc.SharedSecrets;
36 * LogRecord objects are used to pass logging requests between
37 * the logging framework and individual log Handlers.
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.
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.
54 * <b> Serialization notes:</b>
56 * <li>The LogRecord class is serializable.
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).
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.
71 public class LogRecord implements java.io.Serializable {
72 private static final AtomicLong globalSequenceNumber
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.
83 private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;
85 private static final AtomicInteger nextThreadId
86 = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);
88 private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();
91 * @serial Logging message level
96 * @serial Sequence number
98 private long sequenceNumber;
101 * @serial Class that issued logging call
103 private String sourceClassName;
106 * @serial Method that issued logging call
108 private String sourceMethodName;
111 * @serial Non-localized raw message text
113 private String message;
116 * @serial Thread ID for thread that issued logging call.
118 private int threadID;
121 * @serial Event time in milliseconds since 1970
126 * @serial The Throwable (if any) associated with log message
128 private Throwable thrown;
131 * @serial Name of the source Logger.
133 private String loggerName;
136 * @serial Resource bundle name to localized log message.
138 private String resourceBundleName;
140 private transient boolean needToInferCaller;
141 private transient Object parameters[];
142 private transient ResourceBundle resourceBundle;
145 * Returns the default value for a new LogRecord's threadID.
147 private int defaultThreadID() {
148 long tid = Thread.currentThread().getId();
149 if (tid < MIN_SEQUENTIAL_THREAD_ID) {
152 Integer id = threadIds.get();
154 id = nextThreadId.getAndIncrement();
162 * Construct a LogRecord with the given level and message values.
164 * The sequence property will be initialized with a new unique value.
165 * These sequence values are allocated in increasing order within a VM.
167 * The millis property will be initialized to the current time.
169 * The thread ID property will be initialized with a unique ID for
170 * the current thread.
172 * All other properties will be initialized to "null".
174 * @param level a logging level value
175 * @param msg the raw non-localized logging message (may be null)
177 public LogRecord(Level level, String msg) {
178 // Make sure level isn't null, by calling random method.
182 // Assign a thread ID and a unique sequence number.
183 sequenceNumber = globalSequenceNumber.getAndIncrement();
184 threadID = defaultThreadID();
185 millis = System.currentTimeMillis();
186 needToInferCaller = true;
190 * Get the source Logger's name.
192 * @return source logger name (may be null)
194 public String getLoggerName() {
199 * Set the source Logger's name.
201 * @param name the source logger name (may be null)
203 public void setLoggerName(String name) {
208 * Get the localization resource bundle
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.
215 public ResourceBundle getResourceBundle() {
216 return resourceBundle;
220 * Set the localization resource bundle.
222 * @param bundle localization bundle (may be null)
224 public void setResourceBundle(ResourceBundle bundle) {
225 resourceBundle = bundle;
229 * Get the localization resource bundle name
231 * This is the name for the ResourceBundle that should be
232 * used to localize the message string before formatting it.
233 * The result may be null if the message is not localizable.
235 public String getResourceBundleName() {
236 return resourceBundleName;
240 * Set the localization resource bundle name.
242 * @param name localization bundle name (may be null)
244 public void setResourceBundleName(String name) {
245 resourceBundleName = name;
249 * Get the logging message level, for example Level.SEVERE.
250 * @return the logging message level
252 public Level getLevel() {
257 * Set the logging message level, for example Level.SEVERE.
258 * @param level the logging message level
260 public void setLevel(Level level) {
262 throw new NullPointerException();
268 * Get the sequence number.
270 * Sequence numbers are normally assigned in the LogRecord
271 * constructor, which assigns unique sequence numbers to
272 * each new LogRecord in increasing order.
273 * @return the sequence number
275 public long getSequenceNumber() {
276 return sequenceNumber;
280 * Set the sequence number.
282 * Sequence numbers are normally assigned in the LogRecord constructor,
283 * so it should not normally be necessary to use this method.
285 public void setSequenceNumber(long seq) {
286 sequenceNumber = seq;
290 * Get the name of the class that (allegedly) issued the logging request.
292 * Note that this sourceClassName is not verified and may be spoofed.
293 * This information may either have been provided as part of the
294 * logging call, or it may have been inferred automatically by the
295 * logging framework. In the latter case, the information may only
296 * be approximate and may in fact describe an earlier call on the
299 * May be null if no information could be obtained.
301 * @return the source class name
303 public String getSourceClassName() {
304 if (needToInferCaller) {
307 return sourceClassName;
311 * Set the name of the class that (allegedly) issued the logging request.
313 * @param sourceClassName the source class name (may be null)
315 public void setSourceClassName(String sourceClassName) {
316 this.sourceClassName = sourceClassName;
317 needToInferCaller = false;
321 * Get the name of the method that (allegedly) issued the logging request.
323 * Note that this sourceMethodName is not verified and may be spoofed.
324 * This information may either have been provided as part of the
325 * logging call, or it may have been inferred automatically by the
326 * logging framework. In the latter case, the information may only
327 * be approximate and may in fact describe an earlier call on the
330 * May be null if no information could be obtained.
332 * @return the source method name
334 public String getSourceMethodName() {
335 if (needToInferCaller) {
338 return sourceMethodName;
342 * Set the name of the method that (allegedly) issued the logging request.
344 * @param sourceMethodName the source method name (may be null)
346 public void setSourceMethodName(String sourceMethodName) {
347 this.sourceMethodName = sourceMethodName;
348 needToInferCaller = false;
352 * Get the "raw" log message, before localization or formatting.
354 * May be null, which is equivalent to the empty string "".
356 * This message may be either the final text or a localization key.
358 * During formatting, if the source logger has a localization
359 * ResourceBundle and if that ResourceBundle has an entry for
360 * this message string, then the message string is replaced
361 * with the localized value.
363 * @return the raw message string
365 public String getMessage() {
370 * Set the "raw" log message, before localization or formatting.
372 * @param message the raw message string (may be null)
374 public void setMessage(String message) {
375 this.message = message;
379 * Get the parameters to the log message.
381 * @return the log message parameters. May be null if
382 * there are no parameters.
384 public Object[] getParameters() {
389 * Set the parameters to the log message.
391 * @param parameters the log message parameters. (may be null)
393 public void setParameters(Object parameters[]) {
394 this.parameters = parameters;
398 * Get an identifier for the thread where the message originated.
400 * This is a thread identifier within the Java VM and may or
401 * may not map to any operating system ID.
405 public int getThreadID() {
410 * Set an identifier for the thread where the message originated.
411 * @param threadID the thread ID
413 public void setThreadID(int threadID) {
414 this.threadID = threadID;
418 * Get event time in milliseconds since 1970.
420 * @return event time in millis since 1970
422 public long getMillis() {
429 * @param millis event time in millis since 1970
431 public void setMillis(long millis) {
432 this.millis = millis;
436 * Get any throwable associated with the log record.
438 * If the event involved an exception, this will be the
439 * exception object. Otherwise null.
441 * @return a throwable
443 public Throwable getThrown() {
448 * Set a throwable associated with the log event.
450 * @param thrown a throwable (may be null)
452 public void setThrown(Throwable thrown) {
453 this.thrown = thrown;
456 private static final long serialVersionUID = 5372048053134512534L;
459 * @serialData Default fields, followed by a two byte version number
460 * (major byte, followed by minor byte), followed by information on
461 * the log record parameter array. If there is no parameter array,
462 * then -1 is written. If there is a parameter array (possible of zero
463 * length) then the array length is written as an integer, followed
464 * by String values for each parameter. If a parameter is null, then
465 * a null String is written. Otherwise the output of Object.toString()
468 private void writeObject(ObjectOutputStream out) throws IOException {
469 // We have to call defaultWriteObject first.
470 out.defaultWriteObject();
472 // Write our version number.
475 if (parameters == null) {
479 out.writeInt(parameters.length);
480 // Write string values for the parameters.
481 for (int i = 0; i < parameters.length; i++) {
482 if (parameters[i] == null) {
483 out.writeObject(null);
485 out.writeObject(parameters[i].toString());
490 private void readObject(ObjectInputStream in)
491 throws IOException, ClassNotFoundException {
492 // We have to call defaultReadObject first.
493 in.defaultReadObject();
495 // Read version number.
496 byte major = in.readByte();
497 byte minor = in.readByte();
499 throw new IOException("LogRecord: bad version: " + major + "." + minor);
501 int len = in.readInt();
505 parameters = new Object[len];
506 for (int i = 0; i < parameters.length; i++) {
507 parameters[i] = in.readObject();
510 // If necessary, try to regenerate the resource bundle.
511 if (resourceBundleName != null) {
513 resourceBundle = ResourceBundle.getBundle(resourceBundleName);
514 } catch (MissingResourceException ex) {
515 // This is not a good place to throw an exception,
516 // so we simply leave the resourceBundle null.
517 resourceBundle = null;
521 needToInferCaller = false;
524 // Private method to infer the caller's class and method names
525 private void inferCaller() {
526 needToInferCaller = false;
527 JavaLangAccess access = SharedSecrets.getJavaLangAccess();
528 Throwable throwable = new Throwable();
529 int depth = access.getStackTraceDepth(throwable);
531 boolean lookingForLogger = true;
532 for (int ix = 0; ix < depth; ix++) {
533 // Calling getStackTraceElement directly prevents the VM
534 // from paying the cost of building the entire stack frame.
535 StackTraceElement frame =
536 access.getStackTraceElement(throwable, ix);
537 String cname = frame.getClassName();
538 boolean isLoggerImpl = isLoggerImplFrame(cname);
539 if (lookingForLogger) {
540 // Skip all frames until we have found the first logger frame.
542 lookingForLogger = false;
546 // skip reflection call
547 if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
548 // We've found the relevant frame.
549 setSourceClassName(cname);
550 setSourceMethodName(frame.getMethodName());
556 // We haven't found a suitable frame, so just punt. This is
557 // OK as we are only committed to making a "best effort" here.
560 private boolean isLoggerImplFrame(String cname) {
561 // the log record could be created for a platform logger
562 return (cname.equals("java.util.logging.Logger") ||
563 cname.startsWith("java.util.logging.LoggingProxyImpl") ||
564 cname.startsWith("sun.util.logging."));