Skip to content

Commit a3b0525

Browse files
committed
feat(#4085): Support SLF4J's addKeyValue(key, value) method from fluent API in Logback instrumentation
1 parent 25f901b commit a3b0525

14 files changed

Lines changed: 311 additions & 0 deletions

File tree

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ public static class PreviewConfiguration {
342342

343343
public boolean captureLogbackCodeAttributes;
344344

345+
public boolean captureLogbackKeyValues;
346+
345347
public boolean captureLogbackMarker;
346348

347349
public boolean captureLog4jMarker;

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public Map<String, String> apply(ConfigProperties otelConfig) {
6666
// enable capturing all mdc properties
6767
properties.put(
6868
"otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", "*");
69+
properties.put(
70+
"otel.instrumentation.logback-appender.capture-key-value-pair-attributes", "*");
6971
properties.put("otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes", "*");
7072
properties.put(
7173
"otel.instrumentation.log4j-appender.experimental.capture-context-data-attributes", "*");
@@ -279,6 +281,11 @@ private static void enableInstrumentations(
279281
properties.put(
280282
"otel.instrumentation.logback-appender.experimental.capture-marker-attribute", "true");
281283
}
284+
if (config.preview.captureLogbackKeyValues) {
285+
properties.put(
286+
"otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes",
287+
"true");
288+
}
282289
if (config.preview.captureLog4jMarker) {
283290
properties.put(
284291
"otel.instrumentation.log4j-appender.experimental.capture-marker-attribute", "true");

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/SecondEntryPoint.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,10 @@ private static Set<Feature> initStatsbeatFeatureSet(Configuration config) {
488488
if (config.preview.captureLogbackMarker) {
489489
featureList.add(Feature.PREVIEW_CAPTURE_LOGBACK_MARKER);
490490
}
491+
if (config.preview.captureLogbackKeyValues) {
492+
// TODO: uncomment when the new feature is merged and released in azure-sdk-for-java
493+
// featureList.add(Feature.PREVIEW_CAPTURE_LOGBACK_KEY_VALUES);
494+
}
491495
if (config.preview.captureLog4jMarker) {
492496
featureList.add(Feature.PREVIEW_CAPTURE_LOG4J_MARKER);
493497
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ hideFromDependabot(":smoke-tests:apps:LiveMetrics")
103103
hideFromDependabot(":smoke-tests:apps:Log4j1")
104104
hideFromDependabot(":smoke-tests:apps:Log4j2")
105105
hideFromDependabot(":smoke-tests:apps:Logback")
106+
hideFromDependabot(":smoke-tests:apps:LogbackFluentLogging")
106107
hideFromDependabot(":smoke-tests:apps:Micrometer")
107108
hideFromDependabot(":smoke-tests:apps:MongoDB")
108109
hideFromDependabot(":smoke-tests:apps:NonDaemonThreads")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
plugins {
2+
id("ai.smoke-test-war")
3+
}
4+
5+
configurations.all {
6+
val slf4jVersion = "2.0.17"
7+
val logbackVersion = "1.5.20"
8+
resolutionStrategy.force("org.slf4j:slf4j-api:${slf4jVersion}")
9+
resolutionStrategy.force("org.slf4j:log4j-over-slf4j:${slf4jVersion}")
10+
resolutionStrategy.force("org.slf4j:jcl-over-slf4j:${slf4jVersion}")
11+
resolutionStrategy.force("org.slf4j:jul-to-slf4j:${slf4jVersion}")
12+
resolutionStrategy.force("ch.qos.logback:logback-classic:${logbackVersion}")
13+
}
14+
15+
dependencies {
16+
implementation("ch.qos.logback:logback-classic")
17+
}
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.applicationinsights.smoketestapp;
5+
6+
import javax.servlet.annotation.WebServlet;
7+
import javax.servlet.http.HttpServlet;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
11+
// this is used by the test infra in order to know when it's ok to start running the tests
12+
@WebServlet("")
13+
public class HealthCheckServlet extends HttpServlet {
14+
15+
@Override
16+
protected void doGet(HttpServletRequest request, HttpServletResponse response) {}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.applicationinsights.smoketestapp;
5+
6+
import javax.servlet.annotation.WebServlet;
7+
import javax.servlet.http.HttpServlet;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.slf4j.MDC;
13+
import org.slf4j.Marker;
14+
import org.slf4j.MarkerFactory;
15+
16+
@WebServlet("/test")
17+
public class LogbackFluentLoggingServlet extends HttpServlet {
18+
19+
private static final Logger logger = LoggerFactory.getLogger("smoketestapp");
20+
21+
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
22+
Marker marker = MarkerFactory.getMarker("aMarker");
23+
MDC.put("MDC key", "MDC value");
24+
logger.atTrace().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback trace.");
25+
logger.atDebug().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback debug.");
26+
logger.atInfo().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback info.");
27+
logger.atWarn().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback warn.");
28+
logger.atError().addKeyValue("customKey", "customValue").addMarker(marker).log("This is logback error.");
29+
MDC.remove("MDC key");
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.applicationinsights.smoketestapp;
5+
6+
import javax.servlet.annotation.WebServlet;
7+
import javax.servlet.http.HttpServlet;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.slf4j.MDC;
13+
import org.slf4j.Marker;
14+
import org.slf4j.MarkerFactory;
15+
16+
@WebServlet("/testWithException")
17+
public class LogbackFluentLoggingWithExceptionServlet extends HttpServlet {
18+
19+
private static final Logger logger = LoggerFactory.getLogger("smoketestapp");
20+
21+
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
22+
Marker marker = MarkerFactory.getMarker("aMarker");
23+
MDC.put("MDC key", "MDC value");
24+
logger.atError().addKeyValue("customKey", "customValue").addMarker(marker).log("This is an exception!", new Exception("Fake Exception"));
25+
MDC.remove("MDC key");
26+
}
27+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration debug="true">
3+
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
6+
</encoder>
7+
</appender>
8+
9+
<logger name="smoketestapp">
10+
<level value="trace"/>
11+
</logger>
12+
13+
<root>
14+
<appender-ref ref="CONSOLE"/>
15+
</root>
16+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.applicationinsights.smoketest;
5+
6+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11;
7+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_11_OPENJ9;
8+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_17;
9+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_17_OPENJ9;
10+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_21;
11+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_21_OPENJ9;
12+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_25;
13+
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_25_OPENJ9;
14+
15+
import com.microsoft.applicationinsights.smoketest.schemav2.SeverityLevel;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.RegisterExtension;
18+
19+
@UseAgent
20+
abstract class LogbackFluentLoggingTest {
21+
22+
@RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create();
23+
24+
@Test
25+
@TargetUri("/test")
26+
void test() {
27+
testing.waitAndAssertTrace(
28+
trace ->
29+
trace
30+
.hasRequestSatisying(
31+
request ->
32+
request.hasName("GET /LogbackFluentLogging/test").hasSuccess(true).hasNoSampleRate())
33+
.hasMessageCount(2)
34+
.hasMessageSatisfying(
35+
message ->
36+
message
37+
.hasMessage("This is logback warn.")
38+
.hasSeverityLevel(SeverityLevel.WARNING)
39+
.hasProperty("FileName", "LogbackFluentLoggingServlet.java")
40+
.hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingServlet")
41+
.hasProperty("MethodName", "doGet")
42+
.hasProperty("LineNumber", "27")
43+
.hasProperty("SourceType", "Logger")
44+
.hasProperty("LoggerName", "smoketestapp")
45+
.hasPropertyKey("ThreadName")
46+
.hasProperty("MDC key", "MDC value")
47+
.hasProperty("Marker", "aMarker")
48+
.hasProperty("customKey", "customValue")
49+
.hasNoSampleRate()
50+
.hasPropertiesSize(10)
51+
)
52+
.hasMessageSatisfying(
53+
message ->
54+
message
55+
.hasMessage("This is logback error.")
56+
.hasSeverityLevel(SeverityLevel.ERROR)
57+
.hasProperty("FileName", "LogbackFluentLoggingServlet.java")
58+
.hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingServlet")
59+
.hasProperty("MethodName", "doGet")
60+
.hasProperty("LineNumber", "28")
61+
.hasProperty("SourceType", "Logger")
62+
.hasProperty("LoggerName", "smoketestapp")
63+
.hasPropertyKey("ThreadName")
64+
.hasProperty("Marker", "aMarker")
65+
.hasProperty("customKey", "customValue")
66+
.hasPropertiesSize(10)
67+
.hasNoSampleRate()
68+
));
69+
}
70+
71+
@Test
72+
@TargetUri("/testWithException")
73+
void testWithException() {
74+
testing.waitAndAssertTrace(
75+
trace ->
76+
trace
77+
.hasRequestSatisying(
78+
request ->
79+
request
80+
.hasName("GET /LogbackFluentLogging/testWithException")
81+
.hasSuccess(true)
82+
.hasNoSampleRate())
83+
.hasExceptionCount(1)
84+
.hasExceptionSatisfying(
85+
exception ->
86+
exception
87+
.hasExceptionType("java.lang.Exception")
88+
.hasExceptionMessage("Fake Exception")
89+
.hasSeverityLevel(SeverityLevel.ERROR)
90+
.hasProperty("FileName", "LogbackFluentLoggingWithExceptionServlet.java")
91+
.hasProperty("ClassName", "com.microsoft.applicationinsights.smoketestapp.LogbackFluentLoggingWithExceptionServlet")
92+
.hasProperty("MethodName", "doGet")
93+
.hasProperty("LineNumber", "24")
94+
.hasPropertiesSize(11)
95+
.hasProperty("Logger Message", "This is an exception!")
96+
.hasProperty("SourceType", "Logger")
97+
.hasProperty("LoggerName", "smoketestapp")
98+
.hasPropertyKey("ThreadName")
99+
.hasProperty("MDC key", "MDC value")
100+
.hasProperty("Marker", "aMarker")
101+
.hasProperty("customKey", "customValue")
102+
.hasNoSampleRate()));
103+
}
104+
105+
106+
@Environment(TOMCAT_8_JAVA_11)
107+
static class Tomcat8Java11Test extends LogbackFluentLoggingTest {}
108+
109+
@Environment(TOMCAT_8_JAVA_11_OPENJ9)
110+
static class Tomcat8Java11OpenJ9Test extends LogbackFluentLoggingTest {}
111+
112+
@Environment(TOMCAT_8_JAVA_17)
113+
static class Tomcat8Java17Test extends LogbackFluentLoggingTest {}
114+
115+
@Environment(TOMCAT_8_JAVA_17_OPENJ9)
116+
static class Tomcat8Java17OpenJ9Test extends LogbackFluentLoggingTest {}
117+
118+
@Environment(TOMCAT_8_JAVA_21)
119+
static class Tomcat8Java21Test extends LogbackFluentLoggingTest {}
120+
121+
@Environment(TOMCAT_8_JAVA_21_OPENJ9)
122+
static class Tomcat8Java21OpenJ9Test extends LogbackFluentLoggingTest {}
123+
124+
@Environment(TOMCAT_8_JAVA_25)
125+
static class Tomcat8Java23Test extends LogbackFluentLoggingTest {}
126+
127+
@Environment(TOMCAT_8_JAVA_25_OPENJ9)
128+
static class Tomcat8Java23OpenJ9Test extends LogbackFluentLoggingTest {}
129+
130+
}

0 commit comments

Comments
 (0)