diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/GcpLayoutTest.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/GcpLayoutTest.java index c257b1c8af8..6501259cfb2 100644 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/GcpLayoutTest.java +++ b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/GcpLayoutTest.java @@ -21,11 +21,6 @@ import static org.apache.logging.log4j.layout.template.json.TestHelpers.usingSerializedLogEventAccessor; import static org.assertj.core.api.Assertions.assertThat; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Locale; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ContextDataFactory; @@ -44,9 +39,6 @@ class GcpLayoutTest { private static final int LOG_EVENT_COUNT = 1_000; - private static final DateTimeFormatter DATE_TIME_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); - @Test void test_lite_log_events() { LogEventFixture.createLiteLogEvents(LOG_EVENT_COUNT).forEach(GcpLayoutTest::verifySerialization); @@ -84,8 +76,9 @@ private static void verifySerialization(final LogEvent logEvent) { usingSerializedLogEventAccessor(LAYOUT, logEvent, accessor -> { // Verify timestamp. - final String expectedTimestamp = formatLogEventInstant(logEvent); - assertThat(accessor.getString("timestamp")).isEqualTo(expectedTimestamp); + final org.apache.logging.log4j.core.time.Instant instant = logEvent.getInstant(); + assertThat(accessor.getInteger("timestampSeconds")).isEqualTo(instant.getEpochSecond()); + assertThat(accessor.getInteger("timestampNanos")).isEqualTo(instant.getNanoOfSecond()); // Verify severity. final Level level = logEvent.getLevel(); @@ -148,48 +141,25 @@ private static void verifySerialization(final LogEvent logEvent) { .isEmpty(); } - // Verify insert id. - assertThat(accessor.getString("logging.googleapis.com/insertId")).matches("[-]?[0-9]+"); - // Verify exception. if (exception != null) { - // Verify exception class. - assertThat(accessor.getString(new String[] {"_exception", "class"})) - .isEqualTo(exception.getClass().getCanonicalName()); - - // Verify exception message. - assertThat(accessor.getString(new String[] {"_exception", "message"})) - .isEqualTo(exception.getMessage()); - // Verify exception stack trace. - assertThat(accessor.getString(new String[] {"_exception", "stackTrace"})) + assertThat(accessor.getString("exception")) .contains(exception.getLocalizedMessage()) .contains("at org.apache.logging.log4j.layout.template.json") .contains("at " + JAVA_BASE_PREFIX + "java.lang.reflect.Method") .contains("at org.junit.platform.engine"); } else { - assertThat(accessor.getObject(new String[] {"_exception", "class"})) - .isNull(); - assertThat(accessor.getObject(new String[] {"_exception", "message"})) - .isNull(); - assertThat(accessor.getString(new String[] {"_exception", "stackTrace"})) - .isEmpty(); + assertThat(accessor.getString("exception")).isNull(); } // Verify thread name. - assertThat(accessor.getString("_thread")).isEqualTo(logEvent.getThreadName()); + assertThat(accessor.getString("thread")).isEqualTo(logEvent.getThreadName()); // Verify logger name. - assertThat(accessor.getString("_logger")).isEqualTo(logEvent.getLoggerName()); + assertThat(accessor.getString("logger")).isEqualTo(logEvent.getLoggerName()); }); } - - private static String formatLogEventInstant(final LogEvent logEvent) { - final org.apache.logging.log4j.core.time.Instant instant = logEvent.getInstant(); - final ZonedDateTime dateTime = Instant.ofEpochSecond(instant.getEpochSecond(), instant.getNanoOfSecond()) - .atZone(ZoneId.of("UTC")); - return DATE_TIME_FORMATTER.format(dateTime); - } } diff --git a/log4j-layout-template-json/src/main/resources/GcpLayout.json b/log4j-layout-template-json/src/main/resources/GcpLayout.json index f00c84d981e..75bf58a7896 100644 --- a/log4j-layout-template-json/src/main/resources/GcpLayout.json +++ b/log4j-layout-template-json/src/main/resources/GcpLayout.json @@ -1,10 +1,15 @@ { - "timestamp": { + "timestampSeconds": { "$resolver": "timestamp", - "pattern": { - "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", - "timeZone": "UTC", - "locale": "en_US" + "epoch": { + "unit": "secs", + "rounded": true + } + }, + "timestampNanos": { + "$resolver": "timestamp", + "epoch": { + "unit": "secs.nanos" } }, "severity": { @@ -36,10 +41,6 @@ "stackTraceEnabled": false } }, - "logging.googleapis.com/insertId": { - "$resolver": "counter", - "stringified": true - }, "logging.googleapis.com/trace": { "$resolver": "mdc", "key": "trace_id" @@ -49,25 +50,18 @@ "key": "span_id" }, "logging.googleapis.com/trace_sampled": true, - "_exception": { - "class": { - "$resolver": "exception", - "field": "className" - }, - "message": { - "$resolver": "exception", - "field": "message" - }, + "exception": { + "$resolver": "exception", + "field": "stackTrace", "stackTrace": { - "$resolver": "pattern", - "pattern": "%xEx" + "stringified": true } }, - "_thread": { + "thread": { "$resolver": "thread", "field": "name" }, - "_logger": { + "logger": { "$resolver": "logger", "field": "name" } diff --git a/src/changelog/.2.x.x/3586_improve_GcpLayout.xml b/src/changelog/.2.x.x/3586_improve_GcpLayout.xml new file mode 100644 index 00000000000..00e0fc7a69b --- /dev/null +++ b/src/changelog/.2.x.x/3586_improve_GcpLayout.xml @@ -0,0 +1,10 @@ + + + + + Update `GcpLayout.json` JSON Template Layout event template to support automatic timestamp recognition by the Google Cloud Logging. This also changes `exception`, `thread`, `logger` fields, and removes `insertId` field. + +