From e29ed4dec5d69543e0b647131d7938c3072db294 Mon Sep 17 00:00:00 2001 From: Ramanathan Date: Mon, 6 Jul 2026 01:14:52 +0530 Subject: [PATCH 1/2] Add W3C trace metadata resolvers for json template layout --- .../json/resolver/TraceResolverTest.java | 87 +++++++++++++++++++ .../layout/template/json/package-info.java | 2 +- .../json/resolver/SpanIdResolver.java | 53 +++++++++++ .../json/resolver/SpanIdResolverFactory.java | 46 ++++++++++ .../json/resolver/TraceFlagsResolver.java | 53 +++++++++++ .../resolver/TraceFlagsResolverFactory.java | 46 ++++++++++ .../json/resolver/TraceIdResolver.java | 53 +++++++++++ .../json/resolver/TraceIdResolverFactory.java | 46 ++++++++++ .../template/json/resolver/package-info.java | 2 +- 9 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/TraceResolverTest.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolver.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolverFactory.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolver.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolverFactory.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolver.java create mode 100644 log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolverFactory.java diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/TraceResolverTest.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/TraceResolverTest.java new file mode 100644 index 00000000000..0de00e22d82 --- /dev/null +++ b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/TraceResolverTest.java @@ -0,0 +1,87 @@ +/* + * 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.layout.template.json.resolver; + +import static org.apache.logging.log4j.layout.template.json.TestHelpers.CONFIGURATION; +import static org.apache.logging.log4j.layout.template.json.TestHelpers.asMap; +import static org.apache.logging.log4j.layout.template.json.TestHelpers.usingSerializedLogEventAccessor; +import static org.apache.logging.log4j.layout.template.json.TestHelpers.writeJson; +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout; +import org.junit.jupiter.api.Test; + +class TraceResolverTest { + + @Test + void should_resolve_trace_metadata() { + + // Create the event template using JTL TestHelpers + final String eventTemplate = writeJson(asMap( + "traceId", asMap("$resolver", "traceId"), + "spanId", asMap("$resolver", "spanId"), + "traceFlags", asMap("$resolver", "traceFlags"))); + + // Create the layout + final JsonTemplateLayout layout = JsonTemplateLayout.newBuilder() + .setConfiguration(CONFIGURATION) + .setEventTemplate(eventTemplate) + .build(); + + // Create the log event with active tracing data + final LogEvent logEvent = Log4jLogEvent.newBuilder() + .setTraceId("4bf92f3577b34da6a3ce929d0e0e4736") + .setSpanId("00f067aa0ba902b7") + .setTraceFlags("01") + .build(); + + // Check the serialized event parses the JSON correctly + usingSerializedLogEventAccessor(layout, logEvent, accessor -> { + assertThat(accessor.getString("traceId")).isEqualTo("4bf92f3577b34da6a3ce929d0e0e4736"); + assertThat(accessor.getString("spanId")).isEqualTo("00f067aa0ba902b7"); + assertThat(accessor.getString("traceFlags")).isEqualTo("01"); + }); + } + + @Test + void should_resolve_empty_metadata_safely() { + + // Create the event template + final String eventTemplate = writeJson(asMap( + "traceId", asMap("$resolver", "traceId"), + "spanId", asMap("$resolver", "spanId"), + "traceFlags", asMap("$resolver", "traceFlags"))); + + // Create the layout + final JsonTemplateLayout layout = JsonTemplateLayout.newBuilder() + .setConfiguration(CONFIGURATION) + .setEventTemplate(eventTemplate) + .build(); + + // Create the log event with NO tracing data (simulating default behavior) + final LogEvent logEvent = Log4jLogEvent.newBuilder().build(); + + // Check the serialized event does not throw exceptions and handles missing fields safely + usingSerializedLogEventAccessor(layout, logEvent, accessor -> { + assertThat(accessor.getString("traceId")).isNullOrEmpty(); + assertThat(accessor.getString("spanId")).isNullOrEmpty(); + assertThat(accessor.getString("traceFlags")).isNullOrEmpty(); + }); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/package-info.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/package-info.java index ab2e6c5b08d..e60735571b5 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/package-info.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/package-info.java @@ -16,7 +16,7 @@ */ @Export @Open("org.apache.logging.log4j.core") -@Version("2.21.0") +@Version("2.22.0") package org.apache.logging.log4j.layout.template.json; import aQute.bnd.annotation.jpms.Open; diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolver.java new file mode 100644 index 00000000000..4325a447255 --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolver.java @@ -0,0 +1,53 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; + +/** + * Resolves the W3C standard span ID. + * + *

Examples

+ * + * Resolve the span ID: + * + *
+ * {
+ *   "$resolver": "spanId"
+ * }
+ * 
+ */ +public final class SpanIdResolver implements EventResolver { + + private static final SpanIdResolver INSTANCE = new SpanIdResolver(); + + SpanIdResolver() {} + + static SpanIdResolver getInstance() { + return INSTANCE; + } + + static String getName() { + return "spanId"; + } + + @Override + public void resolve(final LogEvent logEvent, final JsonWriter jsonWriter) { + jsonWriter.writeString(logEvent.getSpanId()); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolverFactory.java new file mode 100644 index 00000000000..520452be527 --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/SpanIdResolverFactory.java @@ -0,0 +1,46 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * {@link SpanIdResolver} factory. + */ +@Plugin(name = "SpanIdResolverFactory", category = TemplateResolverFactory.CATEGORY) +public final class SpanIdResolverFactory implements EventResolverFactory { + + private static final SpanIdResolverFactory INSTANCE = new SpanIdResolverFactory(); + + private SpanIdResolverFactory() {} + + @PluginFactory + public static SpanIdResolverFactory getInstance() { + return INSTANCE; + } + + @Override + public String getName() { + return SpanIdResolver.getName(); + } + + @Override + public SpanIdResolver create(final EventResolverContext context, final TemplateResolverConfig config) { + return SpanIdResolver.getInstance(); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolver.java new file mode 100644 index 00000000000..23d46227d3a --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolver.java @@ -0,0 +1,53 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; + +/** + * Resolves the W3C standard trace flags. + * + *

Examples

+ * + * Resolve the trace flags: + * + *
+ * {
+ *   "$resolver": "traceFlags"
+ * }
+ * 
+ */ +public final class TraceFlagsResolver implements EventResolver { + + private static final TraceFlagsResolver INSTANCE = new TraceFlagsResolver(); + + TraceFlagsResolver() {} + + static TraceFlagsResolver getInstance() { + return INSTANCE; + } + + static String getName() { + return "traceFlags"; + } + + @Override + public void resolve(final LogEvent logEvent, final JsonWriter jsonWriter) { + jsonWriter.writeString(logEvent.getTraceFlags()); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolverFactory.java new file mode 100644 index 00000000000..29955b81d56 --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceFlagsResolverFactory.java @@ -0,0 +1,46 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * {@link TraceFlagsResolver} factory. + */ +@Plugin(name = "TraceFlagsResolverFactory", category = TemplateResolverFactory.CATEGORY) +public final class TraceFlagsResolverFactory implements EventResolverFactory { + + private static final TraceFlagsResolverFactory INSTANCE = new TraceFlagsResolverFactory(); + + private TraceFlagsResolverFactory() {} + + @PluginFactory + public static TraceFlagsResolverFactory getInstance() { + return INSTANCE; + } + + @Override + public String getName() { + return TraceFlagsResolver.getName(); + } + + @Override + public TraceFlagsResolver create(final EventResolverContext context, final TemplateResolverConfig config) { + return TraceFlagsResolver.getInstance(); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolver.java new file mode 100644 index 00000000000..894a253d37d --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolver.java @@ -0,0 +1,53 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; + +/** + * Resolves the W3C standard trace ID. + * + *

Examples

+ * + * Resolve the trace ID: + * + *
+ * {
+ *   "$resolver": "traceId"
+ * }
+ * 
+ */ +public final class TraceIdResolver implements EventResolver { + + private static final TraceIdResolver INSTANCE = new TraceIdResolver(); + + TraceIdResolver() {} + + static TraceIdResolver getInstance() { + return INSTANCE; + } + + static String getName() { + return "traceId"; + } + + @Override + public void resolve(final LogEvent logEvent, final JsonWriter jsonWriter) { + jsonWriter.writeString(logEvent.getTraceId()); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolverFactory.java new file mode 100644 index 00000000000..1ba09517812 --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TraceIdResolverFactory.java @@ -0,0 +1,46 @@ +/* + * 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.layout.template.json.resolver; + +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * {@link TraceIdResolver} factory. + */ +@Plugin(name = "TraceIdResolverFactory", category = TemplateResolverFactory.CATEGORY) +public final class TraceIdResolverFactory implements EventResolverFactory { + + private static final TraceIdResolverFactory INSTANCE = new TraceIdResolverFactory(); + + private TraceIdResolverFactory() {} + + @PluginFactory + public static TraceIdResolverFactory getInstance() { + return INSTANCE; + } + + @Override + public String getName() { + return TraceIdResolver.getName(); + } + + @Override + public TraceIdResolver create(final EventResolverContext context, final TemplateResolverConfig config) { + return TraceIdResolver.getInstance(); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/package-info.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/package-info.java index 376a67bd335..3b704adbc0b 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/package-info.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/package-info.java @@ -16,7 +16,7 @@ */ @Export @Open("org.apache.logging.log4j.core") -@Version("2.21.0") +@Version("2.22.0") package org.apache.logging.log4j.layout.template.json.resolver; import aQute.bnd.annotation.jpms.Open; From b10b76ab00efea3414944d60ae8ce6f1b14e521d Mon Sep 17 00:00:00 2001 From: Ramanathan Date: Tue, 7 Jul 2026 00:18:38 +0530 Subject: [PATCH 2/2] Add native JsonTemplateLayout resolvers for W3C tracing fields --- src/changelog/fix_jtl_trace_resolvers.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/changelog/fix_jtl_trace_resolvers.xml diff --git a/src/changelog/fix_jtl_trace_resolvers.xml b/src/changelog/fix_jtl_trace_resolvers.xml new file mode 100644 index 00000000000..5d2454b25a6 --- /dev/null +++ b/src/changelog/fix_jtl_trace_resolvers.xml @@ -0,0 +1,13 @@ + + + + + + Add native `JsonTemplateLayout` resolvers (`traceId`, `spanId`, `traceFlags`) for W3C tracing fields to achieve zero-allocation structured logging. + + \ No newline at end of file