1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/emul/compact/src/main/java/java/util/logging/LogRecord.java Sat Sep 07 13:51:24 2013 +0200
1.3 @@ -0,0 +1,566 @@
1.4 +/*
1.5 + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util.logging;
1.30 +import java.util.*;
1.31 +import java.util.concurrent.atomic.AtomicInteger;
1.32 +import java.util.concurrent.atomic.AtomicLong;
1.33 +import java.io.*;
1.34 +
1.35 +import sun.misc.JavaLangAccess;
1.36 +import sun.misc.SharedSecrets;
1.37 +
1.38 +/**
1.39 + * LogRecord objects are used to pass logging requests between
1.40 + * the logging framework and individual log Handlers.
1.41 + * <p>
1.42 + * When a LogRecord is passed into the logging framework it
1.43 + * logically belongs to the framework and should no longer be
1.44 + * used or updated by the client application.
1.45 + * <p>
1.46 + * Note that if the client application has not specified an
1.47 + * explicit source method name and source class name, then the
1.48 + * LogRecord class will infer them automatically when they are
1.49 + * first accessed (due to a call on getSourceMethodName or
1.50 + * getSourceClassName) by analyzing the call stack. Therefore,
1.51 + * if a logging Handler wants to pass off a LogRecord to another
1.52 + * thread, or to transmit it over RMI, and if it wishes to subsequently
1.53 + * obtain method name or class name information it should call
1.54 + * one of getSourceClassName or getSourceMethodName to force
1.55 + * the values to be filled in.
1.56 + * <p>
1.57 + * <b> Serialization notes:</b>
1.58 + * <ul>
1.59 + * <li>The LogRecord class is serializable.
1.60 + *
1.61 + * <li> Because objects in the parameters array may not be serializable,
1.62 + * during serialization all objects in the parameters array are
1.63 + * written as the corresponding Strings (using Object.toString).
1.64 + *
1.65 + * <li> The ResourceBundle is not transmitted as part of the serialized
1.66 + * form, but the resource bundle name is, and the recipient object's
1.67 + * readObject method will attempt to locate a suitable resource bundle.
1.68 + *
1.69 + * </ul>
1.70 + *
1.71 + * @since 1.4
1.72 + */
1.73 +
1.74 +public class LogRecord implements java.io.Serializable {
1.75 + private static final AtomicLong globalSequenceNumber
1.76 + = new AtomicLong(0);
1.77 +
1.78 + /**
1.79 + * The default value of threadID will be the current thread's
1.80 + * thread id, for ease of correlation, unless it is greater than
1.81 + * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
1.82 + * our promise to keep threadIDs unique by avoiding collisions due
1.83 + * to 32-bit wraparound. Unfortunately, LogRecord.getThreadID()
1.84 + * returns int, while Thread.getId() returns long.
1.85 + */
1.86 + private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;
1.87 +
1.88 + private static final AtomicInteger nextThreadId
1.89 + = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);
1.90 +
1.91 + private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();
1.92 +
1.93 + /**
1.94 + * @serial Logging message level
1.95 + */
1.96 + private Level level;
1.97 +
1.98 + /**
1.99 + * @serial Sequence number
1.100 + */
1.101 + private long sequenceNumber;
1.102 +
1.103 + /**
1.104 + * @serial Class that issued logging call
1.105 + */
1.106 + private String sourceClassName;
1.107 +
1.108 + /**
1.109 + * @serial Method that issued logging call
1.110 + */
1.111 + private String sourceMethodName;
1.112 +
1.113 + /**
1.114 + * @serial Non-localized raw message text
1.115 + */
1.116 + private String message;
1.117 +
1.118 + /**
1.119 + * @serial Thread ID for thread that issued logging call.
1.120 + */
1.121 + private int threadID;
1.122 +
1.123 + /**
1.124 + * @serial Event time in milliseconds since 1970
1.125 + */
1.126 + private long millis;
1.127 +
1.128 + /**
1.129 + * @serial The Throwable (if any) associated with log message
1.130 + */
1.131 + private Throwable thrown;
1.132 +
1.133 + /**
1.134 + * @serial Name of the source Logger.
1.135 + */
1.136 + private String loggerName;
1.137 +
1.138 + /**
1.139 + * @serial Resource bundle name to localized log message.
1.140 + */
1.141 + private String resourceBundleName;
1.142 +
1.143 + private transient boolean needToInferCaller;
1.144 + private transient Object parameters[];
1.145 + private transient ResourceBundle resourceBundle;
1.146 +
1.147 + /**
1.148 + * Returns the default value for a new LogRecord's threadID.
1.149 + */
1.150 + private int defaultThreadID() {
1.151 + long tid = Thread.currentThread().getId();
1.152 + if (tid < MIN_SEQUENTIAL_THREAD_ID) {
1.153 + return (int) tid;
1.154 + } else {
1.155 + Integer id = threadIds.get();
1.156 + if (id == null) {
1.157 + id = nextThreadId.getAndIncrement();
1.158 + threadIds.set(id);
1.159 + }
1.160 + return id;
1.161 + }
1.162 + }
1.163 +
1.164 + /**
1.165 + * Construct a LogRecord with the given level and message values.
1.166 + * <p>
1.167 + * The sequence property will be initialized with a new unique value.
1.168 + * These sequence values are allocated in increasing order within a VM.
1.169 + * <p>
1.170 + * The millis property will be initialized to the current time.
1.171 + * <p>
1.172 + * The thread ID property will be initialized with a unique ID for
1.173 + * the current thread.
1.174 + * <p>
1.175 + * All other properties will be initialized to "null".
1.176 + *
1.177 + * @param level a logging level value
1.178 + * @param msg the raw non-localized logging message (may be null)
1.179 + */
1.180 + public LogRecord(Level level, String msg) {
1.181 + // Make sure level isn't null, by calling random method.
1.182 + level.getClass();
1.183 + this.level = level;
1.184 + message = msg;
1.185 + // Assign a thread ID and a unique sequence number.
1.186 + sequenceNumber = globalSequenceNumber.getAndIncrement();
1.187 + threadID = defaultThreadID();
1.188 + millis = System.currentTimeMillis();
1.189 + needToInferCaller = true;
1.190 + }
1.191 +
1.192 + /**
1.193 + * Get the source Logger's name.
1.194 + *
1.195 + * @return source logger name (may be null)
1.196 + */
1.197 + public String getLoggerName() {
1.198 + return loggerName;
1.199 + }
1.200 +
1.201 + /**
1.202 + * Set the source Logger's name.
1.203 + *
1.204 + * @param name the source logger name (may be null)
1.205 + */
1.206 + public void setLoggerName(String name) {
1.207 + loggerName = name;
1.208 + }
1.209 +
1.210 + /**
1.211 + * Get the localization resource bundle
1.212 + * <p>
1.213 + * This is the ResourceBundle that should be used to localize
1.214 + * the message string before formatting it. The result may
1.215 + * be null if the message is not localizable, or if no suitable
1.216 + * ResourceBundle is available.
1.217 + */
1.218 + public ResourceBundle getResourceBundle() {
1.219 + return resourceBundle;
1.220 + }
1.221 +
1.222 + /**
1.223 + * Set the localization resource bundle.
1.224 + *
1.225 + * @param bundle localization bundle (may be null)
1.226 + */
1.227 + public void setResourceBundle(ResourceBundle bundle) {
1.228 + resourceBundle = bundle;
1.229 + }
1.230 +
1.231 + /**
1.232 + * Get the localization resource bundle name
1.233 + * <p>
1.234 + * This is the name for the ResourceBundle that should be
1.235 + * used to localize the message string before formatting it.
1.236 + * The result may be null if the message is not localizable.
1.237 + */
1.238 + public String getResourceBundleName() {
1.239 + return resourceBundleName;
1.240 + }
1.241 +
1.242 + /**
1.243 + * Set the localization resource bundle name.
1.244 + *
1.245 + * @param name localization bundle name (may be null)
1.246 + */
1.247 + public void setResourceBundleName(String name) {
1.248 + resourceBundleName = name;
1.249 + }
1.250 +
1.251 + /**
1.252 + * Get the logging message level, for example Level.SEVERE.
1.253 + * @return the logging message level
1.254 + */
1.255 + public Level getLevel() {
1.256 + return level;
1.257 + }
1.258 +
1.259 + /**
1.260 + * Set the logging message level, for example Level.SEVERE.
1.261 + * @param level the logging message level
1.262 + */
1.263 + public void setLevel(Level level) {
1.264 + if (level == null) {
1.265 + throw new NullPointerException();
1.266 + }
1.267 + this.level = level;
1.268 + }
1.269 +
1.270 + /**
1.271 + * Get the sequence number.
1.272 + * <p>
1.273 + * Sequence numbers are normally assigned in the LogRecord
1.274 + * constructor, which assigns unique sequence numbers to
1.275 + * each new LogRecord in increasing order.
1.276 + * @return the sequence number
1.277 + */
1.278 + public long getSequenceNumber() {
1.279 + return sequenceNumber;
1.280 + }
1.281 +
1.282 + /**
1.283 + * Set the sequence number.
1.284 + * <p>
1.285 + * Sequence numbers are normally assigned in the LogRecord constructor,
1.286 + * so it should not normally be necessary to use this method.
1.287 + */
1.288 + public void setSequenceNumber(long seq) {
1.289 + sequenceNumber = seq;
1.290 + }
1.291 +
1.292 + /**
1.293 + * Get the name of the class that (allegedly) issued the logging request.
1.294 + * <p>
1.295 + * Note that this sourceClassName is not verified and may be spoofed.
1.296 + * This information may either have been provided as part of the
1.297 + * logging call, or it may have been inferred automatically by the
1.298 + * logging framework. In the latter case, the information may only
1.299 + * be approximate and may in fact describe an earlier call on the
1.300 + * stack frame.
1.301 + * <p>
1.302 + * May be null if no information could be obtained.
1.303 + *
1.304 + * @return the source class name
1.305 + */
1.306 + public String getSourceClassName() {
1.307 + if (needToInferCaller) {
1.308 + inferCaller();
1.309 + }
1.310 + return sourceClassName;
1.311 + }
1.312 +
1.313 + /**
1.314 + * Set the name of the class that (allegedly) issued the logging request.
1.315 + *
1.316 + * @param sourceClassName the source class name (may be null)
1.317 + */
1.318 + public void setSourceClassName(String sourceClassName) {
1.319 + this.sourceClassName = sourceClassName;
1.320 + needToInferCaller = false;
1.321 + }
1.322 +
1.323 + /**
1.324 + * Get the name of the method that (allegedly) issued the logging request.
1.325 + * <p>
1.326 + * Note that this sourceMethodName is not verified and may be spoofed.
1.327 + * This information may either have been provided as part of the
1.328 + * logging call, or it may have been inferred automatically by the
1.329 + * logging framework. In the latter case, the information may only
1.330 + * be approximate and may in fact describe an earlier call on the
1.331 + * stack frame.
1.332 + * <p>
1.333 + * May be null if no information could be obtained.
1.334 + *
1.335 + * @return the source method name
1.336 + */
1.337 + public String getSourceMethodName() {
1.338 + if (needToInferCaller) {
1.339 + inferCaller();
1.340 + }
1.341 + return sourceMethodName;
1.342 + }
1.343 +
1.344 + /**
1.345 + * Set the name of the method that (allegedly) issued the logging request.
1.346 + *
1.347 + * @param sourceMethodName the source method name (may be null)
1.348 + */
1.349 + public void setSourceMethodName(String sourceMethodName) {
1.350 + this.sourceMethodName = sourceMethodName;
1.351 + needToInferCaller = false;
1.352 + }
1.353 +
1.354 + /**
1.355 + * Get the "raw" log message, before localization or formatting.
1.356 + * <p>
1.357 + * May be null, which is equivalent to the empty string "".
1.358 + * <p>
1.359 + * This message may be either the final text or a localization key.
1.360 + * <p>
1.361 + * During formatting, if the source logger has a localization
1.362 + * ResourceBundle and if that ResourceBundle has an entry for
1.363 + * this message string, then the message string is replaced
1.364 + * with the localized value.
1.365 + *
1.366 + * @return the raw message string
1.367 + */
1.368 + public String getMessage() {
1.369 + return message;
1.370 + }
1.371 +
1.372 + /**
1.373 + * Set the "raw" log message, before localization or formatting.
1.374 + *
1.375 + * @param message the raw message string (may be null)
1.376 + */
1.377 + public void setMessage(String message) {
1.378 + this.message = message;
1.379 + }
1.380 +
1.381 + /**
1.382 + * Get the parameters to the log message.
1.383 + *
1.384 + * @return the log message parameters. May be null if
1.385 + * there are no parameters.
1.386 + */
1.387 + public Object[] getParameters() {
1.388 + return parameters;
1.389 + }
1.390 +
1.391 + /**
1.392 + * Set the parameters to the log message.
1.393 + *
1.394 + * @param parameters the log message parameters. (may be null)
1.395 + */
1.396 + public void setParameters(Object parameters[]) {
1.397 + this.parameters = parameters;
1.398 + }
1.399 +
1.400 + /**
1.401 + * Get an identifier for the thread where the message originated.
1.402 + * <p>
1.403 + * This is a thread identifier within the Java VM and may or
1.404 + * may not map to any operating system ID.
1.405 + *
1.406 + * @return thread ID
1.407 + */
1.408 + public int getThreadID() {
1.409 + return threadID;
1.410 + }
1.411 +
1.412 + /**
1.413 + * Set an identifier for the thread where the message originated.
1.414 + * @param threadID the thread ID
1.415 + */
1.416 + public void setThreadID(int threadID) {
1.417 + this.threadID = threadID;
1.418 + }
1.419 +
1.420 + /**
1.421 + * Get event time in milliseconds since 1970.
1.422 + *
1.423 + * @return event time in millis since 1970
1.424 + */
1.425 + public long getMillis() {
1.426 + return millis;
1.427 + }
1.428 +
1.429 + /**
1.430 + * Set event time.
1.431 + *
1.432 + * @param millis event time in millis since 1970
1.433 + */
1.434 + public void setMillis(long millis) {
1.435 + this.millis = millis;
1.436 + }
1.437 +
1.438 + /**
1.439 + * Get any throwable associated with the log record.
1.440 + * <p>
1.441 + * If the event involved an exception, this will be the
1.442 + * exception object. Otherwise null.
1.443 + *
1.444 + * @return a throwable
1.445 + */
1.446 + public Throwable getThrown() {
1.447 + return thrown;
1.448 + }
1.449 +
1.450 + /**
1.451 + * Set a throwable associated with the log event.
1.452 + *
1.453 + * @param thrown a throwable (may be null)
1.454 + */
1.455 + public void setThrown(Throwable thrown) {
1.456 + this.thrown = thrown;
1.457 + }
1.458 +
1.459 + private static final long serialVersionUID = 5372048053134512534L;
1.460 +
1.461 + /**
1.462 + * @serialData Default fields, followed by a two byte version number
1.463 + * (major byte, followed by minor byte), followed by information on
1.464 + * the log record parameter array. If there is no parameter array,
1.465 + * then -1 is written. If there is a parameter array (possible of zero
1.466 + * length) then the array length is written as an integer, followed
1.467 + * by String values for each parameter. If a parameter is null, then
1.468 + * a null String is written. Otherwise the output of Object.toString()
1.469 + * is written.
1.470 + */
1.471 + private void writeObject(ObjectOutputStream out) throws IOException {
1.472 + // We have to call defaultWriteObject first.
1.473 + out.defaultWriteObject();
1.474 +
1.475 + // Write our version number.
1.476 + out.writeByte(1);
1.477 + out.writeByte(0);
1.478 + if (parameters == null) {
1.479 + out.writeInt(-1);
1.480 + return;
1.481 + }
1.482 + out.writeInt(parameters.length);
1.483 + // Write string values for the parameters.
1.484 + for (int i = 0; i < parameters.length; i++) {
1.485 + if (parameters[i] == null) {
1.486 + out.writeObject(null);
1.487 + } else {
1.488 + out.writeObject(parameters[i].toString());
1.489 + }
1.490 + }
1.491 + }
1.492 +
1.493 + private void readObject(ObjectInputStream in)
1.494 + throws IOException, ClassNotFoundException {
1.495 + // We have to call defaultReadObject first.
1.496 + in.defaultReadObject();
1.497 +
1.498 + // Read version number.
1.499 + byte major = in.readByte();
1.500 + byte minor = in.readByte();
1.501 + if (major != 1) {
1.502 + throw new IOException("LogRecord: bad version: " + major + "." + minor);
1.503 + }
1.504 + int len = in.readInt();
1.505 + if (len == -1) {
1.506 + parameters = null;
1.507 + } else {
1.508 + parameters = new Object[len];
1.509 + for (int i = 0; i < parameters.length; i++) {
1.510 + parameters[i] = in.readObject();
1.511 + }
1.512 + }
1.513 + // If necessary, try to regenerate the resource bundle.
1.514 + if (resourceBundleName != null) {
1.515 + try {
1.516 + resourceBundle = ResourceBundle.getBundle(resourceBundleName);
1.517 + } catch (MissingResourceException ex) {
1.518 + // This is not a good place to throw an exception,
1.519 + // so we simply leave the resourceBundle null.
1.520 + resourceBundle = null;
1.521 + }
1.522 + }
1.523 +
1.524 + needToInferCaller = false;
1.525 + }
1.526 +
1.527 + // Private method to infer the caller's class and method names
1.528 + private void inferCaller() {
1.529 + needToInferCaller = false;
1.530 + JavaLangAccess access = SharedSecrets.getJavaLangAccess();
1.531 + Throwable throwable = new Throwable();
1.532 + int depth = access.getStackTraceDepth(throwable);
1.533 +
1.534 + boolean lookingForLogger = true;
1.535 + for (int ix = 0; ix < depth; ix++) {
1.536 + // Calling getStackTraceElement directly prevents the VM
1.537 + // from paying the cost of building the entire stack frame.
1.538 + StackTraceElement frame =
1.539 + access.getStackTraceElement(throwable, ix);
1.540 + String cname = frame.getClassName();
1.541 + boolean isLoggerImpl = isLoggerImplFrame(cname);
1.542 + if (lookingForLogger) {
1.543 + // Skip all frames until we have found the first logger frame.
1.544 + if (isLoggerImpl) {
1.545 + lookingForLogger = false;
1.546 + }
1.547 + } else {
1.548 + if (!isLoggerImpl) {
1.549 + // skip reflection call
1.550 + if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
1.551 + // We've found the relevant frame.
1.552 + setSourceClassName(cname);
1.553 + setSourceMethodName(frame.getMethodName());
1.554 + return;
1.555 + }
1.556 + }
1.557 + }
1.558 + }
1.559 + // We haven't found a suitable frame, so just punt. This is
1.560 + // OK as we are only committed to making a "best effort" here.
1.561 + }
1.562 +
1.563 + private boolean isLoggerImplFrame(String cname) {
1.564 + // the log record could be created for a platform logger
1.565 + return (cname.equals("java.util.logging.Logger") ||
1.566 + cname.startsWith("java.util.logging.LoggingProxyImpl") ||
1.567 + cname.startsWith("sun.util.logging."));
1.568 + }
1.569 +}