diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java index 0e68d27..5b972e8 100644 --- a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java @@ -64,14 +64,16 @@ protected String[] logLines() { } protected void testLogOutput() throws Exception { - JsonFormatter jsonFormatter = getJsonFormatter(); + getJsonFormatter(); org.slf4j.Logger log = LoggerFactory.getLogger("JsonStructuredTest"); OffsetDateTime beforeFirstLog = OffsetDateTime.now().minusSeconds(1); - try (MDC.MDCCloseable closeable = MDC.putCloseable("mdcKey", "mdcVal")) { - log.error("Test {}", "message", - KeyValueStructuredArgument.kv("structuredKey", "structuredValue"), + try ( + MDC.MDCCloseable closeable = MDC.putCloseable("mdcKey", "mdcVal"); + MDC.MDCCloseable traceId = MDC.putCloseable("traceId", "123-456-789"); + MDC.MDCCloseable spanId = MDC.putCloseable("spanId", "987-654-321");) { + log.error("Test {}", "message", KeyValueStructuredArgument.kv("structuredKey", "structuredValue"), new RuntimeException("Testing stackTrace")); } @@ -88,6 +90,8 @@ protected void testLogOutput() throws Exception { "log.level", "process.thread.name", "process.thread.id", + "trace.id", + "span.id", "mdc", "host.name", "error.stack_trace", @@ -124,6 +128,12 @@ protected void testLogOutput() throws Exception { Assertions.assertNotNull(jsonNode.findValue("mdc").findValue("mdcKey")); Assertions.assertEquals("mdcVal", jsonNode.findValue("mdc").findValue("mdcKey").asText()); + Assertions.assertTrue(jsonNode.findValue("trace.id").isTextual()); + Assertions.assertEquals("123-456-789", jsonNode.findValue("trace.id").asText()); + + Assertions.assertTrue(jsonNode.findValue("span.id").isTextual()); + Assertions.assertEquals("987-654-321", jsonNode.findValue("span.id").asText()); + Assertions.assertTrue(jsonNode.findValue("host.name").isTextual()); Assertions.assertNotEquals("", jsonNode.findValue("host.name").asText()); diff --git a/docs/modules/ROOT/pages/includes/quarkus-logging-json.adoc b/docs/modules/ROOT/pages/includes/quarkus-logging-json.adoc index 1c3494e..bf0775b 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-logging-json.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-logging-json.adoc @@ -637,6 +637,90 @@ endif::add-copy-button-to-env-var[] |boolean |`+++false+++` +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.trace-id.field-name+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Used to change the json key for the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.trace-id.enabled+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable or disable the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.span-id.field-name+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Used to change the json key for the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.span-id.enabled+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable or disable the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + 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`]## ifdef::add-copy-button-to-config-props[] config_property_copy_button:+++quarkus.log.json.fields.ndc.field-name+++[] diff --git a/docs/modules/ROOT/pages/includes/quarkus-logging-json_quarkus.log.adoc b/docs/modules/ROOT/pages/includes/quarkus-logging-json_quarkus.log.adoc index 1c3494e..bf0775b 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-logging-json_quarkus.log.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-logging-json_quarkus.log.adoc @@ -637,6 +637,90 @@ endif::add-copy-button-to-env-var[] |boolean |`+++false+++` +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.trace-id.field-name+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Used to change the json key for the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.trace-id.enabled+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable or disable the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_TRACE_ID_ENABLED+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.span-id.field-name+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Used to change the json key for the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +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`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.log.json.fields.span-id.enabled+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable or disable the field. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SPAN_ID_ENABLED+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + 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`]## ifdef::add-copy-button-to-config-props[] config_property_copy_button:+++quarkus.log.json.fields.ndc.field-name+++[] diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java b/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java index 30c6245..458f925 100644 --- a/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java @@ -84,6 +84,8 @@ private List defaultFormat(Config config) { providers.add(new MessageJsonProvider(config.fields().message())); providers.add(new ThreadNameJsonProvider(config.fields().threadName())); providers.add(new ThreadIdJsonProvider(config.fields().threadId())); + providers.add(new TraceIdJsonProvider(config.fields().traceId())); + providers.add(new SpanIdJsonProvider(config.fields().spanId())); providers.add(new MDCJsonProvider(config.fields().mdc())); providers.add(new NDCJsonProvider(config.fields().ndc())); providers.add(new HostNameJsonProvider(config.fields().hostname())); @@ -104,6 +106,8 @@ private List ecsFormat(Config config) { providers.add(new LogLevelJsonProvider(config.fields().level(), "log.level")); providers.add(new ThreadNameJsonProvider(config.fields().threadName(), "process.thread.name")); providers.add(new ThreadIdJsonProvider(config.fields().threadId(), "process.thread.id")); + providers.add(new TraceIdJsonProvider(config.fields().traceId(), "trace.id")); + providers.add(new SpanIdJsonProvider(config.fields().spanId(), "span.id")); providers.add(new MDCJsonProvider(config.fields().mdc())); providers.add(new HostNameJsonProvider(config.fields().hostname(), "host.name")); providers.add(new StackTraceJsonProvider(config.fields().stackTrace(), "error.stack_trace")); diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java b/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java index 5c6ee1b..7f5efa7 100644 --- a/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java @@ -123,6 +123,16 @@ interface FieldsConfig { */ MDCConfig mdc(); + /** + * Options for mdc.traceId + */ + FieldConfig traceId(); + + /** + * Options for mdc.spanId + */ + FieldConfig spanId(); + /** * Options for ndc. */ diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProvider.java new file mode 100644 index 0000000..39aa749 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProvider.java @@ -0,0 +1,42 @@ +package io.quarkiverse.loggingjson.providers; + +import java.io.IOException; + +import org.jboss.logmanager.ExtLogRecord; + +import io.quarkiverse.loggingjson.Enabled; +import io.quarkiverse.loggingjson.JsonGenerator; +import io.quarkiverse.loggingjson.JsonProvider; +import io.quarkiverse.loggingjson.JsonWritingUtils; +import io.quarkiverse.loggingjson.config.Config; +import io.quarkus.runtime.util.StringUtil; + +public class SpanIdJsonProvider implements JsonProvider, Enabled { + + private static final String MDC_FIELD_NAME = "spanId"; + private static final String DEFAULT_FIELD_NAME = "spanId"; + private final String fieldName; + private final Config.FieldConfig config; + + public SpanIdJsonProvider(Config.FieldConfig config) { + this(config, DEFAULT_FIELD_NAME); + } + + public SpanIdJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName().orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + String spanId = event.getMdc(MDC_FIELD_NAME); + if (!StringUtil.isNullOrEmpty(spanId)) { + JsonWritingUtils.writeStringField(generator, fieldName, spanId); + } + } + + @Override + public boolean isEnabled() { + return config.enabled().orElse(true); + } +} diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProvider.java new file mode 100644 index 0000000..1bd6373 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProvider.java @@ -0,0 +1,42 @@ +package io.quarkiverse.loggingjson.providers; + +import java.io.IOException; + +import org.jboss.logmanager.ExtLogRecord; + +import io.quarkiverse.loggingjson.Enabled; +import io.quarkiverse.loggingjson.JsonGenerator; +import io.quarkiverse.loggingjson.JsonProvider; +import io.quarkiverse.loggingjson.JsonWritingUtils; +import io.quarkiverse.loggingjson.config.Config; +import io.quarkus.runtime.util.StringUtil; + +public class TraceIdJsonProvider implements JsonProvider, Enabled { + + private static final String MDC_FIELD_NAME = "traceId"; + private static final String DEFAULT_FIELD_NAME = "traceId"; + private final String fieldName; + private final Config.FieldConfig config; + + public TraceIdJsonProvider(Config.FieldConfig config) { + this(config, DEFAULT_FIELD_NAME); + } + + public TraceIdJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName().orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + String traceId = event.getMdc(MDC_FIELD_NAME); + if (!StringUtil.isNullOrEmpty(traceId)) { + JsonWritingUtils.writeStringField(generator, fieldName, traceId); + } + } + + @Override + public boolean isEnabled() { + return config.enabled().orElse(true); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProviderJsonbTest.java new file mode 100644 index 0000000..3372b3d --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SpanIdJsonProviderJsonbTest.java @@ -0,0 +1,38 @@ +package io.quarkiverse.loggingjson.providers; + +import java.util.Optional; +import java.util.logging.Level; + +import org.jboss.logmanager.ExtLogRecord; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; + +import io.quarkiverse.loggingjson.config.Config; + +public class SpanIdJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + Config.FieldConfig config = fieldConfig(Optional.empty(), Optional.empty()); + final SpanIdJsonProvider provider = new SpanIdJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.putMdc("spanId", "123456"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String traceId = result.findValue("spanId").asText(); + Assertions.assertNotNull(traceId); + Assertions.assertFalse(traceId.isEmpty()); + Assertions.assertEquals("123456", traceId); + Assertions.assertTrue(provider.isEnabled()); + + config = fieldConfig(Optional.empty(), Optional.of(true)); + Assertions.assertTrue(new SpanIdJsonProvider(config).isEnabled()); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProviderJsonbTest.java new file mode 100644 index 0000000..8dbcfad --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/TraceIdJsonProviderJsonbTest.java @@ -0,0 +1,38 @@ +package io.quarkiverse.loggingjson.providers; + +import java.util.Optional; +import java.util.logging.Level; + +import org.jboss.logmanager.ExtLogRecord; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; + +import io.quarkiverse.loggingjson.config.Config; + +public class TraceIdJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + Config.FieldConfig config = fieldConfig(Optional.empty(), Optional.empty()); + final TraceIdJsonProvider provider = new TraceIdJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.putMdc("traceId", "123456"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String traceId = result.findValue("traceId").asText(); + Assertions.assertNotNull(traceId); + Assertions.assertFalse(traceId.isEmpty()); + Assertions.assertEquals("123456", traceId); + Assertions.assertTrue(provider.isEnabled()); + + config = fieldConfig(Optional.empty(), Optional.of(true)); + Assertions.assertTrue(new TraceIdJsonProvider(config).isEnabled()); + } +}