From 4fb46852b0f01770e329a3a99d3793783f8e7f94 Mon Sep 17 00:00:00 2001 From: Thierry Monney Date: Wed, 3 Jul 2024 18:24:29 +0200 Subject: [PATCH] Support "source" fields from LogRecord The following fields are now supported: * sourceClassName * sourceMethodName * sourceFileName * sourceLineNumber Since the LogRecord (or rather ExtLogRecord) class needs to inspect the call stack to determine these fields, and since this may be expensive, the new fields are disabled by default and must explicitly be enabled in the configuration. --- .../JsonDefaultFormatterBaseTest.java | 36 ++++- ...faultFormatterSourceFieldsJacksonTest.java | 26 ++++ ...DefaultFormatterSourceFieldsJsonbTest.java | 26 ++++ .../deployment/JsonECSFormatterBaseTest.java | 31 ++++- ...onECSFormatterSourceFieldsJacksonTest.java | 26 ++++ ...JsonECSFormatterSourceFieldsJsonbTest.java | 26 ++++ ...lication-json-source-fields-ecs.properties | 12 ++ .../application-json-source-fields.properties | 13 ++ .../ROOT/pages/includes/quarkus-log-json.adoc | 128 ++++++++++++++++++ .../loggingjson/LoggingJsonRecorder.java | 7 + .../loggingjson/config/Config.java | 20 +++ .../SourceClassNameJsonProvider.java | 36 +++++ .../providers/SourceFileNameJsonProvider.java | 36 +++++ .../SourceLineNumberJsonProvider.java | 36 +++++ .../SourceMethodNameJsonProvider.java | 36 +++++ .../SourceClassNameJsonProviderJsonbTest.java | 58 ++++++++ .../SourceFileNameJsonProviderJsonbTest.java | 58 ++++++++ ...SourceLineNumberJsonProviderJsonbTest.java | 54 ++++++++ ...SourceMethodNameJsonProviderJsonbTest.java | 58 ++++++++ ...ourceClassNameJsonProviderJacksonTest.java | 10 ++ ...SourceFileNameJsonProviderJacksonTest.java | 10 ++ ...urceLineNumberJsonProviderJacksonTest.java | 10 ++ ...urceMethodNameJsonProviderJacksonTest.java | 10 ++ 23 files changed, 759 insertions(+), 4 deletions(-) create mode 100644 deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJacksonTest.java create mode 100644 deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJsonbTest.java create mode 100644 deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJacksonTest.java create mode 100644 deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJsonbTest.java create mode 100644 deployment/src/test/resources/application-json-source-fields-ecs.properties create mode 100644 deployment/src/test/resources/application-json-source-fields.properties create mode 100644 runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProvider.java create mode 100644 runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProvider.java create mode 100644 runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProvider.java create mode 100644 runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProvider.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProviderJsonbTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProviderJsonbTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProviderJsonbTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProviderJsonbTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceClassNameJsonProviderJacksonTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceFileNameJsonProviderJacksonTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceLineNumberJsonProviderJacksonTest.java create mode 100644 runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceMethodNameJsonProviderJacksonTest.java diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterBaseTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterBaseTest.java index d987a82..74afc38 100644 --- a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterBaseTest.java +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterBaseTest.java @@ -2,6 +2,7 @@ import java.io.StringWriter; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Formatter; @@ -68,6 +69,14 @@ public static JsonFormatter getJsonFormatter() { } protected void testLogOutput() throws Exception { + testLogOutput(false); + } + + protected void testLogOutputWithSourceFields() throws Exception { + testLogOutput(true); + } + + private void testLogOutput(boolean includeSourceFields) throws Exception { JsonFormatter jsonFormatter = getJsonFormatter(); org.slf4j.Logger log = LoggerFactory.getLogger("JsonStructuredTest"); @@ -88,7 +97,7 @@ protected void testLogOutput() throws Exception { JsonNode jsonNode = mapper.readValue(lines[0], JsonNode.class); Assertions.assertTrue(jsonNode.isObject()); - List expectedFields = Arrays.asList( + List expectedFields = new ArrayList<>(Arrays.asList( "timestamp", "sequence", "loggerClassName", @@ -106,7 +115,15 @@ protected void testLogOutput() throws Exception { "errorMessage", "arg0", "structuredKey", - "serviceName"); + "serviceName")); + if (includeSourceFields) { + expectedFields.addAll(expectedFields.indexOf("errorMessage") + 1, Arrays.asList( + "sourceClassName", + "sourceMethodName", + "sourceFileName", + "sourceLineNumber")); + } + Assertions.assertEquals(expectedFields, ImmutableList.copyOf(jsonNode.fieldNames())); String timestamp = jsonNode.findValue("timestamp").asText(); @@ -163,5 +180,20 @@ protected void testLogOutput() throws Exception { Assertions.assertTrue(jsonNode.findValue("serviceName").isTextual()); Assertions.assertEquals("deployment-test", jsonNode.findValue("serviceName").asText()); + + if (includeSourceFields) { + Assertions.assertTrue(jsonNode.findValue("sourceClassName").isTextual()); + Assertions.assertEquals(JsonDefaultFormatterBaseTest.class.getName(), + jsonNode.findValue("sourceClassName").asText()); + + Assertions.assertTrue(jsonNode.findValue("sourceMethodName").isTextual()); + Assertions.assertEquals("testLogOutput", jsonNode.findValue("sourceMethodName").asText()); + + Assertions.assertTrue(jsonNode.findValue("sourceFileName").isTextual()); + Assertions.assertEquals("JsonDefaultFormatterBaseTest.java", jsonNode.findValue("sourceFileName").asText()); + + Assertions.assertTrue(jsonNode.findValue("sourceLineNumber").isInt()); + Assertions.assertEquals(86, jsonNode.findValue("sourceLineNumber").asInt()); + } } } diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJacksonTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJacksonTest.java new file mode 100644 index 0000000..3562402 --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJacksonTest.java @@ -0,0 +1,26 @@ +package io.quarkiverse.loggingjson.deployment; + +import java.util.Collections; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.test.QuarkusUnitTest; + +class JsonDefaultFormatterSourceFieldsJacksonTest extends JsonDefaultFormatterBaseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .setForcedDependencies(Collections.singletonList( + new AppArtifact("io.quarkus", "quarkus-jackson", System.getProperty("test.quarkus.version")))) + .withConfigurationResource("application-json-source-fields.properties"); + + @Test + void testFormatterUsingJackson() throws Exception { + testLogOutputWithSourceFields(); + } +} diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJsonbTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJsonbTest.java new file mode 100644 index 0000000..746a4ba --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonDefaultFormatterSourceFieldsJsonbTest.java @@ -0,0 +1,26 @@ +package io.quarkiverse.loggingjson.deployment; + +import java.util.Collections; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.test.QuarkusUnitTest; + +class JsonDefaultFormatterSourceFieldsJsonbTest extends JsonDefaultFormatterBaseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .setForcedDependencies(Collections.singletonList( + new AppArtifact("io.quarkus", "quarkus-jsonb", System.getProperty("test.quarkus.version")))) + .withConfigurationResource("application-json-source-fields.properties"); + + @Test + void testFormatterUsingJsonb() throws Exception { + testLogOutputWithSourceFields(); + } +} 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 2e7b660..f976a8d 100644 --- a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterBaseTest.java @@ -2,6 +2,7 @@ import java.io.StringWriter; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.*; @@ -64,6 +65,14 @@ protected String[] logLines() { } protected void testLogOutput() throws Exception { + testLogOutput(false); + } + + protected void testLogOutputWithSourceFields() throws Exception { + testLogOutput(true); + } + + protected void testLogOutput(boolean includeSourceFields) throws Exception { JsonFormatter jsonFormatter = getJsonFormatter(); org.slf4j.Logger log = LoggerFactory.getLogger("JsonStructuredTest"); @@ -84,7 +93,7 @@ protected void testLogOutput() throws Exception { JsonNode jsonNode = mapper.readValue(lines[0], JsonNode.class); Assertions.assertTrue(jsonNode.isObject()); - List expectedFields = Arrays.asList( + List expectedFields = new ArrayList<>(Arrays.asList( "@timestamp", "log.logger", "log.level", @@ -99,7 +108,14 @@ protected void testLogOutput() throws Exception { "structuredKey", "service.name", "message", - "ecs.version"); + "ecs.version")); + if (includeSourceFields) { + expectedFields.addAll(expectedFields.indexOf("log.level") + 1, Arrays.asList( + "log.origin.file.name", + "log.origin.file.line", + "log.origin.function")); + } + Assertions.assertEquals(expectedFields, ImmutableList.copyOf(jsonNode.fieldNames())); String timestamp = jsonNode.findValue("@timestamp").asText(); @@ -149,5 +165,16 @@ protected void testLogOutput() throws Exception { Assertions.assertTrue(jsonNode.findValue("ecs.version").isTextual()); Assertions.assertTrue(jsonNode.findValue("ecs.version").asText().matches("^[0-9]*\\.[0-9]*\\.[0-9]*$")); + + if (includeSourceFields) { + Assertions.assertTrue(jsonNode.findValue("log.origin.function").isTextual()); + Assertions.assertEquals("testLogOutput", jsonNode.findValue("log.origin.function").asText()); + + Assertions.assertTrue(jsonNode.findValue("log.origin.file.name").isTextual()); + Assertions.assertEquals("JsonECSFormatterBaseTest.java", jsonNode.findValue("log.origin.file.name").asText()); + + Assertions.assertTrue(jsonNode.findValue("log.origin.file.line").isInt()); + Assertions.assertEquals(86, jsonNode.findValue("log.origin.file.line").asInt()); + } } } diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJacksonTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJacksonTest.java new file mode 100644 index 0000000..8e3f7f3 --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJacksonTest.java @@ -0,0 +1,26 @@ +package io.quarkiverse.loggingjson.deployment; + +import java.util.Collections; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.test.QuarkusUnitTest; + +class JsonECSFormatterSourceFieldsJacksonTest extends JsonECSFormatterBaseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .setForcedDependencies(Collections.singletonList( + new AppArtifact("io.quarkus", "quarkus-jackson-deployment", System.getProperty("test.quarkus.version")))) + .withConfigurationResource("application-json-source-fields-ecs.properties"); + + @Test + void testFormatterUsingJackson() throws Exception { + testLogOutputWithSourceFields(); + } +} diff --git a/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJsonbTest.java b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJsonbTest.java new file mode 100644 index 0000000..cd951dd --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/loggingjson/deployment/JsonECSFormatterSourceFieldsJsonbTest.java @@ -0,0 +1,26 @@ +package io.quarkiverse.loggingjson.deployment; + +import java.util.Collections; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.bootstrap.model.AppArtifact; +import io.quarkus.test.QuarkusUnitTest; + +class JsonECSFormatterSourceFieldsJsonbTest extends JsonECSFormatterBaseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .setForcedDependencies(Collections.singletonList( + new AppArtifact("io.quarkus", "quarkus-jsonb-deployment", System.getProperty("test.quarkus.version")))) + .withConfigurationResource("application-json-source-fields-ecs.properties"); + + @Test + void testFormatterUsingJsonb() throws Exception { + testLogOutputWithSourceFields(); + } +} diff --git a/deployment/src/test/resources/application-json-source-fields-ecs.properties b/deployment/src/test/resources/application-json-source-fields-ecs.properties new file mode 100644 index 0000000..bf7d2d7 --- /dev/null +++ b/deployment/src/test/resources/application-json-source-fields-ecs.properties @@ -0,0 +1,12 @@ +quarkus.log.level=INFO +quarkus.log.console.enable=true +quarkus.log.console.level=WARNING +quarkus.log.json.console.enable=true +quarkus.log.json.log-format=ecs +quarkus.log.json.fields.arguments.include-non-structured-arguments=true +quarkus.log.json.additional-field."service.name".value=deployment-test + +# these fields are disabled by default +quarkus.log.json.fields.source-method-name.enabled=true +quarkus.log.json.fields.source-file-name.enabled=true +quarkus.log.json.fields.source-line-number.enabled=true \ No newline at end of file diff --git a/deployment/src/test/resources/application-json-source-fields.properties b/deployment/src/test/resources/application-json-source-fields.properties new file mode 100644 index 0000000..b73ac14 --- /dev/null +++ b/deployment/src/test/resources/application-json-source-fields.properties @@ -0,0 +1,13 @@ +quarkus.log.level=INFO +quarkus.log.console.enable=true +quarkus.log.console.level=WARNING +quarkus.log.json.console.enable=true +quarkus.log.json.fields.arguments.include-non-structured-arguments=true +quarkus.log.json.additional-field.serviceName.value=deployment-test +quarkus.log.json.file.enable=true + +# these fields are disabled by default +quarkus.log.json.fields.source-class-name.enabled=true +quarkus.log.json.fields.source-method-name.enabled=true +quarkus.log.json.fields.source-file-name.enabled=true +quarkus.log.json.fields.source-line-number.enabled=true diff --git a/docs/modules/ROOT/pages/includes/quarkus-log-json.adoc b/docs/modules/ROOT/pages/includes/quarkus-log-json.adoc index 7539e35..484fb5f 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-log-json.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-log-json.adoc @@ -666,6 +666,134 @@ endif::add-copy-button-to-env-var[] | +a| [[quarkus-log-json_quarkus.log.json.fields.source-class-name.field-name]]`link:#quarkus-log-json_quarkus.log.json.fields.source-class-name.field-name[quarkus.log.json.fields.source-class-name.field-name]` + +[.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_SOURCE_CLASS_NAME_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_CLASS_NAME_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +--|string +| + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-class-name.enabled]]`link:#quarkus-log-json_quarkus.log.json.fields.source-class-name.enabled[quarkus.log.json.fields.source-class-name.enabled]` + +[.description] +-- +Enable or disable the field. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SOURCE_CLASS_NAME_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_CLASS_NAME_ENABLED+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`false` + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-method-name.field-name]]`link:#quarkus-log-json_quarkus.log.json.fields.source-method-name.field-name[quarkus.log.json.fields.source-method-name.field-name]` + +[.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_SOURCE_METHOD_NAME_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_METHOD_NAME_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +--|string +| + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-method-name.enabled]]`link:#quarkus-log-json_quarkus.log.json.fields.source-method-name.enabled[quarkus.log.json.fields.source-method-name.enabled]` + +[.description] +-- +Enable or disable the field. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SOURCE_METHOD_NAME_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_METHOD_NAME_ENABLED+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`false` + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-file-name.field-name]]`link:#quarkus-log-json_quarkus.log.json.fields.source-file-name.field-name[quarkus.log.json.fields.source-file-name.field-name]` + +[.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_SOURCE_FILE_NAME_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_FILE_NAME_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +--|string +| + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-file-name.enabled]]`link:#quarkus-log-json_quarkus.log.json.fields.source-file-name.enabled[quarkus.log.json.fields.source-file-name.enabled]` + +[.description] +-- +Enable or disable the field. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SOURCE_FILE_NAME_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_FILE_NAME_ENABLED+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`false` + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-line-number.field-name]]`link:#quarkus-log-json_quarkus.log.json.fields.source-line-number.field-name[quarkus.log.json.fields.source-line-number.field-name]` + +[.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_SOURCE_LINE_NUMBER_FIELD_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_LINE_NUMBER_FIELD_NAME+++` +endif::add-copy-button-to-env-var[] +--|string +| + + +a| [[quarkus-log-json_quarkus.log.json.fields.source-line-number.enabled]]`link:#quarkus-log-json_quarkus.log.json.fields.source-line-number.enabled[quarkus.log.json.fields.source-line-number.enabled]` + +[.description] +-- +Enable or disable the field. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_LOG_JSON_FIELDS_SOURCE_LINE_NUMBER_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_LOG_JSON_FIELDS_SOURCE_LINE_NUMBER_ENABLED+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`false` + + a| [[quarkus-log-json_quarkus.log.json.pretty-print]]`link:#quarkus-log-json_quarkus.log.json.pretty-print[quarkus.log.json.pretty-print]` [.description] diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java b/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java index 9ea5873..94f586d 100644 --- a/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/LoggingJsonRecorder.java @@ -84,6 +84,10 @@ private List defaultFormat(Config config) { providers.add(new StackTraceJsonProvider(config.fields.stackTrace)); providers.add(new ErrorTypeJsonProvider(config.fields.errorType)); providers.add(new ErrorMessageJsonProvider(config.fields.errorMessage)); + providers.add(new SourceClassNameJsonProvider(config.fields.sourceClassName)); + providers.add(new SourceMethodNameJsonProvider(config.fields.sourceMethodName)); + providers.add(new SourceFileNameJsonProvider(config.fields.sourceFileName)); + providers.add(new SourceLineNumberJsonProvider(config.fields.sourceLineNumber)); providers.add(new ArgumentsJsonProvider(config.fields.arguments)); providers.add(new AdditionalFieldsJsonProvider(config.additionalField)); return providers; @@ -94,6 +98,9 @@ private List ecsFormat(Config config) { providers.add(new TimestampJsonProvider(config.fields.timestamp, "@timestamp")); providers.add(new LoggerNameJsonProvider(config.fields.loggerName, "log.logger")); providers.add(new LogLevelJsonProvider(config.fields.level, "log.level")); + providers.add(new SourceFileNameJsonProvider(config.fields.sourceFileName, "log.origin.file.name")); + providers.add(new SourceLineNumberJsonProvider(config.fields.sourceLineNumber, "log.origin.file.line")); + providers.add(new SourceMethodNameJsonProvider(config.fields.sourceMethodName, "log.origin.function")); providers.add(new ThreadNameJsonProvider(config.fields.threadName, "process.thread.name")); providers.add(new ThreadIdJsonProvider(config.fields.threadId, "process.thread.id")); providers.add(new MDCJsonProvider(config.fields.mdc)); 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 5bdc7fc..aca653d 100644 --- a/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/config/Config.java @@ -137,6 +137,26 @@ public static class FieldsConfig { */ @ConfigItem public FieldConfig errorMessage; + /** + * Options for sourceClassName. + */ + @ConfigItem + public FieldConfig sourceClassName; + /** + * Options for sourceMethodName. + */ + @ConfigItem + public FieldConfig sourceMethodName; + /** + * Options for sourceFileName. + */ + @ConfigItem + public FieldConfig sourceFileName; + /** + * Options for sourceLineNumber. + */ + @ConfigItem + public FieldConfig sourceLineNumber; } @ConfigGroup diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProvider.java new file mode 100644 index 0000000..9ee219c --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProvider.java @@ -0,0 +1,36 @@ +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; + +public class SourceClassNameJsonProvider implements JsonProvider, Enabled { + + private final String fieldName; + private final Config.FieldConfig config; + + public SourceClassNameJsonProvider(Config.FieldConfig config) { + this(config, "sourceClassName"); + } + + public SourceClassNameJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName.orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + JsonWritingUtils.writeStringField(generator, fieldName, event.getSourceClassName()); + } + + @Override + public boolean isEnabled() { + return config.enabled.orElse(false); + } +} diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProvider.java new file mode 100644 index 0000000..2db7d7f --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProvider.java @@ -0,0 +1,36 @@ +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; + +public class SourceFileNameJsonProvider implements JsonProvider, Enabled { + + private final String fieldName; + private final Config.FieldConfig config; + + public SourceFileNameJsonProvider(Config.FieldConfig config) { + this(config, "sourceFileName"); + } + + public SourceFileNameJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName.orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + JsonWritingUtils.writeStringField(generator, fieldName, event.getSourceFileName()); + } + + @Override + public boolean isEnabled() { + return config.enabled.orElse(false); + } +} diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProvider.java new file mode 100644 index 0000000..afeb8d2 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProvider.java @@ -0,0 +1,36 @@ +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; + +public class SourceLineNumberJsonProvider implements JsonProvider, Enabled { + + private final String fieldName; + private final Config.FieldConfig config; + + public SourceLineNumberJsonProvider(Config.FieldConfig config) { + this(config, "sourceLineNumber"); + } + + public SourceLineNumberJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName.orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + JsonWritingUtils.writeNumberField(generator, fieldName, event.getSourceLineNumber()); + } + + @Override + public boolean isEnabled() { + return config.enabled.orElse(false); + } +} diff --git a/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProvider.java b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProvider.java new file mode 100644 index 0000000..a325ec6 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProvider.java @@ -0,0 +1,36 @@ +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; + +public class SourceMethodNameJsonProvider implements JsonProvider, Enabled { + + private final String fieldName; + private final Config.FieldConfig config; + + public SourceMethodNameJsonProvider(Config.FieldConfig config) { + this(config, "sourceMethodName"); + } + + public SourceMethodNameJsonProvider(Config.FieldConfig config, String defaultName) { + this.config = config; + this.fieldName = config.fieldName.orElse(defaultName); + } + + @Override + public void writeTo(JsonGenerator generator, ExtLogRecord event) throws IOException { + JsonWritingUtils.writeStringField(generator, fieldName, event.getSourceMethodName()); + } + + @Override + public boolean isEnabled() { + return config.enabled.orElse(false); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProviderJsonbTest.java new file mode 100644 index 0000000..ab0f639 --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceClassNameJsonProviderJsonbTest.java @@ -0,0 +1,58 @@ +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 SourceClassNameJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.empty(); + config.enabled = Optional.empty(); + final SourceClassNameJsonProvider provider = new SourceClassNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceClassName("SourceClassNameJsonProviderTest"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String className = result.findValue("sourceClassName").asText(); + Assertions.assertNotNull(className); + Assertions.assertFalse(className.isEmpty()); + Assertions.assertEquals("SourceClassNameJsonProviderTest", className); + Assertions.assertFalse(provider.isEnabled()); + } + + @Test + void testCustomConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.of("class"); + config.enabled = Optional.of(false); + final SourceClassNameJsonProvider provider = new SourceClassNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceClassName("SourceClassNameJsonProviderTest"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String className = result.findValue("class").asText(); + Assertions.assertNotNull(className); + Assertions.assertFalse(className.isEmpty()); + Assertions.assertEquals("SourceClassNameJsonProviderTest", className); + Assertions.assertFalse(provider.isEnabled()); + + config.enabled = Optional.of(true); + Assertions.assertTrue(new SourceClassNameJsonProvider(config).isEnabled()); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProviderJsonbTest.java new file mode 100644 index 0000000..8f2de4a --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceFileNameJsonProviderJsonbTest.java @@ -0,0 +1,58 @@ +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 SourceFileNameJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.empty(); + config.enabled = Optional.empty(); + final SourceFileNameJsonProvider provider = new SourceFileNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceFileName("SourceFileNameJsonProviderTest.java"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String fileName = result.findValue("sourceFileName").asText(); + Assertions.assertNotNull(fileName); + Assertions.assertFalse(fileName.isEmpty()); + Assertions.assertEquals("SourceFileNameJsonProviderTest.java", fileName); + Assertions.assertFalse(provider.isEnabled()); + } + + @Test + void testCustomConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.of("file"); + config.enabled = Optional.of(false); + final SourceFileNameJsonProvider provider = new SourceFileNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceFileName("SourceFileNameJsonProviderTest.java"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String fileName = result.findValue("file").asText(); + Assertions.assertNotNull(fileName); + Assertions.assertFalse(fileName.isEmpty()); + Assertions.assertEquals("SourceFileNameJsonProviderTest.java", fileName); + Assertions.assertFalse(provider.isEnabled()); + + config.enabled = Optional.of(true); + Assertions.assertTrue(new SourceFileNameJsonProvider(config).isEnabled()); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProviderJsonbTest.java new file mode 100644 index 0000000..827de6a --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceLineNumberJsonProviderJsonbTest.java @@ -0,0 +1,54 @@ +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 SourceLineNumberJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.empty(); + config.enabled = Optional.empty(); + final SourceLineNumberJsonProvider provider = new SourceLineNumberJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceLineNumber(42); + final JsonNode result = getResultAsJsonNode(provider, event); + + int lineNumber = result.findValue("sourceLineNumber").asInt(); + Assertions.assertEquals(42, lineNumber); + Assertions.assertFalse(provider.isEnabled()); + } + + @Test + void testCustomConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.of("line"); + config.enabled = Optional.of(false); + final SourceLineNumberJsonProvider provider = new SourceLineNumberJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceLineNumber(42); + final JsonNode result = getResultAsJsonNode(provider, event); + + int lineNumber = result.findValue("line").asInt(); + Assertions.assertEquals(42, lineNumber); + Assertions.assertFalse(provider.isEnabled()); + + config.enabled = Optional.of(true); + Assertions.assertTrue(new SourceLineNumberJsonProvider(config).isEnabled()); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProviderJsonbTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProviderJsonbTest.java new file mode 100644 index 0000000..8b976dd --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/SourceMethodNameJsonProviderJsonbTest.java @@ -0,0 +1,58 @@ +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 SourceMethodNameJsonProviderJsonbTest extends JsonProviderBaseTest { + @Override + protected Type type() { + return Type.JSONB; + } + + @Test + void testDefaultConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.empty(); + config.enabled = Optional.empty(); + final SourceMethodNameJsonProvider provider = new SourceMethodNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceMethodName("foo"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String methodName = result.findValue("sourceMethodName").asText(); + Assertions.assertNotNull(methodName); + Assertions.assertFalse(methodName.isEmpty()); + Assertions.assertEquals("foo", methodName); + Assertions.assertFalse(provider.isEnabled()); + } + + @Test + void testCustomConfig() throws Exception { + final Config.FieldConfig config = new Config.FieldConfig(); + config.fieldName = Optional.of("method"); + config.enabled = Optional.of(false); + final SourceMethodNameJsonProvider provider = new SourceMethodNameJsonProvider(config); + + final ExtLogRecord event = new ExtLogRecord(Level.ALL, "", ""); + event.setSourceMethodName("foo"); + final JsonNode result = getResultAsJsonNode(provider, event); + + String methodName = result.findValue("method").asText(); + Assertions.assertNotNull(methodName); + Assertions.assertFalse(methodName.isEmpty()); + Assertions.assertEquals("foo", methodName); + Assertions.assertFalse(provider.isEnabled()); + + config.enabled = Optional.of(true); + Assertions.assertTrue(new SourceMethodNameJsonProvider(config).isEnabled()); + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceClassNameJsonProviderJacksonTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceClassNameJsonProviderJacksonTest.java new file mode 100644 index 0000000..a36ea60 --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceClassNameJsonProviderJacksonTest.java @@ -0,0 +1,10 @@ +package io.quarkiverse.loggingjson.providers.jackson; + +import io.quarkiverse.loggingjson.providers.SourceClassNameJsonProviderJsonbTest; + +public class SourceClassNameJsonProviderJacksonTest extends SourceClassNameJsonProviderJsonbTest { + @Override + protected Type type() { + return Type.JACKSON; + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceFileNameJsonProviderJacksonTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceFileNameJsonProviderJacksonTest.java new file mode 100644 index 0000000..055bf5f --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceFileNameJsonProviderJacksonTest.java @@ -0,0 +1,10 @@ +package io.quarkiverse.loggingjson.providers.jackson; + +import io.quarkiverse.loggingjson.providers.SourceFileNameJsonProviderJsonbTest; + +public class SourceFileNameJsonProviderJacksonTest extends SourceFileNameJsonProviderJsonbTest { + @Override + protected Type type() { + return Type.JACKSON; + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceLineNumberJsonProviderJacksonTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceLineNumberJsonProviderJacksonTest.java new file mode 100644 index 0000000..4469706 --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceLineNumberJsonProviderJacksonTest.java @@ -0,0 +1,10 @@ +package io.quarkiverse.loggingjson.providers.jackson; + +import io.quarkiverse.loggingjson.providers.SourceLineNumberJsonProviderJsonbTest; + +public class SourceLineNumberJsonProviderJacksonTest extends SourceLineNumberJsonProviderJsonbTest { + @Override + protected Type type() { + return Type.JACKSON; + } +} diff --git a/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceMethodNameJsonProviderJacksonTest.java b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceMethodNameJsonProviderJacksonTest.java new file mode 100644 index 0000000..78b0aed --- /dev/null +++ b/runtime/src/test/java/io/quarkiverse/loggingjson/providers/jackson/SourceMethodNameJsonProviderJacksonTest.java @@ -0,0 +1,10 @@ +package io.quarkiverse.loggingjson.providers.jackson; + +import io.quarkiverse.loggingjson.providers.SourceMethodNameJsonProviderJsonbTest; + +public class SourceMethodNameJsonProviderJacksonTest extends SourceMethodNameJsonProviderJsonbTest { + @Override + protected Type type() { + return Type.JACKSON; + } +}