jaroslav@1258: /* jaroslav@1258: * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. jaroslav@1258: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1258: * jaroslav@1258: * This code is free software; you can redistribute it and/or modify it jaroslav@1258: * under the terms of the GNU General Public License version 2 only, as jaroslav@1258: * published by the Free Software Foundation. Oracle designates this jaroslav@1258: * particular file as subject to the "Classpath" exception as provided jaroslav@1258: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1258: * jaroslav@1258: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1258: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1258: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1258: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1258: * accompanied this code). jaroslav@1258: * jaroslav@1258: * You should have received a copy of the GNU General Public License version jaroslav@1258: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1258: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1258: * jaroslav@1258: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1258: * or visit www.oracle.com if you need additional information or have any jaroslav@1258: * questions. jaroslav@1258: */ jaroslav@1258: jaroslav@1258: package java.util.logging; jaroslav@1258: import java.util.*; jaroslav@1258: import java.util.concurrent.atomic.AtomicInteger; jaroslav@1258: import java.util.concurrent.atomic.AtomicLong; jaroslav@1258: import java.io.*; jaroslav@1258: jaroslav@1258: import sun.misc.JavaLangAccess; jaroslav@1258: import sun.misc.SharedSecrets; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * LogRecord objects are used to pass logging requests between jaroslav@1258: * the logging framework and individual log Handlers. jaroslav@1258: *

jaroslav@1258: * When a LogRecord is passed into the logging framework it jaroslav@1258: * logically belongs to the framework and should no longer be jaroslav@1258: * used or updated by the client application. jaroslav@1258: *

jaroslav@1258: * Note that if the client application has not specified an jaroslav@1258: * explicit source method name and source class name, then the jaroslav@1258: * LogRecord class will infer them automatically when they are jaroslav@1258: * first accessed (due to a call on getSourceMethodName or jaroslav@1258: * getSourceClassName) by analyzing the call stack. Therefore, jaroslav@1258: * if a logging Handler wants to pass off a LogRecord to another jaroslav@1258: * thread, or to transmit it over RMI, and if it wishes to subsequently jaroslav@1258: * obtain method name or class name information it should call jaroslav@1258: * one of getSourceClassName or getSourceMethodName to force jaroslav@1258: * the values to be filled in. jaroslav@1258: *

jaroslav@1258: * Serialization notes: jaroslav@1258: *

