diff --git a/README.md b/README.md
index ac31833a..c033fad7 100644
--- a/README.md
+++ b/README.md
@@ -1534,19 +1534,11 @@ The value can be written as a number (instead of a string) like this:
## Customizing Timestamp
-By default, timestamps are written as string values in the format `yyyy-MM-dd'T'HH:mm:ss.SSSZZ` (e.g. `2018-04-28T22:23:59.164-07:00`), in the default TimeZone of the host Java platform.
+By default, timestamps are written as string values in the format specified by
+[`DateTimeFormatter.ISO_OFFSET_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME)
+(e.g. `2019-11-03T10:15:30.123+01:00`), in the default TimeZone of the host Java platform.
-You can change the timezone like this:
-
-```xml
-
- UTC
-
-```
-
-The value of the `timeZone` element can be any string accepted by java's `TimeZone.getTimeZone(String id)` method.
-
-You can change the pattern used like this:
+You can change the pattern like this:
```xml
@@ -1554,19 +1546,24 @@ You can change the pattern used like this:
```
-Use these timestamp pattern values to output the timestamp as a unix timestamp (number of milliseconds since unix epoch).
+The value of the `timestampPattern` can be any of the following:
-* `[UNIX_TIMESTAMP_AS_NUMBER]` - write the timestamp value as a numeric unix timestamp
-* `[UNIX_TIMESTAMP_AS_STRING]` - write the timestamp value as a string verion of the numeric unix timestamp
+* `[UNIX_TIMESTAMP_AS_NUMBER]` - timestamp written as a JSON number value of the milliseconds since unix epoch
+* `[UNIX_TIMESTAMP_AS_STRING]` - timestamp written as a JSON string value of the milliseconds since unix epoch
+* `[` _`constant`_ `]` - (e.g. `[ISO_OFFSET_DATE_TIME]`) timestamp written using the given `DateTimeFormatter` constant
+* any other value - (e.g. `yyyy-MM-dd'T'HH:mm:ss.SSS`) timestamp written using a `DateTimeFormatter` created from the given pattern
-For example:
+
+You can change the timezone like this:
```xml
- [UNIX_TIMESTAMP_AS_NUMBER]
+ UTC
```
+The value of the `timeZone` element can be any string accepted by java's `TimeZone.getTimeZone(String id)` method.
+
## Customizing Message
@@ -1789,12 +1786,7 @@ For LoggingEvents, the available providers and their configuration properties (d
Event timestamp
- fieldName - Output field name (@timestamp)
- - pattern - Output format (yyyy-MM-dd'T'HH:mm:ss.SSSZZ)
-
- - If set to [UNIX_TIMESTAMP_AS_NUMBER], then the timestamp will be written as a numeric unix timestamp value
- - If set to [UNIX_TIMESTAMP_AS_STRING], then the timestamp will be written as a string unix timestamp value
-
-
+ - pattern - Output format ([ISO_OFFSET_DATE_TIME]) See above for possible values.
- timeZone - Timezone (local timezone)
|
@@ -2060,12 +2052,7 @@ For AccessEvents, the available providers and their configuration properties (de
Event timestamp
- fieldName - Output field name (@timestamp)
- - pattern - Output format (yyyy-MM-dd'T'HH:mm:ss.SSSZZ)
-
- - If set to [UNIX_TIMESTAMP_AS_NUMBER], then the timestamp will be written as a numeric unix timestamp value
- - If set to [UNIX_TIMESTAMP_AS_STRING], then the timestamp will be written as a string unix timestamp value
-
-
+ - pattern - Output format ([ISO_OFFSET_DATE_TIME]) See above for possible values.
- timeZone - Timezone (local timezone)
|
diff --git a/pom.xml b/pom.xml
index 51ddb94a..ecfcc6a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,6 @@
3.4.2
- 3.11
5.7.0
@@ -179,11 +178,6 @@
-->
true
-
- org.apache.commons
- commons-lang3
- ${commons-lang3.version}
-
com.lmax
disruptor
@@ -332,10 +326,6 @@
-
- org.apache.commons.lang3
- ${project.groupId}.encoder.org.apache.commons.lang3
-
com.lmax.disruptor
${project.groupId}.encoder.com.lmax.disruptor
@@ -364,7 +354,7 @@
ch.qos.logback.classic
- !org.apache.commons.*,!com.lmax.disruptor.*,!ch.qos.logback.classic.*,!ch.qos.logback.core.*,!org.slf4j.*,*
+ !com.lmax.disruptor.*,!ch.qos.logback.classic.*,!ch.qos.logback.core.*,!org.slf4j.*,*
diff --git a/src/main/java/net/logstash/logback/appender/listener/FailureSummaryLoggingAppenderListener.java b/src/main/java/net/logstash/logback/appender/listener/FailureSummaryLoggingAppenderListener.java
index 266ac112..755a5785 100644
--- a/src/main/java/net/logstash/logback/appender/listener/FailureSummaryLoggingAppenderListener.java
+++ b/src/main/java/net/logstash/logback/appender/listener/FailureSummaryLoggingAppenderListener.java
@@ -14,11 +14,13 @@
package net.logstash.logback.appender.listener;
import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
import java.util.Objects;
+import java.util.TimeZone;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import net.logstash.logback.argument.StructuredArguments;
-import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,7 +54,7 @@ protected void handleFailureSummary(FailureSummary failureSummary, CallbackType
logger.warn("{} {} failures since {} for {}.",
StructuredArguments.value("failEventCount", failureSummary.getConsecutiveFailures()),
StructuredArguments.value("failType", callbackType.name().toLowerCase()),
- StructuredArguments.value("failStartTime", DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(failureSummary.getFirstFailureTime())),
+ StructuredArguments.value("failStartTime", DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(failureSummary.getFirstFailureTime()))),
StructuredArguments.value("failDuration", Duration.ofMillis(System.currentTimeMillis() - failureSummary.getFirstFailureTime()).toString()),
failureSummary.getMostRecentFailure());
}
diff --git a/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java b/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java
index a370775d..fde60ca5 100644
--- a/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java
+++ b/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java
@@ -14,20 +14,21 @@
package net.logstash.logback.composite;
import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
-import net.logstash.logback.fieldnames.LogstashCommonFieldNames;
-
-import org.apache.commons.lang3.time.FastDateFormat;
-
import ch.qos.logback.core.spi.DeferredProcessingAware;
+import net.logstash.logback.fieldnames.LogstashCommonFieldNames;
import com.fasterxml.jackson.core.JsonGenerator;
/**
* Writes the timestamp field as either:
*
- * - A string value formatted by a {@link FastDateFormat} pattern
+ * - A string value formatted by a {@link DateTimeFormatter} pattern
* - A string value representing the number of milliseconds since unix epoch (designated by specifying the pattern value as {@value #UNIX_TIMESTAMP_AS_STRING})
* - A number value of the milliseconds since unix epoch (designated by specifying the pattern value as {@value #UNIX_TIMESTAMP_AS_NUMBER})
*
@@ -48,14 +49,20 @@ public abstract class FormattedTimestampJsonProviderPossible values:
+ *
+ *
+ * - {@value #UNIX_TIMESTAMP_AS_NUMBER} - timestamp written as a JSON number value of the milliseconds since unix epoch
+ * - {@value #UNIX_TIMESTAMP_AS_STRING} - timestamp written as a JSON string value of the milliseconds since unix epoch
+ * [constant]
- timestamp written using the {@link DateTimeFormatter} constant specified by constant
(e.g. {@code [ISO_OFFSET_DATE_TIME]})
+ * - any other value - timestamp written by a {@link DateTimeFormatter} created from the pattern string specified
+ *
*/
private String pattern = DEFAULT_PATTERN;
@@ -68,12 +75,12 @@ public abstract class FormattedTimestampJsonProvider {
+ LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider();
+ provider.setPattern("[foo]");
+ });
+ }
+
+ @Test
+ public void customPattern() throws IOException {
+ LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider();
+ String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS";
+ provider.setPattern(pattern);
+ when(event.getTimeStamp()).thenReturn(0L);
+
+ provider.writeTo(generator, event);
+
+ String expectedValue = DateTimeFormatter.ofPattern(pattern).withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(0));
+ verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue);
+ }
+
+ @Test
+ public void unixEpochAsNumber() throws IOException {
+ LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider();
+ provider.setPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER);
+ when(event.getTimeStamp()).thenReturn(0L);
+
+ provider.writeTo(generator, event);
+
+ verify(generator).writeNumberField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, 0L);
+ }
+
+ @Test
+ public void unixEpochAsString() throws IOException {
+ LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider();
+ provider.setPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING);
+ when(event.getTimeStamp()).thenReturn(0L);
+
+ provider.writeTo(generator, event);
+
+ verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, "0");
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java b/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java
index a514377c..dbd31056 100644
--- a/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java
+++ b/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java
@@ -19,10 +19,16 @@
import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
+import java.util.TimeZone;
-import org.apache.commons.lang3.time.FastDateFormat;
+import ch.qos.logback.access.spi.IAccessEvent;
+import ch.qos.logback.core.Context;
+import net.logstash.logback.Logback11Support;
+import net.logstash.logback.composite.FormattedTimestampJsonProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -31,11 +37,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import ch.qos.logback.access.spi.IAccessEvent;
-import ch.qos.logback.core.Context;
-import net.logstash.logback.Logback11Support;
-import net.logstash.logback.composite.FormattedTimestampJsonProvider;
-
@ExtendWith(MockitoExtension.class)
public class LogstashAccessEncoderTest {
@@ -89,12 +90,11 @@ public void basicsAreIncluded_logback12OrLater() throws Exception {
}
protected void verifyBasics(final long timestamp, IAccessEvent event, JsonNode node) {
- assertThat(node.get("timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format
- (timestamp));
+ assertThat(node.get("timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(timestamp)));
assertThat(node.get("@version").textValue()).isEqualTo("1");
assertThat(node.get("message").textValue()).isEqualTo(String.format("%s - %s [%s] \"%s\" %s %s", event.getRemoteHost(), event.getRemoteUser(),
- FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format
- (event.getTimeStamp()), event.getRequestURL(), event.getStatusCode(),
+ DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId())
+ .format(Instant.ofEpochMilli(event.getTimeStamp())), event.getRequestURL(), event.getStatusCode(),
event.getContentLength()));
assertThat(node.get("method").textValue()).isEqualTo(event.getMethod());
diff --git a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java
index 2192528d..b94008d5 100644
--- a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java
+++ b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java
@@ -30,6 +30,8 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -49,7 +51,7 @@
import net.logstash.logback.decorate.JsonGeneratorDecorator;
import net.logstash.logback.fieldnames.LogstashCommonFieldNames;
import net.logstash.logback.fieldnames.ShortenedFieldNames;
-import org.apache.commons.lang3.time.FastDateFormat;
+
import org.assertj.core.util.Files;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -122,7 +124,7 @@ public void basicsAreIncluded_logback12() throws Exception {
}
protected void verifyBasics(final long timestamp, JsonNode node) {
- assertThat(node.get("@timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format(timestamp));
+ assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(timestamp)));
assertThat(node.get("@version").textValue()).isEqualTo("1");
assertThat(node.get("logger_name").textValue()).isEqualTo("LoggerName");
assertThat(node.get("thread_name").textValue()).isEqualTo("ThreadName");
@@ -143,9 +145,8 @@ public void basicsAreIncludedWithShortenedNames() throws Exception {
byte[] encoded = encoder.encode(event);
JsonNode node = MAPPER.readTree(encoded);
-
- assertThat(node.get("@timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format
- (timestamp));
+
+ assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(timestamp)));
assertThat(node.get("@version").textValue()).isEqualTo("1");
assertThat(node.get("logger").textValue()).isEqualTo("LoggerName");
assertThat(node.get("thread").textValue()).isEqualTo("ThreadName");
@@ -185,7 +186,7 @@ public JsonGenerator decorate(JsonGenerator generator) {
assertThat(output).isEqualTo(String.format(
"{%n"
- + " @timestamp : \"" + FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format(timestamp) + "\",%n"
+ + " @timestamp : \"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(timestamp)) + "\",%n"
+ " @version : \"1\",%n"
+ " message : \"My message\",%n"
+ " logger_name : \"LoggerName\",%n"
@@ -200,10 +201,10 @@ public JsonGenerator decorate(JsonGenerator generator) {
public void loggerNameIsShortenedProperly() throws Exception {
final long timestamp = System.currentTimeMillis();
final int length = 36;
- final String shortenedLoggerName = new TargetLengthBasedClassNameAbbreviator(length).abbreviate(FastDateFormat.class.getCanonicalName());
+ final String shortenedLoggerName = new TargetLengthBasedClassNameAbbreviator(length).abbreviate(DateTimeFormatter.class.getCanonicalName());
ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
- when(event.getLoggerName()).thenReturn(FastDateFormat.class.getCanonicalName());
+ when(event.getLoggerName()).thenReturn(DateTimeFormatter.class.getCanonicalName());
when(event.getTimeStamp()).thenReturn(timestamp);
encoder.setFieldNames(new ShortenedFieldNames());
@@ -213,8 +214,7 @@ public void loggerNameIsShortenedProperly() throws Exception {
JsonNode node = MAPPER.readTree(encoded);
- assertThat(node.get("@timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format
- (timestamp));
+ assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(timestamp)));
assertThat(node.get("@version").textValue()).isEqualTo("1");
assertThat(node.get("logger").textValue()).isEqualTo(shortenedLoggerName);
assertThat(node.get("thread").textValue()).isEqualTo("ThreadName");
@@ -618,9 +618,8 @@ public void customTimeZone() throws Exception {
byte[] encoded = encoder.encode(event);
JsonNode node = MAPPER.readTree(encoded);
-
- assertThat(node.get("@timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ", TimeZone.getTimeZone("UTC")).format
- (timestamp));
+
+ assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()).format(Instant.ofEpochMilli(timestamp)));
assertThat(node.get("@version").textValue()).isEqualTo("1");
assertThat(node.get("logger_name").textValue()).isEqualTo("LoggerName");
assertThat(node.get("thread_name").textValue()).isEqualTo("ThreadName");