diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java index a39a66b454c..c4169275bd3 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java @@ -342,6 +342,8 @@ public static class PreviewConfiguration { public boolean captureLogbackCodeAttributes; + public boolean captureLogbackKeyValues; + public boolean captureLogbackMarker; public boolean captureLog4jMarker; diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java index 5d17bb98b64..8d3107548a9 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java @@ -66,6 +66,8 @@ public Map apply(ConfigProperties otelConfig) { // enable capturing all mdc properties properties.put( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", "*"); + properties.put( + "otel.instrumentation.logback-appender.capture-key-value-pair-attributes", "*"); properties.put("otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes", "*"); properties.put( "otel.instrumentation.log4j-appender.experimental.capture-context-data-attributes", "*"); @@ -279,6 +281,11 @@ private static void enableInstrumentations( properties.put( "otel.instrumentation.logback-appender.experimental.capture-marker-attribute", "true"); } + if (config.preview.captureLogbackKeyValues) { + properties.put( + "otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes", + "true"); + } if (config.preview.captureLog4jMarker) { properties.put( "otel.instrumentation.log4j-appender.experimental.capture-marker-attribute", "true"); diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/SecondEntryPoint.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/SecondEntryPoint.java index ac2fe85bf7f..d5b7f9325db 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/SecondEntryPoint.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/SecondEntryPoint.java @@ -488,6 +488,10 @@ private static Set initStatsbeatFeatureSet(Configuration config) { if (config.preview.captureLogbackMarker) { featureList.add(Feature.PREVIEW_CAPTURE_LOGBACK_MARKER); } + if (config.preview.captureLogbackKeyValues) { +// TODO: uncomment when the new feature is merged and released in azure-sdk-for-java +// featureList.add(Feature.PREVIEW_CAPTURE_LOGBACK_KEY_VALUES); + } if (config.preview.captureLog4jMarker) { featureList.add(Feature.PREVIEW_CAPTURE_LOG4J_MARKER); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 0965cfa6d4e..97863296b1e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -103,6 +103,7 @@ hideFromDependabot(":smoke-tests:apps:LiveMetrics") hideFromDependabot(":smoke-tests:apps:Log4j1") hideFromDependabot(":smoke-tests:apps:Log4j2") hideFromDependabot(":smoke-tests:apps:Logback") +hideFromDependabot(":smoke-tests:apps:LogbackFluentLogging") hideFromDependabot(":smoke-tests:apps:Micrometer") hideFromDependabot(":smoke-tests:apps:MongoDB") hideFromDependabot(":smoke-tests:apps:NonDaemonThreads") diff --git a/smoke-tests/apps/LogbackFluentLogging/build.gradle.kts b/smoke-tests/apps/LogbackFluentLogging/build.gradle.kts new file mode 100644 index 00000000000..c47104be603 --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("ai.smoke-test-war") +} + +configurations.all { + val slf4jVersion = "2.0.17" + val logbackVersion = "1.5.20" + resolutionStrategy.force("org.slf4j:slf4j-api:${slf4jVersion}") + resolutionStrategy.force("org.slf4j:log4j-over-slf4j:${slf4jVersion}") + resolutionStrategy.force("org.slf4j:jcl-over-slf4j:${slf4jVersion}") + resolutionStrategy.force("org.slf4j:jul-to-slf4j:${slf4jVersion}") + resolutionStrategy.force("ch.qos.logback:logback-classic:${logbackVersion}") +} + +dependencies { + implementation("ch.qos.logback:logback-classic") +} + diff --git a/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/HealthCheckServlet.java b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/HealthCheckServlet.java new file mode 100644 index 00000000000..94cfbe9c118 --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/HealthCheckServlet.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +// this is used by the test infra in order to know when it's ok to start running the tests +@WebServlet("") +public class HealthCheckServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) {} +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingServlet.java b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingServlet.java new file mode 100644 index 00000000000..4dd9fa6f19a --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingServlet.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +@WebServlet("/test") +public class LogbackFluentLoggingServlet extends HttpServlet { + + private static final Logger logger = LoggerFactory.getLogger("smoketestapp"); + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + Marker marker = MarkerFactory.getMarker("aMarker"); + MDC.put("MDC key", "MDC value"); + logger.atTrace().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback trace."); + logger.atDebug().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback debug."); + logger.atInfo().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback info."); + logger.atWarn().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback warn."); + logger.atError().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback error."); + MDC.remove("MDC key"); + } +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingWithExceptionServlet.java b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingWithExceptionServlet.java new file mode 100644 index 00000000000..9660208c07a --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/main/java/com/microsoft/applicationinsights/smoketestapp/LogbackFluentLoggingWithExceptionServlet.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketestapp; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +@WebServlet("/testWithException") +public class LogbackFluentLoggingWithExceptionServlet extends HttpServlet { + + private static final Logger logger = LoggerFactory.getLogger("smoketestapp"); + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + Marker marker = MarkerFactory.getMarker("aMarker"); + MDC.put("MDC key", "MDC value"); + logger.atError().addKeyValue("customKey", "customValue").addMarker(marker).log("This is an exception!", new Exception("Fake Exception")); + MDC.remove("MDC key"); + } +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/main/resources/logback.xml b/smoke-tests/apps/LogbackFluentLogging/src/main/resources/logback.xml new file mode 100644 index 00000000000..3d580684d93 --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/LogbackFluentLoggingTest.java b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/LogbackFluentLoggingTest.java new file mode 100644 index 00000000000..b0e82e29b04 --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/LogbackFluentLoggingTest.java @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketest; + +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11_OPENJ9; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_17; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_17_OPENJ9; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_21; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_21_OPENJ9; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_25; +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_25_OPENJ9; + +import com.microsoft.applicationinsights.smoketest.schemav2.SeverityLevel; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +@UseAgent +abstract class LogbackFluentLoggingTest { + + @RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create(); + + @Test + @TargetUri("/test") + void test() { + testing.waitAndAssertTrace( + trace -> + trace + .hasRequestSatisying( + request -> + request.hasName("GET /LogbackFluentLogging/test").hasSuccess(true).hasNoSampleRate()) + .hasMessageCount(2) + .hasMessageSatisfying( + message -> + message + .hasMessage("This is logback warn.") + .hasSeverityLevel(SeverityLevel.WARNING) + .hasProperty("FileName", "LogbackFluentLoggingServlet.java") + .hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingServlet") + .hasProperty("MethodName", "doGet") + .hasProperty("LineNumber", "27") + .hasProperty("SourceType", "Logger") + .hasProperty("LoggerName", "smoketestapp") + .hasPropertyKey("ThreadName") + .hasProperty("MDC key", "MDC value") + .hasProperty("Marker", "aMarker") + .hasProperty("customKey", "customValue") + .hasNoSampleRate() + .hasPropertiesSize(10) + ) + .hasMessageSatisfying( + message -> + message + .hasMessage("This is logback error.") + .hasSeverityLevel(SeverityLevel.ERROR) + .hasProperty("FileName", "LogbackFluentLoggingServlet.java") + .hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingServlet") + .hasProperty("MethodName", "doGet") + .hasProperty("LineNumber", "28") + .hasProperty("SourceType", "Logger") + .hasProperty("LoggerName", "smoketestapp") + .hasPropertyKey("ThreadName") + .hasProperty("Marker", "aMarker") + .hasProperty("customKey", "customValue") + .hasPropertiesSize(10) + .hasNoSampleRate() + )); + } + + @Test + @TargetUri("/testWithException") + void testWithException() { + testing.waitAndAssertTrace( + trace -> + trace + .hasRequestSatisying( + request -> + request + .hasName("GET /LogbackFluentLogging/testWithException") + .hasSuccess(true) + .hasNoSampleRate()) + .hasExceptionCount(1) + .hasExceptionSatisfying( + exception -> + exception + .hasExceptionType("java.lang.Exception") + .hasExceptionMessage("Fake Exception") + .hasSeverityLevel(SeverityLevel.ERROR) + .hasProperty("FileName", "LogbackFluentLoggingWithExceptionServlet.java") + .hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingWithExceptionServlet") + .hasProperty("MethodName", "doGet") + .hasProperty("LineNumber", "24") + .hasPropertiesSize(11) + .hasProperty("Logger Message", "This is an exception!") + .hasProperty("SourceType", "Logger") + .hasProperty("LoggerName", "smoketestapp") + .hasPropertyKey("ThreadName") + .hasProperty("MDC key", "MDC value") + .hasProperty("Marker", "aMarker") + .hasProperty("customKey", "customValue") + .hasNoSampleRate())); + } + + + @Environment(TOMCAT_8_JAVA_11) + static class Tomcat8Java11Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_11_OPENJ9) + static class Tomcat8Java11OpenJ9Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_17) + static class Tomcat8Java17Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_17_OPENJ9) + static class Tomcat8Java17OpenJ9Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_21) + static class Tomcat8Java21Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_21_OPENJ9) + static class Tomcat8Java21OpenJ9Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_25) + static class Tomcat8Java23Test extends LogbackFluentLoggingTest {} + + @Environment(TOMCAT_8_JAVA_25_OPENJ9) + static class Tomcat8Java23OpenJ9Test extends LogbackFluentLoggingTest {} + +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/applicationinsights.json b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/applicationinsights.json new file mode 100644 index 00000000000..b8b0b5bcec3 --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/applicationinsights.json @@ -0,0 +1,19 @@ +{ + "role": { + "name": "testrolename", + "instance": "testroleinstance" + }, + "sampling": { + "percentage": 100 + }, + "instrumentation": { + "logging": { + "level": "warn" + } + }, + "preview": { + "captureLogbackCodeAttributes": true, + "captureLogbackKeyValues": true, + "captureLogbackMarker": true + } +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/disabled_applicationinsights.json b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/disabled_applicationinsights.json new file mode 100644 index 00000000000..135453a1a3a --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/disabled_applicationinsights.json @@ -0,0 +1,14 @@ +{ + "role": { + "name": "testrolename", + "instance": "testroleinstance" + }, + "sampling": { + "percentage": 100 + }, + "instrumentation": { + "logging": { + "enabled": "false" + } + } +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/level_off_applicationinsights.json b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/level_off_applicationinsights.json new file mode 100644 index 00000000000..c19c1fca9dd --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/level_off_applicationinsights.json @@ -0,0 +1,14 @@ +{ + "role": { + "name": "testrolename", + "instance": "testroleinstance" + }, + "sampling": { + "percentage": 100 + }, + "instrumentation": { + "logging": { + "level": "off" + } + } +} diff --git a/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/logback-test.xml b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/logback-test.xml new file mode 100644 index 00000000000..0cbbecd57ce --- /dev/null +++ b/smoke-tests/apps/LogbackFluentLogging/src/smokeTest/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + +