Skip to content

Commit

Permalink
Automatic merge of jdk:master into master
Browse files Browse the repository at this point in the history
  • Loading branch information
duke committed Jul 3, 2020
2 parents ee282bf + e2072bb commit a46a119
Show file tree
Hide file tree
Showing 4 changed files with 495 additions and 42 deletions.
142 changes: 102 additions & 40 deletions src/java.logging/share/classes/java/util/logging/LogRecord.java
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,7 +26,6 @@
package java.util.logging; package java.util.logging;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.io.*; import java.io.*;
import java.security.AccessController; import java.security.AccessController;
Expand Down Expand Up @@ -75,21 +74,6 @@ public class LogRecord implements java.io.Serializable {
private static final AtomicLong globalSequenceNumber private static final AtomicLong globalSequenceNumber
= new AtomicLong(0); = new AtomicLong(0);


/**
* The default value of threadID will be the current thread's
* thread id, for ease of correlation, unless it is greater than
* MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
* our promise to keep threadIDs unique by avoiding collisions due
* to 32-bit wraparound. Unfortunately, LogRecord.getThreadID()
* returns int, while Thread.getId() returns long.
*/
private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;

private static final AtomicInteger nextThreadId
= new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);

private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();

/** /**
* Logging message level * Logging message level
*/ */
Expand Down Expand Up @@ -120,6 +104,11 @@ public class LogRecord implements java.io.Serializable {
*/ */
private int threadID; private int threadID;


/**
* long value of Thread ID for thread that issued logging call.
*/
private long longThreadID;

/** /**
* The Throwable (if any) associated with log message * The Throwable (if any) associated with log message
*/ */
Expand Down Expand Up @@ -147,7 +136,10 @@ public class LogRecord implements java.io.Serializable {
* @serialField sourceClassName String Class that issued logging call * @serialField sourceClassName String Class that issued logging call
* @serialField sourceMethodName String Method that issued logging call * @serialField sourceMethodName String Method that issued logging call
* @serialField message String Non-localized raw message text * @serialField message String Non-localized raw message text
* @serialField threadID int Thread ID for thread that issued logging call * @serialField threadID int this is deprecated and is available for backward compatibility.
* Values may have been synthesized. If present, {@code longThreadID} represents
* the actual thread id.
* @serialField longThreadID long Thread ID for thread that issued logging call
* @serialField millis long Truncated event time in milliseconds since 1970 * @serialField millis long Truncated event time in milliseconds since 1970
* - calculated as getInstant().toEpochMilli(). * - calculated as getInstant().toEpochMilli().
* The event time instant can be reconstructed using * The event time instant can be reconstructed using
Expand All @@ -164,6 +156,7 @@ public class LogRecord implements java.io.Serializable {
* @serialField resourceBundleName String Resource bundle name to localized * @serialField resourceBundleName String Resource bundle name to localized
* log message * log message
*/ */
@Serial
private static final ObjectStreamField[] serialPersistentFields = private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[] { new ObjectStreamField[] {
new ObjectStreamField("level", Level.class), new ObjectStreamField("level", Level.class),
Expand All @@ -172,6 +165,7 @@ public class LogRecord implements java.io.Serializable {
new ObjectStreamField("sourceMethodName", String.class), new ObjectStreamField("sourceMethodName", String.class),
new ObjectStreamField("message", String.class), new ObjectStreamField("message", String.class),
new ObjectStreamField("threadID", int.class), new ObjectStreamField("threadID", int.class),
new ObjectStreamField("longThreadID", long.class),
new ObjectStreamField("millis", long.class), new ObjectStreamField("millis", long.class),
new ObjectStreamField("nanoAdjustment", int.class), new ObjectStreamField("nanoAdjustment", int.class),
new ObjectStreamField("thrown", Throwable.class), new ObjectStreamField("thrown", Throwable.class),
Expand All @@ -184,20 +178,22 @@ public class LogRecord implements java.io.Serializable {
private transient ResourceBundle resourceBundle; private transient ResourceBundle resourceBundle;


/** /**
* Returns the default value for a new LogRecord's threadID. * Synthesizes a pseudo unique integer value from a long {@code id} value.
* For backward compatibility with previous releases,the returned integer is
* such that for any positive long less than or equals to {@code Integer.MAX_VALUE},
* the returned integer is equal to the original value.
* Otherwise - it is synthesized with a best effort hashing algorithm,
* and the returned value is negative.
* Calling this method multiple times with the same value always yields the same result.
*
* @return thread id
*/ */
private int defaultThreadID() {
long tid = Thread.currentThread().getId(); private int shortThreadID(long id) {
if (tid < MIN_SEQUENTIAL_THREAD_ID) { if (id >= 0 && id <= Integer.MAX_VALUE)
return (int) tid; return (int) id;
} else { int hash = Long.hashCode(id);
Integer id = threadIds.get(); return hash < 0 ? hash : (-1 - hash);
if (id == null) {
id = nextThreadId.getAndIncrement();
threadIds.set(id);
}
return id;
}
} }


/** /**
Expand Down Expand Up @@ -225,10 +221,13 @@ public LogRecord(Level level, String msg) {
message = msg; message = msg;
// Assign a thread ID and a unique sequence number. // Assign a thread ID and a unique sequence number.
sequenceNumber = globalSequenceNumber.getAndIncrement(); sequenceNumber = globalSequenceNumber.getAndIncrement();
threadID = defaultThreadID(); long id = Thread.currentThread().getId();
// threadID is deprecated and this value is synthesised for backward compatibility
threadID = shortThreadID(id);
longThreadID = id;
instant = Instant.now(); instant = Instant.now();
needToInferCaller = true; needToInferCaller = true;
} }


/** /**
* Get the source Logger's name. * Get the source Logger's name.
Expand Down Expand Up @@ -447,18 +446,54 @@ public void setParameters(Object parameters[]) {
* This is a thread identifier within the Java VM and may or * This is a thread identifier within the Java VM and may or
* may not map to any operating system ID. * may not map to any operating system ID.
* *
* @deprecated Values returned by this method may be synthesized,
* and may not correspond to the actual {@linkplain Thread#getId() thread id},
* use {@link #getLongThreadID()} instead.
* @return thread ID * @return thread ID
*/ */
@Deprecated(since = "16")
public int getThreadID() { public int getThreadID() {
return threadID; return threadID;
} }


/** /**
* Set an identifier for the thread where the message originated. * Set an identifier for the thread where the message originated.
* @param threadID the thread ID * @param threadID the thread ID
*
* @deprecated This method doesn't allow to pass a long {@linkplain Thread#getId() thread id},
* use {@link #setLongThreadID(long)} instead.
*/ */
@Deprecated(since = "16")
public void setThreadID(int threadID) { public void setThreadID(int threadID) {
this.threadID = threadID; this.threadID = threadID;
this.longThreadID = threadID;
}

/**
* Get a thread identifier for the thread where message originated
*
* <p>
* This is a thread identifier within the Java VM and may or
* may not map to any operating system ID.
*
* @return thread ID
* @since 16
*/
public long getLongThreadID() {
return longThreadID;
}

/**
* Set an identifier for the thread where the message originated.
*
* @param longThreadID the thread ID
* @return this LogRecord
* @since 16
*/
public LogRecord setLongThreadID(long longThreadID) {
this.threadID = shortThreadID(longThreadID);
this.longThreadID = longThreadID;
return this;
} }


/** /**
Expand Down Expand Up @@ -552,6 +587,7 @@ public void setThrown(Throwable thrown) {
this.thrown = thrown; this.thrown = thrown;
} }


@Serial
private static final long serialVersionUID = 5372048053134512534L; private static final long serialVersionUID = 5372048053134512534L;


/** /**
Expand All @@ -564,6 +600,7 @@ public void setThrown(Throwable thrown) {
* a null String is written. Otherwise the output of Object.toString() * a null String is written. Otherwise the output of Object.toString()
* is written. * is written.
*/ */
@Serial
private void writeObject(ObjectOutputStream out) throws IOException { private void writeObject(ObjectOutputStream out) throws IOException {
// We have to write serialized fields first. // We have to write serialized fields first.
ObjectOutputStream.PutField pf = out.putFields(); ObjectOutputStream.PutField pf = out.putFields();
Expand All @@ -573,6 +610,7 @@ private void writeObject(ObjectOutputStream out) throws IOException {
pf.put("sourceMethodName", sourceMethodName); pf.put("sourceMethodName", sourceMethodName);
pf.put("message", message); pf.put("message", message);
pf.put("threadID", threadID); pf.put("threadID", threadID);
pf.put("longThreadID", longThreadID);
pf.put("millis", instant.toEpochMilli()); pf.put("millis", instant.toEpochMilli());
pf.put("nanoAdjustment", instant.getNano() % 1000_000); pf.put("nanoAdjustment", instant.getNano() % 1000_000);
pf.put("thrown", thrown); pf.put("thrown", thrown);
Expand All @@ -594,16 +632,40 @@ private void writeObject(ObjectOutputStream out) throws IOException {
} }
} }


/**
* Initializes the LogRecord from deserialized data.
* <ul>
* <li>If {@code longThreadID} is present in the serial form, its value
* takes precedence over {@code threadID} and a value for {@code threadID}
* is synthesized from it, such that for {@code longThreadID} values between
* {@code 0} and {@code Integer.MAX_VALUE} inclusive, {@code longThreadID}
* and {@code threadID} will have the same value. For values outside of this
* range a negative synthesized value will be deterministically derived
* from {@code longThreadID}.
* <li>Otherwise, when only {@code threadID} is
* present, {@code longThreadID} is initialized with the value of
* {@code threadID} which may be anything between {@code Integer.MIN_VALUE}
* and {Integer.MAX_VALUE}.
* </ul>
*/
@Serial
private void readObject(ObjectInputStream in) private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException { throws IOException, ClassNotFoundException {
// We have to read serialized fields first. // We have to read serialized fields first.
ObjectInputStream.GetField gf = in.readFields(); ObjectInputStream.GetField gf = in.readFields();
level = (Level) gf.get("level", null); level = (Level) gf.get("level", null);
sequenceNumber = gf.get("sequenceNumber", 0L); sequenceNumber = gf.get("sequenceNumber", 0L);
sourceClassName = (String) gf.get("sourceClassName", null); sourceClassName = (String) gf.get("sourceClassName", null);
sourceMethodName = (String) gf.get("sourceMethodName", null); sourceMethodName = (String) gf.get("sourceMethodName", null);
message = (String) gf.get("message", null); message = (String) gf.get("message", null);
threadID = gf.get("threadID", 0); // If longthreadID is not present, it will be initialised with threadID value
// If longthreadID is present, threadID might have a synthesized value
int threadID = gf.get("threadID", 0);
long longThreadID = gf.get("longThreadID", (long)threadID);
if (threadID != longThreadID)
threadID = shortThreadID(longThreadID);
this.threadID = threadID;
this.longThreadID = longThreadID;
long millis = gf.get("millis", 0L); long millis = gf.get("millis", 0L);
int nanoOfMilli = gf.get("nanoAdjustment", 0); int nanoOfMilli = gf.get("nanoAdjustment", 0);
instant = Instant.ofEpochSecond( instant = Instant.ofEpochSecond(
Expand Down Expand Up @@ -641,9 +703,9 @@ private void readObject(ObjectInputStream in)
// use system class loader to ensure the ResourceBundle // use system class loader to ensure the ResourceBundle
// instance is a different instance than null loader uses // instance is a different instance than null loader uses
final ResourceBundle bundle = final ResourceBundle bundle =
ResourceBundle.getBundle(resourceBundleName, ResourceBundle.getBundle(resourceBundleName,
Locale.getDefault(), Locale.getDefault(),
ClassLoader.getSystemClassLoader()); ClassLoader.getSystemClassLoader());
resourceBundle = bundle; resourceBundle = bundle;
} catch (MissingResourceException ex) { } catch (MissingResourceException ex) {
// This is not a good place to throw an exception, // This is not a good place to throw an exception,
Expand Down Expand Up @@ -697,7 +759,7 @@ static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
private static final StackWalker WALKER; private static final StackWalker WALKER;
static { static {
final PrivilegedAction<StackWalker> action = final PrivilegedAction<StackWalker> action =
() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); () -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
WALKER = AccessController.doPrivileged(action); WALKER = AccessController.doPrivileged(action);
} }


Expand Down Expand Up @@ -736,7 +798,7 @@ public boolean test(StackWalker.StackFrame t) {


private boolean isLoggerImplFrame(String cname) { private boolean isLoggerImplFrame(String cname) {
return (cname.equals("java.util.logging.Logger") || return (cname.equals("java.util.logging.Logger") ||
cname.startsWith("sun.util.logging.PlatformLogger")); cname.startsWith("sun.util.logging.PlatformLogger"));
} }
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -219,7 +219,7 @@ public String format(LogRecord record) {
} }


sb.append(" <thread>"); sb.append(" <thread>");
sb.append(record.getThreadID()); sb.append(record.getLongThreadID());
sb.append("</thread>\n"); sb.append("</thread>\n");


if (record.getMessage() != null) { if (record.getMessage() != null) {
Expand Down
86 changes: 86 additions & 0 deletions test/jdk/java/util/logging/LogRecordThreadIdTest.java
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/



/*
* @test
* @bug 8245302
* @summary test the relationship between
* thread id long and int methods
* @build LogRecordThreadIdTest
* @run testng/othervm LogRecordThreadIdTest
*/

import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;



public class LogRecordThreadIdTest {

LogRecord record, record1, record2;

@BeforeTest
public void setUp() throws Exception {
record = new LogRecord(Level.INFO, "record");
record1 = new LogRecord(Level.INFO, "record1");
record2 = new LogRecord(Level.INFO, "record2");
}

/**
* Tests threadID setter methods for consistency
* with longThreadID
*/
@Test
public void testSetThreadId() {
record.setThreadID(Integer.MAX_VALUE - 20);
record1.setThreadID(Integer.MAX_VALUE - 1);
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
assertEquals(record1.getThreadID(), Integer.MAX_VALUE - 1);
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE - 1);
}

/**
* Tests longThreadID methods for consistency
* with threadID
*/
@Test
public void testSetLongThreadId() {
record.setLongThreadID(Integer.MAX_VALUE - 20L);
record1.setLongThreadID(Integer.MAX_VALUE + 10L);
record2.setLongThreadID(Integer.MAX_VALUE);
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
assertNotEquals(record1.getThreadID(), Integer.MAX_VALUE + 10L);
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE + 10L);
assertEquals(record2.getThreadID(), Integer.MAX_VALUE);
assertEquals(record2.getLongThreadID(), Integer.MAX_VALUE);

}
}

0 comments on commit a46a119

Please sign in to comment.