LOG4J2-1883 replace timeMillis with Instant/MutableInstant in LogEvent; support formatting date times in higher precision than milliseconds; add Java 9 precise time source; update docs
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e3a126ee Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e3a126ee Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e3a126ee Branch: refs/heads/master Commit: e3a126ee14830e7a67c1cd405a2b27b331c7981e Parents: 6b31ab3 Author: rpopma <[email protected]> Authored: Sun Jan 14 13:48:25 2018 +0900 Committer: rpopma <[email protected]> Committed: Sun Jan 28 19:46:17 2018 +0900 ---------------------------------------------------------------------- log4j-core-java9/pom.xml | 11 +- log4j-core-java9/src/assembly/java9.xml | 8 +- .../apache/logging/log4j/core/util/Clock.java | 32 +++ .../apache/logging/log4j/core/util/Dummy.java | 24 -- .../apache/logging/log4j/core/util/Instant.java | 75 ++++++ .../logging/log4j/core/util/MutableInstant.java | 155 +++++++++++++ .../logging/log4j/core/util/PreciseClock.java | 36 +++ .../logging/log4j/core/util/SystemClock.java | 44 ++++ .../log4j/core/util/SystemMillisClock.java | 34 +++ .../apache/logging/log4j/core/util/Temp.java | 24 -- .../logging/log4j/core/AbstractLogEvent.java | 9 + .../org/apache/logging/log4j/core/LogEvent.java | 17 +- .../converter/InstantAttributeConverter.java | 58 +++++ .../logging/log4j/core/async/AsyncLogger.java | 6 +- .../log4j/core/async/RingBufferLogEvent.java | 34 ++- .../async/RingBufferLogEventTranslator.java | 22 +- .../logging/log4j/core/impl/Log4jLogEvent.java | 77 ++++--- .../log4j/core/impl/MutableLogEvent.java | 33 ++- .../core/impl/ReusableLogEventFactory.java | 6 +- .../log4j/core/jackson/Initializers.java | 3 + .../log4j/core/jackson/InstantMixIn.java | 55 +++++ .../log4j/core/jackson/JsonConstants.java | 1 + .../log4j/core/jackson/LogEventJsonMixIn.java | 13 +- .../jackson/LogEventWithContextListMixIn.java | 13 +- .../log4j/core/jackson/XmlConstants.java | 1 + .../core/pattern/DatePatternConverter.java | 100 +++++--- .../logging/log4j/core/util/ClockFactory.java | 27 ++- .../log4j/core/util/DummyPreciseClock.java | 49 ++++ .../apache/logging/log4j/core/util/Instant.java | 72 ++++++ .../logging/log4j/core/util/MutableInstant.java | 152 ++++++++++++ .../logging/log4j/core/util/PreciseClock.java | 33 +++ .../core/util/datetime/FixedDateFormat.java | 122 +++++++++- .../appender/JsonCompleteFileAppenderTest.java | 75 +++--- .../appender/XmlCompleteFileAppenderTest.java | 115 ++++------ .../core/appender/XmlFileAppenderTest.java | 40 ++-- .../InstantAttributeConverterTest.java | 68 ++++++ .../async/AsyncLoggerTimestampMessageTest.java | 2 +- .../core/async/RingBufferLogEventTest.java | 28 +-- .../ConcurrentLoggingWithJsonLayoutTest.java | 2 +- .../log4j/core/layout/JsonLayoutTest.java | 2 +- .../log4j/core/layout/XmlLayoutTest.java | 5 +- .../log4j/core/layout/YamlLayoutTest.java | 2 +- .../core/parser/JsonLogEventParserTest.java | 3 +- .../log4j/core/parser/LogEventParserTest.java | 2 +- .../core/parser/XmlLogEventParserTest.java | 6 + .../core/parser/YamlLogEventParserTest.java | 10 +- .../core/pattern/DatePatternConverterTest.java | 74 ++++++ .../log4j/core/util/MutableInstantTest.java | 230 +++++++++++++++++++ .../core/util/datetime/FixedDateFormatTest.java | 15 ++ .../log4j/flume/appender/FlumeEvent.java | 10 + .../db/jpa/AbstractLogEventWrapperEntity.java | 21 ++ .../appender/db/jpa/BasicLogEventEntity.java | 15 +- .../core/appender/db/jpa/JpaH2AppenderTest.java | 11 +- .../appender/db/jpa/JpaHsqldbAppenderTest.java | 11 +- .../appender/db/jpa/LogEventEntityTest.java | 7 + .../core/appender/db/jpa/TestBaseEntity.java | 9 + .../src/test/resources/META-INF/persistence.xml | 2 + .../src/main/resources/META-INF/persistence.xml | 2 + src/site/xdoc/manual/layouts.xml.vm | 50 +++- 59 files changed, 1816 insertions(+), 347 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/pom.xml ---------------------------------------------------------------------- diff --git a/log4j-core-java9/pom.xml b/log4j-core-java9/pom.xml index e05f30e..b46f8c9 100644 --- a/log4j-core-java9/pom.xml +++ b/log4j-core-java9/pom.xml @@ -33,6 +33,11 @@ <projectDir>/core</projectDir> </properties> <dependencies> + <!-- Naturally, all implementations require the log4j-api JAR --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> @@ -84,12 +89,6 @@ </goals> </execution> </executions> - <configuration> - <source>9</source> - <target>9</target> - <release>9</release> - <proc>none</proc> - </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/assembly/java9.xml ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/assembly/java9.xml b/log4j-core-java9/src/assembly/java9.xml index 3e59561..f8f129d 100644 --- a/log4j-core-java9/src/assembly/java9.xml +++ b/log4j-core-java9/src/assembly/java9.xml @@ -34,10 +34,10 @@ <excludes> <exclude>module-info.class</exclude> <exclude>**/Dummy.class</exclude> - <exclude>**/spi/Provider.class</exclude> - <exclude>**/util/PropertySource.class</exclude> - <exclude>**/message/ThreadDumpMessage.class</exclude> - <exclude>**/message/ThreadDumpMessage$ThreadInfoFactory.class</exclude> + <exclude>**/util/Clock.class</exclude> + <exclude>**/util/Instant.class</exclude> + <exclude>**/util/MutableInstant.class</exclude> + <exclude>**/util/PreciseClock.class</exclude> </excludes> </fileSet> <fileSet> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..8c961d4 --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Clock.java @@ -0,0 +1,32 @@ +/* + * 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(); +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Dummy.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Dummy.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Dummy.java deleted file mode 100644 index 6bffac6..0000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Dummy.java +++ /dev/null @@ -1,24 +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 is a dummy class and is only here to allow module-info.java to compile. It will not - * be copied into the log4j-api module. - */ -public class Dummy { -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Instant.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Instant.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Instant.java new file mode 100644 index 0000000..cd4ac9d --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Instant.java @@ -0,0 +1,75 @@ +/* + * 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. + +/** + * Models a point in time, suitable for event timestamps. + * <p> + * Provides methods for obtaining high precision time information similar to the + * <a href="https://docs.oracle.com/javase/9/docs/api/java/time/Instant.html">Instant</a> class introduced in Java 8, + * while also supporting the legacy millisecond precision API. + * </p><p> + * 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. + * </p><p> + * This class bridges these two time APIs. + * </p> + * @since 2.11 + */ +public interface Instant { + /** + * Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z. + * <p> + * 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()}. + * </p> + * @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. + * <p> + * The nanosecond-of-second value measures the total number of nanoseconds from the second returned by {@link #getEpochSecond()}. + * </p> + * @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. + * <p> + * 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()}. + * </p> + * @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. + * <p> + * The nanosecond-of-millisecond value measures the total number of nanoseconds from the millisecond returned by {@link #getEpochMillisecond()}. + * </p> + * @return the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999} + */ + int getNanoOfMillisecond(); +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java new file mode 100644 index 0000000..8960fc5 --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/MutableInstant.java @@ -0,0 +1,155 @@ +/* + * 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 org.apache.logging.log4j.util.PerformanceSensitive; + +import java.io.Serializable; + +// 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. + * Modelled on <a href="https://docs.oracle.com/javase/9/docs/api/index.html?java/time/class-use/Instant.html">java.time.Instant</a>, + * except that this version is mutable to prevent allocating temporary objects that need to be garbage-collected later. + * <p> + * Instances of this class are <em>not</em> thread-safe and should not be shared between threads. + * </p> + * + * @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; + long epochMillisecond = epochSecond * MILLIS_PER_SECOND + millis; + return epochMillisecond; + } + + @Override + public int getNanoOfMillisecond() { + final int millis = nanoOfSecond / NANOS_PER_MILLI; + 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) { + 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; + } + 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 + "]"; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java new file mode 100644 index 0000000..3d1f314 --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/PreciseClock.java @@ -0,0 +1,36 @@ +/* + * 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. + +/** + * 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); +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..a74a1fd --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemClock.java @@ -0,0 +1,44 @@ +/* + * 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; + +/** + * 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(MutableInstant mutableInstant) { + Instant instant = java.time.Clock.systemUTC().instant(); + mutableInstant.initFromEpochSecond(instant.getEpochSecond(), instant.getNano()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemMillisClock.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemMillisClock.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemMillisClock.java new file mode 100644 index 0000000..f267320 --- /dev/null +++ b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/SystemMillisClock.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * Implementation of the {@code Clock} interface that returns the system time in millisecond granularity. + * @since 2.11 + */ +public final class SystemMillisClock implements Clock { + + /** + * Returns the system time. + * @return the result of calling {@code System.currentTimeMillis()} + */ + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Temp.java ---------------------------------------------------------------------- diff --git a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Temp.java b/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Temp.java deleted file mode 100644 index edb6d47..0000000 --- a/log4j-core-java9/src/main/java/org/apache/logging/log4j/core/util/Temp.java +++ /dev/null @@ -1,24 +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; - -/** - * Temporary class to ensure the log4j-core-java9 jar has at least one class (or Maven will refuse to proceed). - * To be removed as soon as real classes are added to the log4j-core-java9 module. - */ -public class Temp { -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java index 11cc071..bc7d1b4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java @@ -24,6 +24,8 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext.ContextStack; import org.apache.logging.log4j.core.impl.ThrowableProxy; +import org.apache.logging.log4j.core.util.Instant; +import org.apache.logging.log4j.core.util.MutableInstant; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; @@ -35,6 +37,8 @@ public abstract class AbstractLogEvent implements LogEvent { private static final long serialVersionUID = 1L; + private MutableInstant instant = new MutableInstant(); + /** * Subclasses should implement this method to provide an immutable version. */ @@ -122,6 +126,11 @@ public abstract class AbstractLogEvent implements LogEvent { } @Override + public Instant getInstant() { + return instant; + } + + @Override public boolean isEndOfBatch() { return false; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java index 41b20a7..0f72050 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java @@ -24,6 +24,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.impl.ThrowableProxy; +import org.apache.logging.log4j.core.util.Instant; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; @@ -124,13 +125,27 @@ public interface LogEvent extends Serializable { /** * Gets event time in milliseconds since midnight, January 1, 1970 UTC. + * Use {@link #getInstant()} to get higher precision timestamp information if available on this platform. * - * @return milliseconds since midnight, January 1, 1970 UTC. + * @return the milliseconds component of this log event's {@linkplain #getInstant() timestamp} * @see java.lang.System#currentTimeMillis() */ long getTimeMillis(); /** + * Returns the timestamp when the message was logged. + * <p> + * <b>Caution</b>: if this {@code LogEvent} implementation is mutable and reused for multiple consecutive log messages, + * then the {@code Instant} object returned by this method is also mutable and reused. + * Client code should not keep a reference to the returned object but make a copy instead. + * </p> + * + * @return the {@code Instant} holding timestamp details for this log event + * @since 2.11 + */ + Instant getInstant(); + + /** * Gets the source of logging request. * * @return source of logging request, may be null. http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverter.java new file mode 100644 index 0000000..408d972 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/InstantAttributeConverter.java @@ -0,0 +1,58 @@ +/* + * 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.appender.db.jpa.converter; + +import org.apache.logging.log4j.core.util.Instant; +import org.apache.logging.log4j.core.util.MutableInstant; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.Strings; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +/** + * A JPA 2.1 attribute converter for {@link Instant}s in {@link org.apache.logging.log4j.core.LogEvent}s. This + * converter is capable of converting both to and from {@link String}s. + */ +@Converter(autoApply = false) +public class InstantAttributeConverter implements AttributeConverter<Instant, String> { + private static final StatusLogger LOGGER = StatusLogger.getLogger(); + + @Override + public String convertToDatabaseColumn(final Instant instant) { + if (instant == null) { + return null; + } + return instant.getEpochSecond() + "," + instant.getNanoOfSecond(); + } + + @Override + public Instant convertToEntityAttribute(final String s) { + if (Strings.isEmpty(s)) { + return null; + } + + int pos = s.indexOf(","); + long epochSecond = Long.parseLong(s.substring(0, pos)); + int nanos = Integer.parseInt(s.substring(pos + 1, s.length())); + + MutableInstant result = new MutableInstant(); + result.initFromEpochSecond(epochSecond, nanos); + return result; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java index 036a130..358422c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java @@ -203,8 +203,8 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf // location (expensive to calculate) calcLocationIfRequested(fqcn), // - CLOCK.currentTimeMillis(), // - nanoClock.nanoTime() // + CLOCK, // + nanoClock // ); } @@ -293,7 +293,7 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf // in the AsyncLogger#actualAsyncLog method CONTEXT_DATA_INJECTOR.injectContextData(null, (StringMap) event.getContextData()), contextStack, currentThread.getId(), threadName, currentThread.getPriority(), location, - CLOCK.currentTimeMillis(), nanoClock.nanoTime()); + CLOCK, nanoClock); } /** http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java index b0b023b..af06e47 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java @@ -27,7 +27,7 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ContextDataFactory; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; -import org.apache.logging.log4j.core.util.Constants; +import org.apache.logging.log4j.core.util.*; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.message.ReusableMessage; @@ -70,7 +70,7 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen private int threadPriority; private long threadId; - private long currentTimeMillis; + private MutableInstant instant = new MutableInstant(); private long nanoTime; private short parameterCount; private boolean includeLocation; @@ -92,18 +92,18 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen private transient AsyncLogger asyncLogger; public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker, - final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, - final StringMap mutableContextData, final ContextStack aContextStack, final long threadId, - final String threadName, final int threadPriority, final StackTraceElement aLocation, - final long aCurrentTimeMillis, final long aNanoTime) { + final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, + final StringMap mutableContextData, final ContextStack aContextStack, final long threadId, + final String threadName, final int threadPriority, final StackTraceElement aLocation, + final Clock clock, final NanoClock nanoClock) { this.threadPriority = threadPriority; this.threadId = threadId; - this.currentTimeMillis = aCurrentTimeMillis; - this.nanoTime = aNanoTime; this.level = aLevel; this.threadName = threadName; this.loggerName = aLoggerName; setMessage(msg); + initTime(clock); + this.nanoTime = nanoClock.nanoTime(); this.thrown = aThrowable; this.thrownProxy = null; this.marker = aMarker; @@ -114,6 +114,14 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen this.asyncLogger = anAsyncLogger; } + private void initTime(final Clock clock) { + if (message instanceof TimestampMessage) { + instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0); + } else { + instant.initFrom(clock); + } + } + @Override public LogEvent toImmutable() { return createMemento(); @@ -367,7 +375,12 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen @Override public long getTimeMillis() { - return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :currentTimeMillis; + return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : instant.getEpochMillisecond(); + } + + @Override + public Instant getInstant() { + return instant; } @Override @@ -443,7 +456,8 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen .setThreadPriority(threadPriority) // .setThrown(getThrown()) // may deserialize from thrownProxy .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy - .setTimeMillis(currentTimeMillis); + .setInstant(instant) // + ; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java index cb3e6ca..ec2ff1c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java @@ -21,6 +21,8 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext.ContextStack; import org.apache.logging.log4j.core.ContextDataInjector; import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory; +import org.apache.logging.log4j.core.util.Clock; +import org.apache.logging.log4j.core.util.NanoClock; import org.apache.logging.log4j.util.StringMap; import org.apache.logging.log4j.message.Message; @@ -48,8 +50,8 @@ public class RingBufferLogEventTranslator implements private String threadName = Thread.currentThread().getName(); private int threadPriority = Thread.currentThread().getPriority(); private StackTraceElement location; - private long currentTimeMillis; - private long nanoTime; + private Clock clock; + private NanoClock nanoClock; // @Override @Override @@ -59,7 +61,7 @@ public class RingBufferLogEventTranslator implements // config properties are taken care of in the EventHandler thread // in the AsyncLogger#actualAsyncLog method injector.injectContextData(null, (StringMap) event.getContextData()), contextStack, - threadId, threadName, threadPriority, location, currentTimeMillis, nanoTime); + threadId, threadName, threadPriority, location, clock, nanoClock); clear(); // clear the translator } @@ -77,15 +79,15 @@ public class RingBufferLogEventTranslator implements null, // t null, // contextStack null, // location - 0, // currentTimeMillis - 0 // nanoTime + null, // clock + null // nanoClock ); } public void setBasicValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker, - final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, - final ContextStack aContextStack, final StackTraceElement aLocation, - final long aCurrentTimeMillis, final long aNanoTime) { + final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, + final ContextStack aContextStack, final StackTraceElement aLocation, + final Clock aClock, final NanoClock aNanoClock) { this.asyncLogger = anAsyncLogger; this.loggerName = aLoggerName; this.marker = aMarker; @@ -95,8 +97,8 @@ public class RingBufferLogEventTranslator implements this.thrown = aThrowable; this.contextStack = aContextStack; this.location = aLocation; - this.currentTimeMillis = aCurrentTimeMillis; - this.nanoTime = aNanoTime; + this.clock = aClock; + this.nanoClock = aNanoClock; } public void updateThreadValues() { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java index 74ddaf6..7332531 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java @@ -29,15 +29,12 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.ContextDataInjector; +import org.apache.logging.log4j.core.util.*; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.async.RingBufferLogEvent; import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.core.util.Clock; -import org.apache.logging.log4j.core.util.ClockFactory; -import org.apache.logging.log4j.core.util.DummyNanoClock; -import org.apache.logging.log4j.core.util.NanoClock; import org.apache.logging.log4j.message.LoggerNameAwareMessage; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ReusableMessage; @@ -63,7 +60,7 @@ public class Log4jLogEvent implements LogEvent { private final Level level; private final String loggerName; private Message message; - private final long timeMillis; + private final MutableInstant instant = new MutableInstant(); private final transient Throwable thrown; private ThrowableProxy thrownProxy; private final StringMap contextData; @@ -86,7 +83,7 @@ public class Log4jLogEvent implements LogEvent { private String loggerName; private Message message; private Throwable thrown; - private long timeMillis = CLOCK.currentTimeMillis(); + private MutableInstant instant = new MutableInstant(); private ThrowableProxy thrownProxy; private StringMap contextData = createContextData((List<Property>) null); private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack(); @@ -116,7 +113,7 @@ public class Log4jLogEvent implements LogEvent { this.level = other.getLevel(); this.loggerName = other.getLoggerName(); this.message = other.getMessage(); - this.timeMillis = other.getTimeMillis(); + this.instant.initFrom(other.getInstant()); this.thrown = other.getThrown(); this.contextStack = other.getContextStack(); this.includeLocation = other.isIncludeLocation(); @@ -183,7 +180,12 @@ public class Log4jLogEvent implements LogEvent { } public Builder setTimeMillis(final long timeMillis) { - this.timeMillis = timeMillis; + this.instant.initFromEpochMilli(timeMillis, 0); + return this; + } + + public Builder setInstant(final Instant instant) { + this.instant.initFrom(instant); return this; } @@ -256,13 +258,20 @@ public class Log4jLogEvent implements LogEvent { @Override public Log4jLogEvent build() { + initTimeFields(); final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown, - thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis, - nanoTime); + thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, + instant.getEpochMillisecond(), instant.getNanoOfMillisecond(), nanoTime); result.setIncludeLocation(includeLocation); result.setEndOfBatch(endOfBatch); return result; } + + private void initTimeFields() { + if (instant.getEpochMillisecond() == 0) { + instant.initFrom(CLOCK); + } + } } /** @@ -275,7 +284,7 @@ public class Log4jLogEvent implements LogEvent { public Log4jLogEvent() { this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null, - 0, null, CLOCK.currentTimeMillis(), nanoClock.nanoTime()); + 0, null, CLOCK.currentTimeMillis(), 0, nanoClock.nanoTime()); } /** @@ -285,7 +294,7 @@ public class Log4jLogEvent implements LogEvent { @Deprecated public Log4jLogEvent(final long timestamp) { this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null, - 0, null, timestamp, nanoClock.nanoTime()); + 0, null, timestamp, 0, nanoClock.nanoTime()); } /** @@ -325,7 +334,7 @@ public class Log4jLogEvent implements LogEvent { null, // LOG4J2-628 use log4j.Clock for timestamps // LOG4J2-744 unless TimestampMessage already has one message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : - CLOCK.currentTimeMillis(), nanoClock.nanoTime()); + CLOCK.currentTimeMillis(), 0, nanoClock.nanoTime()); } /** @@ -349,7 +358,7 @@ public class Log4jLogEvent implements LogEvent { final ThreadContext.ContextStack ndc, final String threadName, final StackTraceElement location, final long timestampMillis) { this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0, - threadName, 0, location, timestampMillis, nanoClock.nanoTime()); + threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime()); } /** @@ -377,7 +386,7 @@ public class Log4jLogEvent implements LogEvent { final String threadName, final StackTraceElement location, final long timestamp) { final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, - thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, nanoClock.nanoTime()); + thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime()); return result; } @@ -397,6 +406,7 @@ public class Log4jLogEvent implements LogEvent { * @param threadPriority the thread priority * @param source The locations of the caller. * @param timestampMillis The timestamp of the event. + * @param nanoOfMillisecond the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999} * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was * created. */ @@ -404,7 +414,7 @@ public class Log4jLogEvent implements LogEvent { final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, final String threadName, final int threadPriority, final StackTraceElement source, - final long timestampMillis, final long nanoTime) { + final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) { this.loggerName = loggerName; this.marker = marker; this.loggerFqcn = loggerFQCN; @@ -414,9 +424,10 @@ public class Log4jLogEvent implements LogEvent { this.thrownProxy = thrownProxy; this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData; this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; - this.timeMillis = message instanceof TimestampMessage + long millis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestampMillis; + this.instant.initFromEpochMilli(millis, nanoOfMillisecond); this.threadId = threadId; this.threadName = threadName; this.threadPriority = threadPriority; @@ -539,12 +550,20 @@ public class Log4jLogEvent implements LogEvent { } /** - * Returns the time in milliseconds from the epoch when the event occurred. - * @return The time the event occurred. + * {@inheritDoc} */ @Override public long getTimeMillis() { - return timeMillis; + return instant.getEpochMillisecond(); + } + + /** + * {@inheritDoc} + * @since 2.11 + */ + @Override + public Instant getInstant() { + return instant; } /** @@ -707,7 +726,8 @@ public class Log4jLogEvent implements LogEvent { final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker, proxy.loggerFQCN, proxy.level, proxy.message, proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId, - proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoTime); + proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond, + proxy.nanoTime); result.setEndOfBatch(proxy.isEndOfBatch); result.setIncludeLocation(proxy.isLocationRequired); return result; @@ -759,7 +779,7 @@ public class Log4jLogEvent implements LogEvent { if (includeLocation != that.includeLocation) { return false; } - if (timeMillis != that.timeMillis) { + if (!instant.equals(that.instant)) { return false; } if (nanoTime != that.nanoTime) { @@ -816,7 +836,7 @@ public class Log4jLogEvent implements LogEvent { result = 31 * result + (level != null ? level.hashCode() : 0); result = 31 * result + loggerName.hashCode(); result = 31 * result + message.hashCode(); - result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32)); + result = 31 * result + instant.hashCode(); result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32)); result = 31 * result + (thrown != null ? thrown.hashCode() : 0); result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); @@ -849,6 +869,8 @@ public class Log4jLogEvent implements LogEvent { /** since 2.8 */ private String messageString; private final long timeMillis; + /** since 2.11 */ + private final int nanoOfMillisecond; private final transient Throwable thrown; private final ThrowableProxy thrownProxy; /** @since 2.7 */ @@ -873,7 +895,8 @@ public class Log4jLogEvent implements LogEvent { this.message = event.message instanceof ReusableMessage ? memento((ReusableMessage) event.message) : event.message; - this.timeMillis = event.timeMillis; + this.timeMillis = event.instant.getEpochMillisecond(); + this.nanoOfMillisecond = event.instant.getNanoOfMillisecond(); this.thrown = event.thrown; this.thrownProxy = event.thrownProxy; this.contextData = event.contextData; @@ -897,7 +920,8 @@ public class Log4jLogEvent implements LogEvent { message = temp instanceof ReusableMessage ? memento((ReusableMessage) temp) : temp; - this.timeMillis = event.getTimeMillis(); + this.timeMillis = event.getInstant().getEpochMillisecond(); + this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond(); this.thrown = event.getThrown(); this.thrownProxy = event.getThrownProxy(); this.contextData = memento(event.getContextData()); @@ -942,7 +966,7 @@ public class Log4jLogEvent implements LogEvent { protected Object readResolve() { final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis, - nanoTime); + nanoOfMillisecond, nanoTime); result.setEndOfBatch(isEndOfBatch); result.setIncludeLocation(isLocationRequired); return result; @@ -959,5 +983,4 @@ public class Log4jLogEvent implements LogEvent { return new SimpleMessage(messageString); } } - } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java index acc31b3..ec807d3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java @@ -26,11 +26,8 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.async.InternalAsyncUtil; -import org.apache.logging.log4j.core.util.Constants; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.message.ReusableMessage; -import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.core.util.*; +import org.apache.logging.log4j.message.*; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.StringBuilders; @@ -46,7 +43,7 @@ public class MutableLogEvent implements LogEvent, ReusableMessage { private int threadPriority; private long threadId; - private long timeMillis; + private MutableInstant instant = new MutableInstant(); private long nanoTime; private short parameterCount; private boolean includeLocation; @@ -95,10 +92,11 @@ public class MutableLogEvent implements LogEvent, ReusableMessage { this.marker = event.getMarker(); this.level = event.getLevel(); this.loggerName = event.getLoggerName(); - this.timeMillis = event.getTimeMillis(); this.thrown = event.getThrown(); this.thrownProxy = event.getThrownProxy(); + this.instant.initFrom(event.getInstant()); + // NOTE: this ringbuffer event SHOULD NOT keep a reference to the specified // thread-local MutableLogEvent's context data, because then two threads would call // ReadOnlyStringMap.clear() on the same shared instance, resulting in data corruption. @@ -307,13 +305,27 @@ public class MutableLogEvent implements LogEvent, ReusableMessage { this.thrown = thrown; } + void initTime(final Clock clock, final NanoClock nanoClock) { + if (message instanceof TimestampMessage) { + instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0); + } else { + instant.initFrom(clock); + } + nanoTime = nanoClock.nanoTime(); + } + @Override public long getTimeMillis() { - return timeMillis; + return instant.getEpochMillisecond(); } public void setTimeMillis(final long timeMillis) { - this.timeMillis = timeMillis; + this.instant.initFromEpochMilli(timeMillis, 0); + } + + @Override + public Instant getInstant() { + return instant; } /** @@ -468,7 +480,8 @@ public class MutableLogEvent implements LogEvent, ReusableMessage { .setThreadPriority(threadPriority) // .setThrown(getThrown()) // may deserialize from thrownProxy .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy - .setTimeMillis(timeMillis); + .setInstant(instant) // + ; } private Message getNonNullImmutableMessage() { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java index bef1b8d..0ecefed 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java @@ -28,7 +28,6 @@ import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.util.Clock; import org.apache.logging.log4j.core.util.ClockFactory; import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.TimestampMessage; import org.apache.logging.log4j.util.StringMap; /** @@ -79,13 +78,10 @@ public class ReusableLogEventFactory implements LogEventFactory { result.setLoggerFqcn(fqcn); result.setLevel(level == null ? Level.OFF : level); result.setMessage(message); + result.initTime(CLOCK, Log4jLogEvent.getNanoClock()); result.setThrown(t); result.setContextData(injector.injectContextData(properties, (StringMap) result.getContextData())); result.setContextStack(ThreadContext.getDepth() == 0 ? ThreadContext.EMPTY_STACK : ThreadContext.cloneStack());// mutable copy - result.setTimeMillis(message instanceof TimestampMessage - ? ((TimestampMessage) message).getTimestamp() - : CLOCK.currentTimeMillis()); - result.setNanoTime(Log4jLogEvent.getNanoClock().nanoTime()); if (THREAD_NAME_CACHING_STRATEGY == ThreadNameCachingStrategy.UNCACHED) { result.setThreadName(Thread.currentThread().getName()); // Thread.getName() allocates Objects on each call http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/Initializers.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/Initializers.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/Initializers.java index 65c8438..18e30e2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/Initializers.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/Initializers.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.message.ObjectMessage; import com.fasterxml.jackson.databind.Module.SetupContext; import com.fasterxml.jackson.databind.module.SimpleModule; +import org.apache.logging.log4j.core.util.Instant; /** * Initialization utils. @@ -47,6 +48,7 @@ class Initializers { // Log4j API classes: we do not want to edit those with Jackson annotations because the API module should not depend on Jackson. context.setMixInAnnotations(Marker.class, MarkerMixIn.class); context.setMixInAnnotations(Level.class, LevelMixIn.class); + context.setMixInAnnotations(Instant.class, InstantMixIn.class); context.setMixInAnnotations(LogEvent.class, LogEventWithContextListMixIn.class); // Log4j Core classes: we do not want to bring in Jackson at runtime if we do not have to. context.setMixInAnnotations(ExtendedStackTraceElement.class, ExtendedStackTraceElementMixIn.class); @@ -68,6 +70,7 @@ class Initializers { // Log4j API classes: we do not want to edit those with Jackson annotations because the API module should not depend on Jackson. context.setMixInAnnotations(Marker.class, MarkerMixIn.class); context.setMixInAnnotations(Level.class, LevelMixIn.class); + context.setMixInAnnotations(Instant.class, InstantMixIn.class); context.setMixInAnnotations(LogEvent.class, LogEventJsonMixIn.class); // different ThreadContext handling // Log4j Core classes: we do not want to bring in Jackson at runtime if we do not have to. context.setMixInAnnotations(ExtendedStackTraceElement.class, ExtendedStackTraceElementMixIn.class); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/InstantMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/InstantMixIn.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/InstantMixIn.java new file mode 100644 index 0000000..44b9a6d --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/InstantMixIn.java @@ -0,0 +1,55 @@ +/* + * 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.jackson; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.core.util.Instant; + +/** + * Jackson mix-in for {@link Instant}. + * <p> + * <em>Consider this class private.</em> + * </p> + * @see Marker + */ +@JsonIgnoreProperties({ "epochMillisecond", "nanoOfMillisecond" }) +abstract class InstantMixIn { + + @JsonCreator + InstantMixIn( + // @formatter:off + @JsonProperty("epochSecond") final long epochSecond, + @JsonProperty("nanoOfSecond") final int nanoOfSecond) + // @formatter:on + { + // empty + } + + @JsonProperty("epochSecond") + @JacksonXmlProperty(localName = "epochSecond", isAttribute = true) + abstract long getEpochSecond(); + + @JsonProperty("nanoOfSecond") + @JacksonXmlProperty(localName = "nanoOfSecond", isAttribute = true) + abstract int getNanoOfSecond(); +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java index 8e9ed2c..bb9898d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/JsonConstants.java @@ -31,4 +31,5 @@ public final class JsonConstants { public static final String ELT_MESSAGE = "message"; public static final String ELT_EXTENDED_STACK_TRACE = "extendedStackTrace"; public static final String ELT_NANO_TIME = "nanoTime"; + public static final String ELT_INSTANT = "instant"; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java index b659e9b..6c27295 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext.ContextStack; +import org.apache.logging.log4j.core.util.Instant; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; @@ -40,7 +41,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JsonRootName(XmlConstants.ELT_EVENT) @JacksonXmlRootElement(namespace = XmlConstants.XML_NAMESPACE, localName = XmlConstants.ELT_EVENT) @JsonFilter("org.apache.logging.log4j.core.impl.Log4jLogEvent") -@JsonPropertyOrder({ "timeMillis", "threadName", "level", "loggerName", "marker", "message", "thrown", XmlConstants.ELT_CONTEXT_MAP, +@JsonPropertyOrder({ "timeMillis", XmlConstants.ELT_INSTANT, "threadName", "level", "loggerName", "marker", "message", "thrown", XmlConstants.ELT_CONTEXT_MAP, JsonConstants.ELT_CONTEXT_STACK, "loggerFQCN", "Source", "endOfBatch" }) abstract class LogEventJsonMixIn implements LogEvent { @@ -126,11 +127,17 @@ abstract class LogEventJsonMixIn implements LogEvent { @Override public abstract ThrowableProxy getThrownProxy(); - @JsonProperty() - @JacksonXmlProperty(isAttribute = true) + @JsonIgnore // ignore from 2.11 +// @JsonProperty() +// @JacksonXmlProperty(isAttribute = true) @Override public abstract long getTimeMillis(); + @JsonProperty(JsonConstants.ELT_INSTANT) + @JacksonXmlProperty(namespace = XmlConstants.XML_NAMESPACE, localName = XmlConstants.ELT_INSTANT) + @Override + public abstract Instant getInstant(); + @JsonProperty() @JacksonXmlProperty(isAttribute = true) @Override http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java index 176d82d..b71fcfc 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext.ContextStack; +import org.apache.logging.log4j.core.util.Instant; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; @@ -40,7 +41,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JsonRootName(XmlConstants.ELT_EVENT) @JacksonXmlRootElement(namespace = XmlConstants.XML_NAMESPACE, localName = XmlConstants.ELT_EVENT) @JsonFilter("org.apache.logging.log4j.core.impl.Log4jLogEvent") -@JsonPropertyOrder({ "timeMillis", "threadName", "level", "loggerName", "marker", "message", "thrown", XmlConstants.ELT_CONTEXT_MAP, +@JsonPropertyOrder({ "timeMillis", XmlConstants.ELT_INSTANT, "threadName", "level", "loggerName", "marker", "message", "thrown", XmlConstants.ELT_CONTEXT_MAP, JsonConstants.ELT_CONTEXT_STACK, "loggerFQCN", "Source", "endOfBatch" }) abstract class LogEventWithContextListMixIn implements LogEvent { @@ -125,11 +126,17 @@ abstract class LogEventWithContextListMixIn implements LogEvent { @Override public abstract ThrowableProxy getThrownProxy(); - @JsonProperty() - @JacksonXmlProperty(isAttribute = true) + @JsonIgnore // ignore from 2.11 +// @JsonProperty() +// @JacksonXmlProperty(isAttribute = true) @Override public abstract long getTimeMillis(); + @JsonProperty(JsonConstants.ELT_INSTANT) + @JacksonXmlProperty(namespace = XmlConstants.XML_NAMESPACE, localName = XmlConstants.ELT_INSTANT) + @Override + public abstract Instant getInstant(); + @JsonProperty() @JacksonXmlProperty(isAttribute = true) @Override http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/XmlConstants.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/XmlConstants.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/XmlConstants.java index c3b03cc..0871440 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/XmlConstants.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/XmlConstants.java @@ -27,6 +27,7 @@ public final class XmlConstants { public static final String ELT_EVENT = "Event"; public static final String ELT_EXTENDED_STACK_TRACE = "ExtendedStackTrace"; public static final String ELT_EXTENDED_STACK_TRACE_ITEM = "ExtendedStackTraceItem"; + public static final String ELT_INSTANT = "Instant"; public static final String ELT_MARKER = "Marker"; public static final String ELT_MESSAGE = "Message"; public static final String ELT_PARENTS = "Parents"; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java index 732acf8..aa49ee4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.util.Constants; +import org.apache.logging.log4j.core.util.Instant; +import org.apache.logging.log4j.core.util.MutableInstant; import org.apache.logging.log4j.core.util.datetime.FastDateFormat; import org.apache.logging.log4j.core.util.datetime.FixedDateFormat; import org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat; @@ -40,10 +42,11 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme private abstract static class Formatter { long previousTime; // for ThreadLocal caching mode + int nanos; - abstract String format(long timeMillis); + abstract String format(final Instant instant); - abstract void formatToBuffer(long timeMillis, StringBuilder destination); + abstract void formatToBuffer(final Instant instant, StringBuilder destination); public String toPattern() { return null; @@ -61,12 +64,13 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme } @Override - String format(final long timeMillis) { - return fastDateFormat.format(timeMillis); + String format(final Instant instant) { + return fastDateFormat.format(instant.getEpochMillisecond()); } @Override - void formatToBuffer(final long timeMillis, final StringBuilder destination) { + void formatToBuffer(final Instant instant, final StringBuilder destination) { + final long timeMillis = instant.getEpochMillisecond(); if (previousTime != timeMillis) { cachedBuffer.setLength(0); fastDateFormat.format(timeMillis, cachedBuffer); @@ -92,14 +96,18 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme } @Override - String format(final long timeMillis) { - return fixedDateFormat.format(timeMillis); + String format(final Instant instant) { + return fixedDateFormat.formatInstant(instant); } @Override - void formatToBuffer(final long timeMillis, final StringBuilder destination) { - if (previousTime != timeMillis) { - length = fixedDateFormat.format(timeMillis, cachedBuffer, 0); + void formatToBuffer(final Instant instant, final StringBuilder destination) { + final long epochSecond = instant.getEpochSecond(); + final int nanoOfSecond = instant.getNanoOfSecond(); + if (previousTime != epochSecond && nanos != nanoOfSecond) { + length = fixedDateFormat.formatInstant(instant, cachedBuffer, 0); + previousTime = epochSecond; + nanos = nanoOfSecond; } destination.append(cachedBuffer, 0, length); } @@ -113,36 +121,38 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme private static final class UnixFormatter extends Formatter { @Override - String format(final long timeMillis) { - return Long.toString(timeMillis / 1000); + String format(final Instant instant) { + return Long.toString(instant.getEpochSecond()); } @Override - void formatToBuffer(final long timeMillis, final StringBuilder destination) { - destination.append(timeMillis / 1000); // no need for caching + void formatToBuffer(final Instant instant, final StringBuilder destination) { + destination.append(instant.getEpochSecond()); // no need for caching } } private static final class UnixMillisFormatter extends Formatter { @Override - String format(final long timeMillis) { - return Long.toString(timeMillis); + String format(final Instant instant) { + return Long.toString(instant.getEpochMillisecond()); } @Override - void formatToBuffer(final long timeMillis, final StringBuilder destination) { - destination.append(timeMillis); // no need for caching + void formatToBuffer(final Instant instant, final StringBuilder destination) { + destination.append(instant.getEpochMillisecond()); // no need for caching } } private final class CachedTime { - public long timestampMillis; + public long epochSecond; + public int nanoOfSecond; public String formatted; - public CachedTime(final long timestampMillis) { - this.timestampMillis = timestampMillis; - this.formatted = formatter.format(this.timestampMillis); + public CachedTime(final Instant instant) { + this.epochSecond = instant.getEpochSecond(); + this.nanoOfSecond = instant.getNanoOfSecond(); + this.formatted = formatter.format(instant); } } @@ -157,6 +167,7 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS"; private final String[] options; + private final ThreadLocal<MutableInstant> threadLocalMutableInstant = new ThreadLocal<>(); private final ThreadLocal<Formatter> threadLocalFormatter = new ThreadLocal<>(); private final AtomicReference<CachedTime> cachedTime; private final Formatter formatter; @@ -170,7 +181,13 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme super("Date", "date"); this.options = options == null ? null : Arrays.copyOf(options, options.length); this.formatter = createFormatter(options); - cachedTime = new AtomicReference<>(new CachedTime(System.currentTimeMillis())); + cachedTime = new AtomicReference<>(fromEpochMillis(System.currentTimeMillis())); + } + + private CachedTime fromEpochMillis(long epochMillis) { + final MutableInstant temp = new MutableInstant(); + temp.initFromEpochMilli(epochMillis, 0); + return new CachedTime(temp); } private Formatter createFormatter(final String[] options) { @@ -245,19 +262,38 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme */ @Override public void format(final LogEvent event, final StringBuilder output) { - format(event.getTimeMillis(), output); + format(event.getInstant(), output); + } + + public void format(final long epochMilli, final StringBuilder output) { + MutableInstant instant = getMutableInstant(); + instant.initFromEpochMilli(epochMilli, 0); + format(instant, output); + } + + private MutableInstant getMutableInstant() { + if (Constants.ENABLE_THREADLOCALS) { + MutableInstant result = threadLocalMutableInstant.get(); + if (result == null) { + result = new MutableInstant(); + threadLocalMutableInstant.set(result); + } + return result; + } else { + return new MutableInstant(); + } } - public void format(final long timestampMillis, final StringBuilder output) { + public void format(final Instant instant, final StringBuilder output) { if (Constants.ENABLE_THREADLOCALS) { - formatWithoutAllocation(timestampMillis, output); + formatWithoutAllocation(instant, output); } else { - formatWithoutThreadLocals(timestampMillis, output); + formatWithoutThreadLocals(instant, output); } } - private void formatWithoutAllocation(final long timestampMillis, final StringBuilder output) { - getThreadLocalFormatter().formatToBuffer(timestampMillis, output); + private void formatWithoutAllocation(final Instant instant, final StringBuilder output) { + getThreadLocalFormatter().formatToBuffer(instant, output); } private Formatter getThreadLocalFormatter() { @@ -269,10 +305,10 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme return result; } - private void formatWithoutThreadLocals(final long timestampMillis, final StringBuilder output) { + private void formatWithoutThreadLocals(final Instant instant, final StringBuilder output) { CachedTime cached = cachedTime.get(); - if (timestampMillis != cached.timestampMillis) { - final CachedTime newTime = new CachedTime(timestampMillis); + if (instant.getEpochSecond() != cached.epochSecond || instant.getNanoOfSecond() != cached.nanoOfSecond) { + final CachedTime newTime = new CachedTime(instant); if (cachedTime.compareAndSet(cached, newTime)) { cached = newTime; } else { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3a126ee/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ClockFactory.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ClockFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ClockFactory.java index 129f281..bd5e840 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ClockFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ClockFactory.java @@ -18,6 +18,10 @@ package org.apache.logging.log4j.core.util; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.Supplier; + +import java.util.HashMap; +import java.util.Map; /** * Factory for {@code Clock} objects. @@ -62,21 +66,24 @@ public final class ClockFactory { return createClock(); } + private static Map<String, Supplier<Clock>> aliases() { + Map<String, Supplier<Clock>> result = new HashMap<>(); + result.put("SystemClock", new Supplier<Clock>() { @Override public Clock get() { return new SystemClock(); } }); + result.put("CachedClock", new Supplier<Clock>() { @Override public Clock get() { return CachedClock.instance(); } }); + result.put("CoarseCachedClock", new Supplier<Clock>() { @Override public Clock get() { return CoarseCachedClock.instance(); } }); + return result; + } + private static Clock createClock() { final String userRequest = PropertiesUtil.getProperties().getStringProperty(PROPERTY_NAME); - if (userRequest == null || "SystemClock".equals(userRequest)) { + if (userRequest == null) { LOGGER.trace("Using default SystemClock for timestamps."); return new SystemClock(); } - if (CachedClock.class.getName().equals(userRequest) - || "CachedClock".equals(userRequest)) { - LOGGER.trace("Using specified CachedClock for timestamps."); - return CachedClock.instance(); - } - if (CoarseCachedClock.class.getName().equals(userRequest) - || "CoarseCachedClock".equals(userRequest)) { - LOGGER.trace("Using specified CoarseCachedClock for timestamps."); - return CoarseCachedClock.instance(); + Supplier<Clock> specified = aliases().get(userRequest); + if (specified != null) { + LOGGER.trace("Using specified {} for timestamps.", userRequest); + return specified.get(); } try { final Clock result = Loader.newCheckedInstanceOf(userRequest, Clock.class);
