From 7f81b510d6b8049ceb1fc1dbf52128ebb9917f7a Mon Sep 17 00:00:00 2001 From: Jay Katariya <32147410+jaykataria1111@users.noreply.github.com> Date: Mon, 13 Jan 2025 03:56:57 -0800 Subject: [PATCH] Feat(SystemClock): Added PreciseClock implementation to the system clock class, for the benefit of Java 8 users. (#3217) Added PreciseClock implementation to the system clock class, for the benefit of Java 8 users. Co-authored-by: Jay Katariya Co-authored-by: Piotr P. Karwasz --- log4j-core-java9/src/assembly/java9.xml | 1 - .../logging/log4j/core/time/Instant.java | 77 --------- .../log4j/core/time/MutableInstant.java | 157 ------------------ .../logging/log4j/core/time/PreciseClock.java | 38 ----- .../apache/logging/log4j/core/util/Clock.java | 32 ---- .../logging/log4j/core/util/SystemClock.java | 46 ----- .../logging/log4j/core/util/SystemClock.java | 12 +- .../logging/log4j/core/util/package-info.java | 2 +- src/changelog/.2.x.x/3217_precise_clock.xml | 10 ++ .../ROOT/pages/manual/garbagefree.adoc | 2 + .../properties-log4j-core-misc.adoc | 21 ++- 11 files changed, 40 insertions(+), 358 deletions(-) delete mode 100644 log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/Instant.java delete mode 100644 log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/MutableInstant.java delete mode 100644 log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/PreciseClock.java delete mode 100644 log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java delete mode 100644 log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java create mode 100644 src/changelog/.2.x.x/3217_precise_clock.xml diff --git a/log4j-core-java9/src/assembly/java9.xml b/log4j-core-java9/src/assembly/java9.xml index c5016453189..4349d38f147 100644 --- a/log4j-core-java9/src/assembly/java9.xml +++ b/log4j-core-java9/src/assembly/java9.xml @@ -31,7 +31,6 @@ org/apache/logging/log4j/core/jackson/Log4jStackTraceElementDeserializer.class org/apache/logging/log4j/core/jackson/StackTraceElementMixIn.class org/apache/logging/log4j/core/util/internal/UnsafeUtil*.class - org/apache/logging/log4j/core/util/SystemClock.class diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/Instant.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/Instant.java deleted file mode 100644 index 5191bcea236..00000000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/Instant.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.time; - -// This class is here to allow {@link SystemClock}, {@link SystemMillisClock} -// to compile. It will not be copied into the log4j-core module. - -import org.apache.logging.log4j.core.util.Clock; - -/** - * Models a point in time, suitable for event timestamps. - *

- * Provides methods for obtaining high precision time information similar to the - * Instant class introduced in Java 8, - * while also supporting the legacy millisecond precision API. - *

- * Depending on the platform, time sources ({@link Clock} implementations) may produce high precision or millisecond - * precision time values. At the same time, some time value consumers (for example timestamp formatters) may only be - * able to consume time values of millisecond precision, while some others may require a high precision time value. - *

- * This class bridges these two time APIs. - *

- * @since 2.11 - */ -public interface Instant { - /** - * Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z. - *

- * The epoch second count is a simple incrementing count of seconds where second 0 is 1970-01-01T00:00:00Z. - * The nanosecond part of the day is returned by {@link #getNanoOfSecond()}. - *

- * @return the seconds from the epoch of 1970-01-01T00:00:00Z - */ - long getEpochSecond(); - - /** - * Gets the number of nanoseconds, later along the time-line, from the start of the second. - *

- * The nanosecond-of-second value measures the total number of nanoseconds from the second returned by {@link #getEpochSecond()}. - *

- * @return the nanoseconds within the second, always positive, never exceeds {@code 999,999,999} - */ - int getNanoOfSecond(); - - /** - * Gets the number of milliseconds from the Java epoch of 1970-01-01T00:00:00Z. - *

- * The epoch millisecond count is a simple incrementing count of milliseconds where millisecond 0 is 1970-01-01T00:00:00Z. - * The nanosecond part of the day is returned by {@link #getNanoOfMillisecond()}. - *

- * @return the milliseconds from the epoch of 1970-01-01T00:00:00Z - */ - long getEpochMillisecond(); - - /** - * Gets the number of nanoseconds, later along the time-line, from the start of the millisecond. - *

- * The nanosecond-of-millisecond value measures the total number of nanoseconds from the millisecond returned by {@link #getEpochMillisecond()}. - *

- * @return the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999} - */ - int getNanoOfMillisecond(); -} diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/MutableInstant.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/MutableInstant.java deleted file mode 100644 index 674843ed0eb..00000000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/MutableInstant.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.time; - -import java.io.Serializable; -import org.apache.logging.log4j.core.util.Clock; -import org.apache.logging.log4j.util.PerformanceSensitive; - -// This class is here to allow {@link SystemClock}, {@link SystemMillisClock} -// to compile. It will not be copied into the log4j-core module. - -/** - * An instantaneous point on the time line, used for high-precision log event timestamps. - * Modeled on java.time.Instant, - * except that this version is mutable to prevent allocating temporary objects that need to be garbage-collected later. - *

- * Instances of this class are not thread-safe and should not be shared between threads. - *

- * - * @since 2.11 - */ -@PerformanceSensitive("allocation") -public class MutableInstant implements Instant, Serializable { - - private static final int MILLIS_PER_SECOND = 1000; - private static final int NANOS_PER_MILLI = 1000_000; - static final int NANOS_PER_SECOND = MILLIS_PER_SECOND * NANOS_PER_MILLI; - - private long epochSecond; - private int nanoOfSecond; - - @Override - public long getEpochSecond() { - return epochSecond; - } - - @Override - public int getNanoOfSecond() { - return nanoOfSecond; - } - - @Override - public long getEpochMillisecond() { - final int millis = nanoOfSecond / NANOS_PER_MILLI; - final long epochMillisecond = epochSecond * MILLIS_PER_SECOND + millis; - return epochMillisecond; - } - - @Override - public int getNanoOfMillisecond() { - final int millis = nanoOfSecond / NANOS_PER_MILLI; - final int nanoOfMillisecond = - nanoOfSecond - (millis * NANOS_PER_MILLI); // cheaper than nanoOfSecond % NANOS_PER_MILLI - return nanoOfMillisecond; - } - - public void initFrom(final Instant other) { - this.epochSecond = other.getEpochSecond(); - this.nanoOfSecond = other.getNanoOfSecond(); - } - - /** - * Updates the fields of this {@code MutableInstant} from the specified epoch millis. - * @param epochMilli the number of milliseconds from the Java epoch of 1970-01-01T00:00:00Z - * @param nanoOfMillisecond the number of nanoseconds, later along the time-line, from the start of the millisecond - */ - public void initFromEpochMilli(final long epochMilli, final int nanoOfMillisecond) { - validateNanoOfMillisecond(nanoOfMillisecond); - this.epochSecond = epochMilli / MILLIS_PER_SECOND; - this.nanoOfSecond = - (int) (epochMilli - (epochSecond * MILLIS_PER_SECOND)) * NANOS_PER_MILLI + nanoOfMillisecond; - } - - private void validateNanoOfMillisecond(final int nanoOfMillisecond) { - if (nanoOfMillisecond < 0 || nanoOfMillisecond >= NANOS_PER_MILLI) { - throw new IllegalArgumentException("Invalid nanoOfMillisecond " + nanoOfMillisecond); - } - } - - public void initFrom(final Clock clock) { - if (clock instanceof PreciseClock) { - ((PreciseClock) clock).init(this); - } else { - initFromEpochMilli(clock.currentTimeMillis(), 0); - } - } - - /** - * Updates the fields of this {@code MutableInstant} from the specified instant components. - * @param epochSecond the number of seconds from the Java epoch of 1970-01-01T00:00:00Z - * @param nano the number of nanoseconds, later along the time-line, from the start of the second - */ - public void initFromEpochSecond(final long epochSecond, final int nano) { - validateNanoOfSecond(nano); - this.epochSecond = epochSecond; - this.nanoOfSecond = nano; - } - - private void validateNanoOfSecond(final int nano) { - if (nano < 0 || nano >= NANOS_PER_SECOND) { - throw new IllegalArgumentException("Invalid nanoOfSecond " + nano); - } - } - - /** - * Updates the elements of the specified {@code long[]} result array from the specified instant components. - * @param epochSecond (input) the number of seconds from the Java epoch of 1970-01-01T00:00:00Z - * @param nano (input) the number of nanoseconds, later along the time-line, from the start of the second - * @param result (output) a two-element array to store the result: the first element is the number of milliseconds - * from the Java epoch of 1970-01-01T00:00:00Z, - * the second element is the number of nanoseconds, later along the time-line, from the start of the millisecond - */ - public static void instantToMillisAndNanos(final long epochSecond, final int nano, final long[] result) { - final int millis = nano / NANOS_PER_MILLI; - result[0] = epochSecond * MILLIS_PER_SECOND + millis; - result[1] = nano - (millis * NANOS_PER_MILLI); // cheaper than nanoOfSecond % NANOS_PER_MILLI - } - - @Override - public boolean equals(final Object object) { - if (object == this) { - return true; - } - if (!(object instanceof MutableInstant)) { - return false; - } - final MutableInstant other = (MutableInstant) object; - return epochSecond == other.epochSecond && nanoOfSecond == other.nanoOfSecond; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (int) (epochSecond ^ (epochSecond >>> 32)); - result = 31 * result + nanoOfSecond; - return result; - } - - @Override - public String toString() { - return "MutableInstant[epochSecond=" + epochSecond + ", nano=" + nanoOfSecond + "]"; - } -} diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/PreciseClock.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/PreciseClock.java deleted file mode 100644 index 9207507d4da..00000000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/time/PreciseClock.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.time; - -// This class is here to allow {@link SystemClock}, {@link SystemMillisClock} -// to compile. It will not be copied into the log4j-core module. - -import org.apache.logging.log4j.core.util.Clock; - -/** - * Extension of the {@link Clock} interface that is able to provide more accurate time information than milliseconds - * since the epoch. {@code PreciseClock} implementations are free to return millisecond-precision time - * if that is the most accurate time information available on this platform. - * @since 2.11 - */ -public interface PreciseClock extends Clock { - - /** - * Initializes the specified instant with time information as accurate as available on this platform. - * @param mutableInstant the container to be initialized with the accurate time information - * @since 2.11 - */ - void init(final MutableInstant mutableInstant); -} diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java deleted file mode 100644 index bf5a8f77fd1..00000000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.util; - -// This class is here to allow {@link SystemClock}, {@link SystemMillisClock} -// to compile. It will not be copied into the log4j-core module. - -/** - * Provides the time stamp used in log events. - */ -public interface Clock { - /** - * Returns the time in milliseconds since the epoch. - * - * @return the time in milliseconds since the epoch - */ - long currentTimeMillis(); -} diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java deleted file mode 100644 index 3a884224aaa..00000000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.util; - -import java.time.Instant; -import org.apache.logging.log4j.core.time.MutableInstant; -import org.apache.logging.log4j.core.time.PreciseClock; - -/** - * Implementation of the {@code Clock} interface that returns the system time. - * @since 2.11 - */ -public final class SystemClock implements Clock, PreciseClock { - - /** - * Returns the system time. - * @return the result of calling {@code System.currentTimeMillis()} - */ - @Override - public long currentTimeMillis() { - return System.currentTimeMillis(); - } - - /** - * {@inheritDoc} - */ - @Override - public void init(final MutableInstant mutableInstant) { - final Instant instant = java.time.Clock.systemUTC().instant(); - mutableInstant.initFromEpochSecond(instant.getEpochSecond(), instant.getNano()); - } -} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java index e1a9cd2a09a..7ded3499d64 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java @@ -16,10 +16,14 @@ */ package org.apache.logging.log4j.core.util; +import java.time.Instant; +import org.apache.logging.log4j.core.time.MutableInstant; +import org.apache.logging.log4j.core.time.PreciseClock; + /** * Implementation of the {@code Clock} interface that returns the system time. */ -public final class SystemClock implements Clock { +public final class SystemClock implements Clock, PreciseClock { /** * Returns the system time. @@ -29,4 +33,10 @@ public final class SystemClock implements Clock { public long currentTimeMillis() { return System.currentTimeMillis(); } + + @Override + public void init(final MutableInstant mutableInstant) { + final Instant instant = java.time.Clock.systemUTC().instant(); + mutableInstant.initFromEpochSecond(instant.getEpochSecond(), instant.getNano()); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java index 06e847dcfef..932e8bc0800 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java @@ -18,7 +18,7 @@ * Log4j 2 helper classes. */ @Export -@Version("2.24.2") +@Version("2.25.0") package org.apache.logging.log4j.core.util; import org.osgi.annotation.bundle.Export; diff --git a/src/changelog/.2.x.x/3217_precise_clock.xml b/src/changelog/.2.x.x/3217_precise_clock.xml new file mode 100644 index 00000000000..e35dc3f1682 --- /dev/null +++ b/src/changelog/.2.x.x/3217_precise_clock.xml @@ -0,0 +1,10 @@ + + + + Currently Java 8 users do not have access to timestamps with sub-millisecond precision. + This change moves the SystemClock implementation from log4j-core-java9 to log4j-core, overriding the current one, so that java 8 users can benefit from it. + + diff --git a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc index fb6cba10f78..e6d29369f81 100644 --- a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc @@ -71,6 +71,8 @@ include::partial$manual/systemproperties/properties-garbage-collection.adoc[leve include::partial$manual/systemproperties/properties-thread-context-core.adoc[leveloffset=+2,tag=gcfree] +include::partial$manual/systemproperties/properties-log4j-core-misc.adoc[leveloffset=+2,tag=clock] + [#Layouts] === Layouts diff --git a/src/site/antora/modules/ROOT/partials/manual/systemproperties/properties-log4j-core-misc.adoc b/src/site/antora/modules/ROOT/partials/manual/systemproperties/properties-log4j-core-misc.adoc index beae1c22810..e9a44de433b 100644 --- a/src/site/antora/modules/ROOT/partials/manual/systemproperties/properties-log4j-core-misc.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/systemproperties/properties-log4j-core-misc.adoc @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. //// +// tag::clock[] [id=log4j2.clock] == `log4j2.clock` @@ -24,26 +25,36 @@ | Default value | ``SystemClock`` |=== -Specifies the +It specifies the link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/Clock.html[`Clock`] implementation used to timestamp log events. This must be the fully qualified class name of the implementation or one of these predefined constants: -SystemClock:: uses the best available system clock as time source. +SystemClock:: It uses the best available system time source. See https://docs.oracle.com/javase/{java-target-version}/docs/api/java/time/Clock.html#systemDefaultZone--[`Clock#systemDefaultZone()`] for details. ++ +[NOTE] +==== +Depending on the version of the JRE, this implementation might not be garbage-free or might only become garbage-free when the code is hot enough. + +If you don't require a nanosecond precision, and you need a garbage-free implementation, use <>. +==== -SystemMillisClock:: same as `SystemClock`, but truncates the result to a millisecond. +[id=log4j2.clock.SystemMillisClock] +SystemMillisClock:: It is similar to `SystemClock`, but truncates the result to a millisecond. +This implementation is garbage-free. -CachedClock:: uses a separate thread to update the timestamp value. +CachedClock:: It uses a separate thread to update the timestamp value. See link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/CachedClock.html[`CachedClock`] for details. -CoarseCachedClock:: alternative implementation of `CachedClock` with a slightly lower precision. +CoarseCachedClock:: This is an alternative implementation of `CachedClock` with a slightly lower precision. See link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/CoarseCachedClock.html[`CoarseCachedClock`] for details. +// end::clock[] [id=log4j2.contextData] == `log4j2.contextData`