11/*
2- * Copyright (c) 2000, 2015 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2000, 2020 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2626package java .util .logging ;
2727import java .time .Instant ;
2828import java .util .*;
29- import java .util .concurrent .atomic .AtomicInteger ;
3029import java .util .concurrent .atomic .AtomicLong ;
3130import java .io .*;
3231import java .security .AccessController ;
@@ -75,21 +74,6 @@ public class LogRecord implements java.io.Serializable {
7574 private static final AtomicLong globalSequenceNumber
7675 = new AtomicLong (0 );
7776
78- /**
79- * The default value of threadID will be the current thread's
80- * thread id, for ease of correlation, unless it is greater than
81- * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
82- * our promise to keep threadIDs unique by avoiding collisions due
83- * to 32-bit wraparound. Unfortunately, LogRecord.getThreadID()
84- * returns int, while Thread.getId() returns long.
85- */
86- private static final int MIN_SEQUENTIAL_THREAD_ID = Integer .MAX_VALUE / 2 ;
87-
88- private static final AtomicInteger nextThreadId
89- = new AtomicInteger (MIN_SEQUENTIAL_THREAD_ID );
90-
91- private static final ThreadLocal <Integer > threadIds = new ThreadLocal <>();
92-
9377 /**
9478 * Logging message level
9579 */
@@ -120,6 +104,11 @@ public class LogRecord implements java.io.Serializable {
120104 */
121105 private int threadID ;
122106
107+ /**
108+ * long value of Thread ID for thread that issued logging call.
109+ */
110+ private long longThreadID ;
111+
123112 /**
124113 * The Throwable (if any) associated with log message
125114 */
@@ -147,7 +136,10 @@ public class LogRecord implements java.io.Serializable {
147136 * @serialField sourceClassName String Class that issued logging call
148137 * @serialField sourceMethodName String Method that issued logging call
149138 * @serialField message String Non-localized raw message text
150- * @serialField threadID int Thread ID for thread that issued logging call
139+ * @serialField threadID int this is deprecated and is available for backward compatibility.
140+ * Values may have been synthesized. If present, {@code longThreadID} represents
141+ * the actual thread id.
142+ * @serialField longThreadID long Thread ID for thread that issued logging call
151143 * @serialField millis long Truncated event time in milliseconds since 1970
152144 * - calculated as getInstant().toEpochMilli().
153145 * The event time instant can be reconstructed using
@@ -164,6 +156,7 @@ public class LogRecord implements java.io.Serializable {
164156 * @serialField resourceBundleName String Resource bundle name to localized
165157 * log message
166158 */
159+ @ Serial
167160 private static final ObjectStreamField [] serialPersistentFields =
168161 new ObjectStreamField [] {
169162 new ObjectStreamField ("level" , Level .class ),
@@ -172,6 +165,7 @@ public class LogRecord implements java.io.Serializable {
172165 new ObjectStreamField ("sourceMethodName" , String .class ),
173166 new ObjectStreamField ("message" , String .class ),
174167 new ObjectStreamField ("threadID" , int .class ),
168+ new ObjectStreamField ("longThreadID" , long .class ),
175169 new ObjectStreamField ("millis" , long .class ),
176170 new ObjectStreamField ("nanoAdjustment" , int .class ),
177171 new ObjectStreamField ("thrown" , Throwable .class ),
@@ -184,20 +178,22 @@ public class LogRecord implements java.io.Serializable {
184178 private transient ResourceBundle resourceBundle ;
185179
186180 /**
187- * Returns the default value for a new LogRecord's threadID.
181+ * Synthesizes a pseudo unique integer value from a long {@code id} value.
182+ * For backward compatibility with previous releases,the returned integer is
183+ * such that for any positive long less than or equals to {@code Integer.MAX_VALUE},
184+ * the returned integer is equal to the original value.
185+ * Otherwise - it is synthesized with a best effort hashing algorithm,
186+ * and the returned value is negative.
187+ * Calling this method multiple times with the same value always yields the same result.
188+ *
189+ * @return thread id
188190 */
189- private int defaultThreadID () {
190- long tid = Thread .currentThread ().getId ();
191- if (tid < MIN_SEQUENTIAL_THREAD_ID ) {
192- return (int ) tid ;
193- } else {
194- Integer id = threadIds .get ();
195- if (id == null ) {
196- id = nextThreadId .getAndIncrement ();
197- threadIds .set (id );
198- }
199- return id ;
200- }
191+
192+ private int shortThreadID (long id ) {
193+ if (id >= 0 && id <= Integer .MAX_VALUE )
194+ return (int ) id ;
195+ int hash = Long .hashCode (id );
196+ return hash < 0 ? hash : (-1 - hash );
201197 }
202198
203199 /**
@@ -225,10 +221,13 @@ public LogRecord(Level level, String msg) {
225221 message = msg ;
226222 // Assign a thread ID and a unique sequence number.
227223 sequenceNumber = globalSequenceNumber .getAndIncrement ();
228- threadID = defaultThreadID ();
224+ long id = Thread .currentThread ().getId ();
225+ // threadID is deprecated and this value is synthesised for backward compatibility
226+ threadID = shortThreadID (id );
227+ longThreadID = id ;
229228 instant = Instant .now ();
230229 needToInferCaller = true ;
231- }
230+ }
232231
233232 /**
234233 * Get the source Logger's name.
@@ -447,18 +446,54 @@ public void setParameters(Object parameters[]) {
447446 * This is a thread identifier within the Java VM and may or
448447 * may not map to any operating system ID.
449448 *
449+ * @deprecated Values returned by this method may be synthesized,
450+ * and may not correspond to the actual {@linkplain Thread#getId() thread id},
451+ * use {@link #getLongThreadID()} instead.
450452 * @return thread ID
451453 */
454+ @ Deprecated (since = "16" )
452455 public int getThreadID () {
453456 return threadID ;
454457 }
455458
456459 /**
457460 * Set an identifier for the thread where the message originated.
458461 * @param threadID the thread ID
462+ *
463+ * @deprecated This method doesn't allow to pass a long {@linkplain Thread#getId() thread id},
464+ * use {@link #setLongThreadID(long)} instead.
459465 */
466+ @ Deprecated (since = "16" )
460467 public void setThreadID (int threadID ) {
461468 this .threadID = threadID ;
469+ this .longThreadID = threadID ;
470+ }
471+
472+ /**
473+ * Get a thread identifier for the thread where message originated
474+ *
475+ * <p>
476+ * This is a thread identifier within the Java VM and may or
477+ * may not map to any operating system ID.
478+ *
479+ * @return thread ID
480+ * @since 16
481+ */
482+ public long getLongThreadID () {
483+ return longThreadID ;
484+ }
485+
486+ /**
487+ * Set an identifier for the thread where the message originated.
488+ *
489+ * @param longThreadID the thread ID
490+ * @return this LogRecord
491+ * @since 16
492+ */
493+ public LogRecord setLongThreadID (long longThreadID ) {
494+ this .threadID = shortThreadID (longThreadID );
495+ this .longThreadID = longThreadID ;
496+ return this ;
462497 }
463498
464499 /**
@@ -552,6 +587,7 @@ public void setThrown(Throwable thrown) {
552587 this .thrown = thrown ;
553588 }
554589
590+ @ Serial
555591 private static final long serialVersionUID = 5372048053134512534L ;
556592
557593 /**
@@ -564,6 +600,7 @@ public void setThrown(Throwable thrown) {
564600 * a null String is written. Otherwise the output of Object.toString()
565601 * is written.
566602 */
603+ @ Serial
567604 private void writeObject (ObjectOutputStream out ) throws IOException {
568605 // We have to write serialized fields first.
569606 ObjectOutputStream .PutField pf = out .putFields ();
@@ -573,6 +610,7 @@ private void writeObject(ObjectOutputStream out) throws IOException {
573610 pf .put ("sourceMethodName" , sourceMethodName );
574611 pf .put ("message" , message );
575612 pf .put ("threadID" , threadID );
613+ pf .put ("longThreadID" , longThreadID );
576614 pf .put ("millis" , instant .toEpochMilli ());
577615 pf .put ("nanoAdjustment" , instant .getNano () % 1000_000 );
578616 pf .put ("thrown" , thrown );
@@ -594,16 +632,40 @@ private void writeObject(ObjectOutputStream out) throws IOException {
594632 }
595633 }
596634
635+ /**
636+ * Initializes the LogRecord from deserialized data.
637+ * <ul>
638+ * <li>If {@code longThreadID} is present in the serial form, its value
639+ * takes precedence over {@code threadID} and a value for {@code threadID}
640+ * is synthesized from it, such that for {@code longThreadID} values between
641+ * {@code 0} and {@code Integer.MAX_VALUE} inclusive, {@code longThreadID}
642+ * and {@code threadID} will have the same value. For values outside of this
643+ * range a negative synthesized value will be deterministically derived
644+ * from {@code longThreadID}.
645+ * <li>Otherwise, when only {@code threadID} is
646+ * present, {@code longThreadID} is initialized with the value of
647+ * {@code threadID} which may be anything between {@code Integer.MIN_VALUE}
648+ * and {Integer.MAX_VALUE}.
649+ * </ul>
650+ */
651+ @ Serial
597652 private void readObject (ObjectInputStream in )
598- throws IOException , ClassNotFoundException {
653+ throws IOException , ClassNotFoundException {
599654 // We have to read serialized fields first.
600655 ObjectInputStream .GetField gf = in .readFields ();
601656 level = (Level ) gf .get ("level" , null );
602657 sequenceNumber = gf .get ("sequenceNumber" , 0L );
603658 sourceClassName = (String ) gf .get ("sourceClassName" , null );
604659 sourceMethodName = (String ) gf .get ("sourceMethodName" , null );
605660 message = (String ) gf .get ("message" , null );
606- threadID = gf .get ("threadID" , 0 );
661+ // If longthreadID is not present, it will be initialised with threadID value
662+ // If longthreadID is present, threadID might have a synthesized value
663+ int threadID = gf .get ("threadID" , 0 );
664+ long longThreadID = gf .get ("longThreadID" , (long )threadID );
665+ if (threadID != longThreadID )
666+ threadID = shortThreadID (longThreadID );
667+ this .threadID = threadID ;
668+ this .longThreadID = longThreadID ;
607669 long millis = gf .get ("millis" , 0L );
608670 int nanoOfMilli = gf .get ("nanoAdjustment" , 0 );
609671 instant = Instant .ofEpochSecond (
@@ -641,9 +703,9 @@ private void readObject(ObjectInputStream in)
641703 // use system class loader to ensure the ResourceBundle
642704 // instance is a different instance than null loader uses
643705 final ResourceBundle bundle =
644- ResourceBundle .getBundle (resourceBundleName ,
645- Locale .getDefault (),
646- ClassLoader .getSystemClassLoader ());
706+ ResourceBundle .getBundle (resourceBundleName ,
707+ Locale .getDefault (),
708+ ClassLoader .getSystemClassLoader ());
647709 resourceBundle = bundle ;
648710 } catch (MissingResourceException ex ) {
649711 // This is not a good place to throw an exception,
@@ -697,7 +759,7 @@ static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
697759 private static final StackWalker WALKER ;
698760 static {
699761 final PrivilegedAction <StackWalker > action =
700- () -> StackWalker .getInstance (StackWalker .Option .RETAIN_CLASS_REFERENCE );
762+ () -> StackWalker .getInstance (StackWalker .Option .RETAIN_CLASS_REFERENCE );
701763 WALKER = AccessController .doPrivileged (action );
702764 }
703765
@@ -736,7 +798,7 @@ public boolean test(StackWalker.StackFrame t) {
736798
737799 private boolean isLoggerImplFrame (String cname ) {
738800 return (cname .equals ("java.util.logging.Logger" ) ||
739- cname .startsWith ("sun.util.logging.PlatformLogger" ));
801+ cname .startsWith ("sun.util.logging.PlatformLogger" ));
740802 }
741803 }
742804}
0 commit comments