jaroslav@1258: * jaroslav@1258: * @since 1.4 jaroslav@1258: */ jaroslav@1258: jaroslav@1258: public class LogRecord implements java.io.Serializable { jaroslav@1258: private static final AtomicLong globalSequenceNumber jaroslav@1258: = new AtomicLong(0); jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * The default value of threadID will be the current thread's jaroslav@1258: * thread id, for ease of correlation, unless it is greater than jaroslav@1258: * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep jaroslav@1258: * our promise to keep threadIDs unique by avoiding collisions due jaroslav@1258: * to 32-bit wraparound. Unfortunately, LogRecord.getThreadID() jaroslav@1258: * returns int, while Thread.getId() returns long. jaroslav@1258: */ jaroslav@1258: private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2; jaroslav@1258: jaroslav@1258: private static final AtomicInteger nextThreadId jaroslav@1258: = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID); jaroslav@1258: jaroslav@1258: private static final ThreadLocal threadIds = new ThreadLocal<>(); jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Logging message level jaroslav@1258: */ jaroslav@1258: private Level level; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Sequence number jaroslav@1258: */ jaroslav@1258: private long sequenceNumber; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Class that issued logging call jaroslav@1258: */ jaroslav@1258: private String sourceClassName; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Method that issued logging call jaroslav@1258: */ jaroslav@1258: private String sourceMethodName; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Non-localized raw message text jaroslav@1258: */ jaroslav@1258: private String message; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Thread ID for thread that issued logging call. jaroslav@1258: */ jaroslav@1258: private int threadID; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Event time in milliseconds since 1970 jaroslav@1258: */ jaroslav@1258: private long millis; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial The Throwable (if any) associated with log message jaroslav@1258: */ jaroslav@1258: private Throwable thrown; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Name of the source Logger. jaroslav@1258: */ jaroslav@1258: private String loggerName; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serial Resource bundle name to localized log message. jaroslav@1258: */ jaroslav@1258: private String resourceBundleName; jaroslav@1258: jaroslav@1258: private transient boolean needToInferCaller; jaroslav@1258: private transient Object parameters[]; jaroslav@1258: private transient ResourceBundle resourceBundle; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Returns the default value for a new LogRecord's threadID. jaroslav@1258: */ jaroslav@1258: private int defaultThreadID() { jaroslav@1258: long tid = Thread.currentThread().getId(); jaroslav@1258: if (tid < MIN_SEQUENTIAL_THREAD_ID) { jaroslav@1258: return (int) tid; jaroslav@1258: } else { jaroslav@1258: Integer id = threadIds.get(); jaroslav@1258: if (id == null) { jaroslav@1258: id = nextThreadId.getAndIncrement(); jaroslav@1258: threadIds.set(id); jaroslav@1258: } jaroslav@1258: return id; jaroslav@1258: } jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Construct a LogRecord with the given level and message values. jaroslav@1258: *

jaroslav@1258: * The sequence property will be initialized with a new unique value. jaroslav@1258: * These sequence values are allocated in increasing order within a VM. jaroslav@1258: *

jaroslav@1258: * The millis property will be initialized to the current time. jaroslav@1258: *

jaroslav@1258: * The thread ID property will be initialized with a unique ID for jaroslav@1258: * the current thread. jaroslav@1258: *

jaroslav@1258: * All other properties will be initialized to "null". jaroslav@1258: * jaroslav@1258: * @param level a logging level value jaroslav@1258: * @param msg the raw non-localized logging message (may be null) jaroslav@1258: */ jaroslav@1258: public LogRecord(Level level, String msg) { jaroslav@1258: // Make sure level isn't null, by calling random method. jaroslav@1258: level.getClass(); jaroslav@1258: this.level = level; jaroslav@1258: message = msg; jaroslav@1258: // Assign a thread ID and a unique sequence number. jaroslav@1258: sequenceNumber = globalSequenceNumber.getAndIncrement(); jaroslav@1258: threadID = defaultThreadID(); jaroslav@1258: millis = System.currentTimeMillis(); jaroslav@1258: needToInferCaller = true; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the source Logger's name. jaroslav@1258: * jaroslav@1258: * @return source logger name (may be null) jaroslav@1258: */ jaroslav@1258: public String getLoggerName() { jaroslav@1258: return loggerName; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the source Logger's name. jaroslav@1258: * jaroslav@1258: * @param name the source logger name (may be null) jaroslav@1258: */ jaroslav@1258: public void setLoggerName(String name) { jaroslav@1258: loggerName = name; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the localization resource bundle jaroslav@1258: *

jaroslav@1258: * This is the ResourceBundle that should be used to localize jaroslav@1258: * the message string before formatting it. The result may jaroslav@1258: * be null if the message is not localizable, or if no suitable jaroslav@1258: * ResourceBundle is available. jaroslav@1258: */ jaroslav@1258: public ResourceBundle getResourceBundle() { jaroslav@1258: return resourceBundle; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the localization resource bundle. jaroslav@1258: * jaroslav@1258: * @param bundle localization bundle (may be null) jaroslav@1258: */ jaroslav@1258: public void setResourceBundle(ResourceBundle bundle) { jaroslav@1258: resourceBundle = bundle; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the localization resource bundle name jaroslav@1258: *

jaroslav@1258: * This is the name for the ResourceBundle that should be jaroslav@1258: * used to localize the message string before formatting it. jaroslav@1258: * The result may be null if the message is not localizable. jaroslav@1258: */ jaroslav@1258: public String getResourceBundleName() { jaroslav@1258: return resourceBundleName; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the localization resource bundle name. jaroslav@1258: * jaroslav@1258: * @param name localization bundle name (may be null) jaroslav@1258: */ jaroslav@1258: public void setResourceBundleName(String name) { jaroslav@1258: resourceBundleName = name; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the logging message level, for example Level.SEVERE. jaroslav@1258: * @return the logging message level jaroslav@1258: */ jaroslav@1258: public Level getLevel() { jaroslav@1258: return level; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the logging message level, for example Level.SEVERE. jaroslav@1258: * @param level the logging message level jaroslav@1258: */ jaroslav@1258: public void setLevel(Level level) { jaroslav@1258: if (level == null) { jaroslav@1258: throw new NullPointerException(); jaroslav@1258: } jaroslav@1258: this.level = level; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the sequence number. jaroslav@1258: *

jaroslav@1258: * Sequence numbers are normally assigned in the LogRecord jaroslav@1258: * constructor, which assigns unique sequence numbers to jaroslav@1258: * each new LogRecord in increasing order. jaroslav@1258: * @return the sequence number jaroslav@1258: */ jaroslav@1258: public long getSequenceNumber() { jaroslav@1258: return sequenceNumber; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the sequence number. jaroslav@1258: *

jaroslav@1258: * Sequence numbers are normally assigned in the LogRecord constructor, jaroslav@1258: * so it should not normally be necessary to use this method. jaroslav@1258: */ jaroslav@1258: public void setSequenceNumber(long seq) { jaroslav@1258: sequenceNumber = seq; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the name of the class that (allegedly) issued the logging request. jaroslav@1258: *

jaroslav@1258: * Note that this sourceClassName is not verified and may be spoofed. jaroslav@1258: * This information may either have been provided as part of the jaroslav@1258: * logging call, or it may have been inferred automatically by the jaroslav@1258: * logging framework. In the latter case, the information may only jaroslav@1258: * be approximate and may in fact describe an earlier call on the jaroslav@1258: * stack frame. jaroslav@1258: *

jaroslav@1258: * May be null if no information could be obtained. jaroslav@1258: * jaroslav@1258: * @return the source class name jaroslav@1258: */ jaroslav@1258: public String getSourceClassName() { jaroslav@1258: if (needToInferCaller) { jaroslav@1258: inferCaller(); jaroslav@1258: } jaroslav@1258: return sourceClassName; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the name of the class that (allegedly) issued the logging request. jaroslav@1258: * jaroslav@1258: * @param sourceClassName the source class name (may be null) jaroslav@1258: */ jaroslav@1258: public void setSourceClassName(String sourceClassName) { jaroslav@1258: this.sourceClassName = sourceClassName; jaroslav@1258: needToInferCaller = false; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the name of the method that (allegedly) issued the logging request. jaroslav@1258: *

jaroslav@1258: * Note that this sourceMethodName is not verified and may be spoofed. jaroslav@1258: * This information may either have been provided as part of the jaroslav@1258: * logging call, or it may have been inferred automatically by the jaroslav@1258: * logging framework. In the latter case, the information may only jaroslav@1258: * be approximate and may in fact describe an earlier call on the jaroslav@1258: * stack frame. jaroslav@1258: *

jaroslav@1258: * May be null if no information could be obtained. jaroslav@1258: * jaroslav@1258: * @return the source method name jaroslav@1258: */ jaroslav@1258: public String getSourceMethodName() { jaroslav@1258: if (needToInferCaller) { jaroslav@1258: inferCaller(); jaroslav@1258: } jaroslav@1258: return sourceMethodName; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the name of the method that (allegedly) issued the logging request. jaroslav@1258: * jaroslav@1258: * @param sourceMethodName the source method name (may be null) jaroslav@1258: */ jaroslav@1258: public void setSourceMethodName(String sourceMethodName) { jaroslav@1258: this.sourceMethodName = sourceMethodName; jaroslav@1258: needToInferCaller = false; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the "raw" log message, before localization or formatting. jaroslav@1258: *

jaroslav@1258: * May be null, which is equivalent to the empty string "". jaroslav@1258: *

jaroslav@1258: * This message may be either the final text or a localization key. jaroslav@1258: *

jaroslav@1258: * During formatting, if the source logger has a localization jaroslav@1258: * ResourceBundle and if that ResourceBundle has an entry for jaroslav@1258: * this message string, then the message string is replaced jaroslav@1258: * with the localized value. jaroslav@1258: * jaroslav@1258: * @return the raw message string jaroslav@1258: */ jaroslav@1258: public String getMessage() { jaroslav@1258: return message; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the "raw" log message, before localization or formatting. jaroslav@1258: * jaroslav@1258: * @param message the raw message string (may be null) jaroslav@1258: */ jaroslav@1258: public void setMessage(String message) { jaroslav@1258: this.message = message; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get the parameters to the log message. jaroslav@1258: * jaroslav@1258: * @return the log message parameters. May be null if jaroslav@1258: * there are no parameters. jaroslav@1258: */ jaroslav@1258: public Object[] getParameters() { jaroslav@1258: return parameters; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set the parameters to the log message. jaroslav@1258: * jaroslav@1258: * @param parameters the log message parameters. (may be null) jaroslav@1258: */ jaroslav@1258: public void setParameters(Object parameters[]) { jaroslav@1258: this.parameters = parameters; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get an identifier for the thread where the message originated. jaroslav@1258: *

jaroslav@1258: * This is a thread identifier within the Java VM and may or jaroslav@1258: * may not map to any operating system ID. jaroslav@1258: * jaroslav@1258: * @return thread ID jaroslav@1258: */ jaroslav@1258: public int getThreadID() { jaroslav@1258: return threadID; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set an identifier for the thread where the message originated. jaroslav@1258: * @param threadID the thread ID jaroslav@1258: */ jaroslav@1258: public void setThreadID(int threadID) { jaroslav@1258: this.threadID = threadID; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get event time in milliseconds since 1970. jaroslav@1258: * jaroslav@1258: * @return event time in millis since 1970 jaroslav@1258: */ jaroslav@1258: public long getMillis() { jaroslav@1258: return millis; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set event time. jaroslav@1258: * jaroslav@1258: * @param millis event time in millis since 1970 jaroslav@1258: */ jaroslav@1258: public void setMillis(long millis) { jaroslav@1258: this.millis = millis; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Get any throwable associated with the log record. jaroslav@1258: *

jaroslav@1258: * If the event involved an exception, this will be the jaroslav@1258: * exception object. Otherwise null. jaroslav@1258: * jaroslav@1258: * @return a throwable jaroslav@1258: */ jaroslav@1258: public Throwable getThrown() { jaroslav@1258: return thrown; jaroslav@1258: } jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * Set a throwable associated with the log event. jaroslav@1258: * jaroslav@1258: * @param thrown a throwable (may be null) jaroslav@1258: */ jaroslav@1258: public void setThrown(Throwable thrown) { jaroslav@1258: this.thrown = thrown; jaroslav@1258: } jaroslav@1258: jaroslav@1258: private static final long serialVersionUID = 5372048053134512534L; jaroslav@1258: jaroslav@1258: /** jaroslav@1258: * @serialData Default fields, followed by a two byte version number jaroslav@1258: * (major byte, followed by minor byte), followed by information on jaroslav@1258: * the log record parameter array. If there is no parameter array, jaroslav@1258: * then -1 is written. If there is a parameter array (possible of zero jaroslav@1258: * length) then the array length is written as an integer, followed jaroslav@1258: * by String values for each parameter. If a parameter is null, then jaroslav@1258: * a null String is written. Otherwise the output of Object.toString() jaroslav@1258: * is written. jaroslav@1258: */ jaroslav@1258: private void writeObject(ObjectOutputStream out) throws IOException { jaroslav@1258: // We have to call defaultWriteObject first. jaroslav@1258: out.defaultWriteObject(); jaroslav@1258: jaroslav@1258: // Write our version number. jaroslav@1258: out.writeByte(1); jaroslav@1258: out.writeByte(0); jaroslav@1258: if (parameters == null) { jaroslav@1258: out.writeInt(-1); jaroslav@1258: return; jaroslav@1258: } jaroslav@1258: out.writeInt(parameters.length); jaroslav@1258: // Write string values for the parameters. jaroslav@1258: for (int i = 0; i < parameters.length; i++) { jaroslav@1258: if (parameters[i] == null) { jaroslav@1258: out.writeObject(null); jaroslav@1258: } else { jaroslav@1258: out.writeObject(parameters[i].toString()); jaroslav@1258: } jaroslav@1258: } jaroslav@1258: } jaroslav@1258: jaroslav@1258: private void readObject(ObjectInputStream in) jaroslav@1258: throws IOException, ClassNotFoundException { jaroslav@1258: // We have to call defaultReadObject first. jaroslav@1258: in.defaultReadObject(); jaroslav@1258: jaroslav@1258: // Read version number. jaroslav@1258: byte major = in.readByte(); jaroslav@1258: byte minor = in.readByte(); jaroslav@1258: if (major != 1) { jaroslav@1258: throw new IOException("LogRecord: bad version: " + major + "." + minor); jaroslav@1258: } jaroslav@1258: int len = in.readInt(); jaroslav@1258: if (len == -1) { jaroslav@1258: parameters = null; jaroslav@1258: } else { jaroslav@1258: parameters = new Object[len]; jaroslav@1258: for (int i = 0; i < parameters.length; i++) { jaroslav@1258: parameters[i] = in.readObject(); jaroslav@1258: } jaroslav@1258: } jaroslav@1258: // If necessary, try to regenerate the resource bundle. jaroslav@1258: if (resourceBundleName != null) { jaroslav@1258: try { jaroslav@1258: resourceBundle = ResourceBundle.getBundle(resourceBundleName); jaroslav@1258: } catch (MissingResourceException ex) { jaroslav@1258: // This is not a good place to throw an exception, jaroslav@1258: // so we simply leave the resourceBundle null. jaroslav@1258: resourceBundle = null; jaroslav@1258: } jaroslav@1258: } jaroslav@1258: jaroslav@1258: needToInferCaller = false; jaroslav@1258: } jaroslav@1258: jaroslav@1258: // Private method to infer the caller's class and method names jaroslav@1258: private void inferCaller() { jaroslav@1258: needToInferCaller = false; jaroslav@1258: JavaLangAccess access = SharedSecrets.getJavaLangAccess(); jaroslav@1258: Throwable throwable = new Throwable(); jaroslav@1258: int depth = access.getStackTraceDepth(throwable); jaroslav@1258: jaroslav@1258: boolean lookingForLogger = true; jaroslav@1258: for (int ix = 0; ix < depth; ix++) { jaroslav@1258: // Calling getStackTraceElement directly prevents the VM jaroslav@1258: // from paying the cost of building the entire stack frame. jaroslav@1258: StackTraceElement frame = jaroslav@1258: access.getStackTraceElement(throwable, ix); jaroslav@1258: String cname = frame.getClassName(); jaroslav@1258: boolean isLoggerImpl = isLoggerImplFrame(cname); jaroslav@1258: if (lookingForLogger) { jaroslav@1258: // Skip all frames until we have found the first logger frame. jaroslav@1258: if (isLoggerImpl) { jaroslav@1258: lookingForLogger = false; jaroslav@1258: } jaroslav@1258: } else { jaroslav@1258: if (!isLoggerImpl) { jaroslav@1258: // skip reflection call jaroslav@1258: if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { jaroslav@1258: // We've found the relevant frame. jaroslav@1258: setSourceClassName(cname); jaroslav@1258: setSourceMethodName(frame.getMethodName()); jaroslav@1258: return; jaroslav@1258: } jaroslav@1258: } jaroslav@1258: } jaroslav@1258: } jaroslav@1258: // We haven't found a suitable frame, so just punt. This is jaroslav@1258: // OK as we are only committed to making a "best effort" here. jaroslav@1258: } jaroslav@1258: jaroslav@1258: private boolean isLoggerImplFrame(String cname) { jaroslav@1258: // the log record could be created for a platform logger jaroslav@1258: return (cname.equals("java.util.logging.Logger") || jaroslav@1258: cname.startsWith("java.util.logging.LoggingProxyImpl") || jaroslav@1258: cname.startsWith("sun.util.logging.")); jaroslav@1258: } jaroslav@1258: }