From c09e2f9b22fe84a4b5859c8fd33e976424233417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vilius=20=C5=A0umskas?= Date: Sun, 4 May 2025 00:28:47 +0300 Subject: [PATCH] Adjust GcpLayout JSON to latest format (#3586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adjust GcpLayout JSON to latest format First, it formats the log timestamp field to the correct format recognized by Fluent-Bit (component of Google Cloud Logging) and Google Ops Agent. Secondly, severity field now must be prefixed with logging.googleapis.com. Third, counter cannot be used for insertId as it is duplicated on different threads. And the last but not the least, exception, thread and logger fields are pretty standard when logging via Logback's JSON layout and Google's Spring GCP libraries. Field name changes now match these other loggers. * revert severity changes, remove insertId * Remove insertid from tests * fix spotless error * Switch exception field to use exception resolver * try to fix timestamp tests * Fix tests with empty exceptions * Add changelog * Improve changelog. --------- Co-authored-by: Volkan Yazıcı --- .../layout/template/json/GcpLayoutTest.java | 44 +++---------------- .../src/main/resources/GcpLayout.json | 38 +++++++--------- 2 files changed, 23 insertions(+), 59 deletions(-) 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 a617d3a2e28..080bb41f115 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 @@ -20,11 +20,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; @@ -43,9 +38,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); @@ -83,8 +75,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(); @@ -147,48 +140,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/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" }