This is an automated email from the ASF dual-hosted git repository. mattsicker pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/main by this push: new 6af1756063 Adjust GcpLayout JSON to latest format (#3586) (#3867) 6af1756063 is described below commit 6af17560635ab14bb97651aaf4a7c7b17ba8c7bd Author: Matt Sicker <mattsic...@apache.org> AuthorDate: Fri Aug 1 12:08:57 2025 -0500 Adjust GcpLayout JSON to latest format (#3586) (#3867) * 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: Vilius Šumskas <vil...@sumskas.eu> Co-authored-by: Volkan Yazıcı <volkan.yaz...@oracle.com> --- .../log4j/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 a617d3a2e2..080bb41f11 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.CONFIGUR 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 @@ class GcpLayoutTest { 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 @@ class GcpLayoutTest { .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 f00c84d981..75bf58a789 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" }