Skip to content

Commit 3d90469

Browse files
committed
add ECS span id and trace id fields
1 parent b334a03 commit 3d90469

File tree

9 files changed

+356
-4
lines changed

9 files changed

+356
-4
lines changed

deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,16 @@ protected String[] logLines() {
6464
}
6565

6666
protected void testLogOutput() throws Exception {
67-
JsonFormatter jsonFormatter = getJsonFormatter();
67+
getJsonFormatter();
6868

6969
org.slf4j.Logger log = LoggerFactory.getLogger("JsonStructuredTest");
7070
OffsetDateTime beforeFirstLog = OffsetDateTime.now().minusSeconds(1);
7171

72-
try (MDC.MDCCloseable closeable = MDC.putCloseable("mdcKey", "mdcVal")) {
73-
log.error("Test {}", "message",
74-
KeyValueStructuredArgument.kv("structuredKey", "structuredValue"),
72+
try (
73+
MDC.MDCCloseable closeable = MDC.putCloseable("mdcKey", "mdcVal");
74+
MDC.MDCCloseable traceId = MDC.putCloseable("traceId", "123-456-789");
75+
MDC.MDCCloseable spanId = MDC.putCloseable("spanId", "987-654-321");) {
76+
log.error("Test {}", "message", KeyValueStructuredArgument.kv("structuredKey", "structuredValue"),
7577
new RuntimeException("Testing stackTrace"));
7678
}
7779

@@ -88,6 +90,8 @@ protected void testLogOutput() throws Exception {
8890
"log.level",
8991
"process.thread.name",
9092
"process.thread.id",
93+
"trace.id",
94+
"span.id",
9195
"mdc",
9296
"host.name",
9397
"error.stack_trace",
@@ -124,6 +128,12 @@ protected void testLogOutput() throws Exception {
124128
Assertions.assertNotNull(jsonNode.findValue("mdc").findValue("mdcKey"));
125129
Assertions.assertEquals("mdcVal", jsonNode.findValue("mdc").findValue("mdcKey").asText());
126130

131+
Assertions.assertTrue(jsonNode.findValue("trace.id").isTextual());
132+
Assertions.assertEquals("123-456-789", jsonNode.findValue("trace.id").asText());
133+
134+
Assertions.assertTrue(jsonNode.findValue("span.id").isTextual());
135+
Assertions.assertEquals("987-654-321", jsonNode.findValue("span.id").asText());
136+
127137
Assertions.assertTrue(jsonNode.findValue("host.name").isTextual());
128138
Assertions.assertNotEquals("", jsonNode.findValue("host.name").asText());
129139

docs/modules/ROOT/pages/includes/quarkus-logging-json.adoc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,90 @@ endif::add-copy-button-to-env-var[]
637637
|boolean
638638
|`+++false+++`
639639

640+
a| [[quarkus-logging-json_quarkus-log-json-fields-trace-id-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-trace-id-field-name[`quarkus.log.json.fields.trace-id.field-name`]##
641+
ifdef::add-copy-button-to-config-props[]
642+
config_property_copy_button:+++quarkus.log.json.fields.trace-id.field-name+++[]
643+
endif::add-copy-button-to-config-props[]
644+
645+
646+
[.description]
647+
--
648+
Used to change the json key for the field.
649+
650+
651+
ifdef::add-copy-button-to-env-var[]
652+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++[]
653+
endif::add-copy-button-to-env-var[]
654+
ifndef::add-copy-button-to-env-var[]
655+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++`
656+
endif::add-copy-button-to-env-var[]
657+
--
658+
|string
659+
|
660+
661+
a| [[quarkus-logging-json_quarkus-log-json-fields-trace-id-enabled]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-trace-id-enabled[`quarkus.log.json.fields.trace-id.enabled`]##
662+
ifdef::add-copy-button-to-config-props[]
663+
config_property_copy_button:+++quarkus.log.json.fields.trace-id.enabled+++[]
664+
endif::add-copy-button-to-config-props[]
665+
666+
667+
[.description]
668+
--
669+
Enable or disable the field.
670+
671+
672+
ifdef::add-copy-button-to-env-var[]
673+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++[]
674+
endif::add-copy-button-to-env-var[]
675+
ifndef::add-copy-button-to-env-var[]
676+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++`
677+
endif::add-copy-button-to-env-var[]
678+
--
679+
|boolean
680+
|
681+
682+
a| [[quarkus-logging-json_quarkus-log-json-fields-span-id-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-span-id-field-name[`quarkus.log.json.fields.span-id.field-name`]##
683+
ifdef::add-copy-button-to-config-props[]
684+
config_property_copy_button:+++quarkus.log.json.fields.span-id.field-name+++[]
685+
endif::add-copy-button-to-config-props[]
686+
687+
688+
[.description]
689+
--
690+
Used to change the json key for the field.
691+
692+
693+
ifdef::add-copy-button-to-env-var[]
694+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++[]
695+
endif::add-copy-button-to-env-var[]
696+
ifndef::add-copy-button-to-env-var[]
697+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++`
698+
endif::add-copy-button-to-env-var[]
699+
--
700+
|string
701+
|
702+
703+
a| [[quarkus-logging-json_quarkus-log-json-fields-span-id-enabled]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-span-id-enabled[`quarkus.log.json.fields.span-id.enabled`]##
704+
ifdef::add-copy-button-to-config-props[]
705+
config_property_copy_button:+++quarkus.log.json.fields.span-id.enabled+++[]
706+
endif::add-copy-button-to-config-props[]
707+
708+
709+
[.description]
710+
--
711+
Enable or disable the field.
712+
713+
714+
ifdef::add-copy-button-to-env-var[]
715+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++[]
716+
endif::add-copy-button-to-env-var[]
717+
ifndef::add-copy-button-to-env-var[]
718+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++`
719+
endif::add-copy-button-to-env-var[]
720+
--
721+
|boolean
722+
|
723+
640724
a| [[quarkus-logging-json_quarkus-log-json-fields-ndc-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-ndc-field-name[`quarkus.log.json.fields.ndc.field-name`]##
641725
ifdef::add-copy-button-to-config-props[]
642726
config_property_copy_button:+++quarkus.log.json.fields.ndc.field-name+++[]

docs/modules/ROOT/pages/includes/quarkus-logging-json_quarkus.log.adoc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,90 @@ endif::add-copy-button-to-env-var[]
637637
|boolean
638638
|`+++false+++`
639639

640+
a| [[quarkus-logging-json_quarkus-log-json-fields-trace-id-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-trace-id-field-name[`quarkus.log.json.fields.trace-id.field-name`]##
641+
ifdef::add-copy-button-to-config-props[]
642+
config_property_copy_button:+++quarkus.log.json.fields.trace-id.field-name+++[]
643+
endif::add-copy-button-to-config-props[]
644+
645+
646+
[.description]
647+
--
648+
Used to change the json key for the field.
649+
650+
651+
ifdef::add-copy-button-to-env-var[]
652+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++[]
653+
endif::add-copy-button-to-env-var[]
654+
ifndef::add-copy-button-to-env-var[]
655+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++`
656+
endif::add-copy-button-to-env-var[]
657+
--
658+
|string
659+
|
660+
661+
a| [[quarkus-logging-json_quarkus-log-json-fields-trace-id-enabled]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-trace-id-enabled[`quarkus.log.json.fields.trace-id.enabled`]##
662+
ifdef::add-copy-button-to-config-props[]
663+
config_property_copy_button:+++quarkus.log.json.fields.trace-id.enabled+++[]
664+
endif::add-copy-button-to-config-props[]
665+
666+
667+
[.description]
668+
--
669+
Enable or disable the field.
670+
671+
672+
ifdef::add-copy-button-to-env-var[]
673+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++[]
674+
endif::add-copy-button-to-env-var[]
675+
ifndef::add-copy-button-to-env-var[]
676+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++`
677+
endif::add-copy-button-to-env-var[]
678+
--
679+
|boolean
680+
|
681+
682+
a| [[quarkus-logging-json_quarkus-log-json-fields-span-id-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-span-id-field-name[`quarkus.log.json.fields.span-id.field-name`]##
683+
ifdef::add-copy-button-to-config-props[]
684+
config_property_copy_button:+++quarkus.log.json.fields.span-id.field-name+++[]
685+
endif::add-copy-button-to-config-props[]
686+
687+
688+
[.description]
689+
--
690+
Used to change the json key for the field.
691+
692+
693+
ifdef::add-copy-button-to-env-var[]
694+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++[]
695+
endif::add-copy-button-to-env-var[]
696+
ifndef::add-copy-button-to-env-var[]
697+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++`
698+
endif::add-copy-button-to-env-var[]
699+
--
700+
|string
701+
|
702+
703+
a| [[quarkus-logging-json_quarkus-log-json-fields-span-id-enabled]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-span-id-enabled[`quarkus.log.json.fields.span-id.enabled`]##
704+
ifdef::add-copy-button-to-config-props[]
705+
config_property_copy_button:+++quarkus.log.json.fields.span-id.enabled+++[]
706+
endif::add-copy-button-to-config-props[]
707+
708+
709+
[.description]
710+
--
711+
Enable or disable the field.
712+
713+
714+
ifdef::add-copy-button-to-env-var[]
715+
Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++[]
716+
endif::add-copy-button-to-env-var[]
717+
ifndef::add-copy-button-to-env-var[]
718+
Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++`
719+
endif::add-copy-button-to-env-var[]
720+
--
721+
|boolean
722+
|
723+
640724
a| [[quarkus-logging-json_quarkus-log-json-fields-ndc-field-name]] [.property-path]##link:#quarkus-logging-json_quarkus-log-json-fields-ndc-field-name[`quarkus.log.json.fields.ndc.field-name`]##
641725
ifdef::add-copy-button-to-config-props[]
642726
config_property_copy_button:+++quarkus.log.json.fields.ndc.field-name+++[]

runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ private List<JsonProvider> defaultFormat(Config config) {
8484
providers.add(new MessageJsonProvider(config.fields().message()));
8585
providers.add(new ThreadNameJsonProvider(config.fields().threadName()));
8686
providers.add(new ThreadIdJsonProvider(config.fields().threadId()));
87+
providers.add(new TraceIdJsonProvider(config.fields().traceId()));
88+
providers.add(new SpanIdJsonProvider(config.fields().spanId()));
8789
providers.add(new MDCJsonProvider(config.fields().mdc()));
8890
providers.add(new NDCJsonProvider(config.fields().ndc()));
8991
providers.add(new HostNameJsonProvider(config.fields().hostname()));
@@ -104,6 +106,8 @@ private List<JsonProvider> ecsFormat(Config config) {
104106
providers.add(new LogLevelJsonProvider(config.fields().level(), "log.level"));
105107
providers.add(new ThreadNameJsonProvider(config.fields().threadName(), "process.thread.name"));
106108
providers.add(new ThreadIdJsonProvider(config.fields().threadId(), "process.thread.id"));
109+
providers.add(new TraceIdJsonProvider(config.fields().traceId(), "trace.id"));
110+
providers.add(new SpanIdJsonProvider(config.fields().spanId(), "span.id"));
107111
providers.add(new MDCJsonProvider(config.fields().mdc()));
108112
providers.add(new HostNameJsonProvider(config.fields().hostname(), "host.name"));
109113
providers.add(new StackTraceJsonProvider(config.fields().stackTrace(), "error.stack_trace"));

runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ interface FieldsConfig {
123123
*/
124124
MDCConfig mdc();
125125

126+
/**
127+
* Options for mdc.traceId
128+
*/
129+
FieldConfig traceId();
130+
131+
/**
132+
* Options for mdc.spanId
133+
*/
134+
FieldConfig spanId();
135+
126136
/**
127137
* Options for ndc.
128138
*/
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.quarkiverse.loggingjson.providers;
2+
3+
import java.io.IOException;
4+
5+
import org.jboss.logmanager.ExtLogRecord;
6+
7+
import io.quarkiverse.loggingjson.Enabled;
8+
import io.quarkiverse.loggingjson.JsonGenerator;
9+
import io.quarkiverse.loggingjson.JsonProvider;
10+
import io.quarkiverse.loggingjson.JsonWritingUtils;
11+
import io.quarkiverse.loggingjson.config.Config;
12+
import io.quarkus.runtime.util.StringUtil;
13+
14+
public class SpanIdJsonProvider implements JsonProvider, Enabled {
15+
16+
private static final String MDC_FIELD_NAME = "spanId";
17+
private static final String DEFAULT_FIELD_NAME = "spanId";
18+
private final String fieldName;
19+
private final Config.FieldConfig config;
20+
21+
public SpanIdJsonProvider(Config.FieldConfig config) {
22+
this(config, DEFAULT_FIELD_NAME);
23+
}
24+
25+
public SpanIdJsonProvider(Config.FieldConfig config, String defaultName) {
26+
this.config = config;
27+
this.fieldName = config.fieldName().orElse(defaultName);
28+
}
29+
30+
@Override
31+
public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException {
32+
String spanId = event.getMdc(MDC_FIELD_NAME);
33+
if (!StringUtil.isNullOrEmpty(spanId)) {
34+
JsonWritingUtils.writeStringField(generator, fieldName, spanId);
35+
}
36+
}
37+
38+
@Override
39+
public boolean isEnabled() {
40+
return config.enabled().orElse(true);
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.quarkiverse.loggingjson.providers;
2+
3+
import java.io.IOException;
4+
5+
import org.jboss.logmanager.ExtLogRecord;
6+
7+
import io.quarkiverse.loggingjson.Enabled;
8+
import io.quarkiverse.loggingjson.JsonGenerator;
9+
import io.quarkiverse.loggingjson.JsonProvider;
10+
import io.quarkiverse.loggingjson.JsonWritingUtils;
11+
import io.quarkiverse.loggingjson.config.Config;
12+
import io.quarkus.runtime.util.StringUtil;
13+
14+
public class TraceIdJsonProvider implements JsonProvider, Enabled {
15+
16+
private static final String MDC_FIELD_NAME = "traceId";
17+
private static final String DEFAULT_FIELD_NAME = "traceId";
18+
private final String fieldName;
19+
private final Config.FieldConfig config;
20+
21+
public TraceIdJsonProvider(Config.FieldConfig config) {
22+
this(config, DEFAULT_FIELD_NAME);
23+
}
24+
25+
public TraceIdJsonProvider(Config.FieldConfig config, String defaultName) {
26+
this.config = config;
27+
this.fieldName = config.fieldName().orElse(defaultName);
28+
}
29+
30+
@Override
31+
public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException {
32+
String traceId = event.getMdc(MDC_FIELD_NAME);
33+
if (!StringUtil.isNullOrEmpty(traceId)) {
34+
JsonWritingUtils.writeStringField(generator, fieldName, traceId);
35+
}
36+
}
37+
38+
@Override
39+
public boolean isEnabled() {
40+
return config.enabled().orElse(true);
41+
}
42+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.quarkiverse.loggingjson.providers;
2+
3+
import java.util.Optional;
4+
import java.util.logging.Level;
5+
6+
import org.jboss.logmanager.ExtLogRecord;
7+
import org.junit.jupiter.api.Assertions;
8+
import org.junit.jupiter.api.Test;
9+
10+
import com.fasterxml.jackson.databind.JsonNode;
11+
12+
import io.quarkiverse.loggingjson.config.Config;
13+
14+
public class SpanIdJsonProviderJsonbTest extends JsonProviderBaseTest {
15+
@Override
16+
protected Type type() {
17+
return Type.JSONB;
18+
}
19+
20+
@Test
21+
void testDefaultConfig() throws Exception {
22+
Config.FieldConfig config = fieldConfig(Optional.empty(), Optional.empty());
23+
final SpanIdJsonProvider provider = new SpanIdJsonProvider(config);
24+
25+
final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", "");
26+
event.putMdc("spanId", "123456");
27+
final JsonNode result = getResultAsJsonNode(provider, event);
28+
29+
String traceId = result.findValue("spanId").asText();
30+
Assertions.assertNotNull(traceId);
31+
Assertions.assertFalse(traceId.isEmpty());
32+
Assertions.assertEquals("123456", traceId);
33+
Assertions.assertTrue(provider.isEnabled());
34+
35+
config = fieldConfig(Optional.empty(), Optional.of(true));
36+
Assertions.assertTrue(new SpanIdJsonProvider(config).isEnabled());
37+
}
38+
}

0 commit comments

Comments
 (0)