Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to export Logback and Log4j 2 markers #2529

Merged
merged 10 commits into from
Oct 20, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ public static class PreviewConfiguration {

public boolean captureLogbackCodeAttributes;

public boolean captureLogbackMarker;

public boolean captureLog4jMarker;

// this is to support interoperability with other systems
// intentionally not allowing the removal of w3c propagator since that is key to many Azure
// integrated experiences
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ private static void enableInstrumentations(Configuration config, Map<String, Str
properties.put(
"otel.instrumentation.logback-appender.experimental.capture-code-attributes", "true");
}
if (config.preview.captureLogbackMarker) {
properties.put(
"otel.instrumentation.logback-appender.experimental.capture-marker-attribute", "true");
}
if (config.preview.captureLog4jMarker) {
properties.put(
"otel.instrumentation.log4j-appender.experimental.capture-marker-attribute", "true");
}
if (config.preview.instrumentation.akka.enabled) {
properties.put("otel.instrumentation.akka-actor.enabled", "true");
properties.put("otel.instrumentation.akka-http.enabled", "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.azure.monitor.opentelemetry.exporter.implementation.models.SeverityLevel;
import com.azure.monitor.opentelemetry.exporter.implementation.models.TelemetryItem;
import com.azure.monitor.opentelemetry.exporter.implementation.utils.FormattedTime;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.SpanContext;
Expand All @@ -27,6 +28,12 @@ public class LogDataMapper {
private final boolean captureLoggingLevelAsCustomDimension;
private final BiConsumer<AbstractTelemetryBuilder, Resource> telemetryInitializer;

private static final AttributeKey<String> OTEL_LOG4J_MARKER =
AttributeKey.stringKey("log4j.marker");

private static final AttributeKey<String> OTEL_LOGBACK_MARKER =
AttributeKey.stringKey("logback.marker");

public LogDataMapper(
boolean captureLoggingLevelAsCustomDimension,
BiConsumer<AbstractTelemetryBuilder, Resource> telemetryInitializer) {
Expand Down Expand Up @@ -171,6 +178,10 @@ private static void setExtraAttributes(
telemetryBuilder.addProperty("LineNumber", String.valueOf(value));
return;
}
if (OTEL_LOG4J_MARKER.getKey().equals(key) || OTEL_LOGBACK_MARKER.getKey().equals(key)) {
telemetryBuilder.addProperty("Marker", String.valueOf(value));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Marker be a good property name?

return;
}
if (key.startsWith(JBOSS_LOGGING_MDC_PREFIX)) {
telemetryBuilder.addProperty(
key.substring(JBOSS_LOGGING_MDC_PREFIX.length()), String.valueOf(value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;

@WebServlet("/traceLog4j2")
Expand All @@ -22,6 +23,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) {
logger.info("This is log4j2 info.");
ThreadContext.put("MDC key", "MDC value");
logger.warn("This is log4j2 warn.");
logger.warn(MarkerManager.getMarker("aMarker"), "Warn with marker");
ThreadContext.remove("MDC key");
logger.error("This is log4j2 error.");
logger.fatal("This is log4j2 fatal.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
abstract class TraceLog4j2Test {

@RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create();
private static final String TRACE_OPERATION_NAME = "GET /TraceLog4j2UsingAgent/traceLog4j2";

@Test
@TargetUri("/traceLog4j2")
Expand All @@ -38,16 +39,18 @@ void testTraceLog4j2() throws Exception {

Envelope rdEnvelope = rdList.get(0);
String operationId = rdEnvelope.getTags().get("ai.operation.id");
List<Envelope> mdList = testing.mockedIngestion.waitForMessageItemsInRequest(3, operationId);
List<Envelope> mdList = testing.mockedIngestion.waitForMessageItemsInRequest(4, operationId);

Envelope mdEnvelope1 = mdList.get(0);
Envelope mdEnvelope2 = mdList.get(1);
Envelope mdEnvelope3 = mdList.get(2);
Envelope mdEnvelope4 = mdList.get(3);

assertThat(rdEnvelope.getSampleRate()).isNull();
assertThat(mdEnvelope1.getSampleRate()).isNull();
assertThat(mdEnvelope2.getSampleRate()).isNull();
assertThat(mdEnvelope3.getSampleRate()).isNull();
assertThat(mdEnvelope4.getSampleRate()).isNull();

RequestData rd = (RequestData) ((Data<?>) rdEnvelope.getData()).getBaseData();

Expand All @@ -57,6 +60,7 @@ void testTraceLog4j2() throws Exception {
MessageData md1 = logs.get(0);
MessageData md2 = logs.get(1);
MessageData md3 = logs.get(2);
MessageData md4 = logs.get(3);

assertThat(md1.getMessage()).isEqualTo("This is log4j2 warn.");
assertThat(md1.getSeverityLevel()).isEqualTo(SeverityLevel.WARNING);
Expand All @@ -66,26 +70,26 @@ void testTraceLog4j2() throws Exception {
assertThat(md1.getProperties()).containsEntry("MDC key", "MDC value");
assertThat(md1.getProperties()).hasSize(4);

assertThat(md2.getMessage()).isEqualTo("This is log4j2 error.");
assertThat(md2.getSeverityLevel()).isEqualTo(SeverityLevel.ERROR);
assertThat(md2.getProperties()).containsEntry("SourceType", "Logger");
assertThat(md1.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md1.getProperties()).containsKey("ThreadName");
assertThat(md2.getProperties()).hasSize(3);
assertThat(md2.getProperties()).containsEntry("Marker", "aMarker");

assertThat(md3.getMessage()).isEqualTo("This is log4j2 fatal.");
assertThat(md3.getSeverityLevel()).isEqualTo(SeverityLevel.CRITICAL);
assertThat(md3.getMessage()).isEqualTo("This is log4j2 error.");
assertThat(md3.getSeverityLevel()).isEqualTo(SeverityLevel.ERROR);
assertThat(md3.getProperties()).containsEntry("SourceType", "Logger");
assertThat(md3.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md3.getProperties()).containsKey("ThreadName");
assertThat(md1.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md1.getProperties()).containsKey("ThreadName");
assertThat(md3.getProperties()).hasSize(3);

SmokeTestExtension.assertParentChild(
rd, rdEnvelope, mdEnvelope1, "GET /TraceLog4j2UsingAgent/traceLog4j2");
SmokeTestExtension.assertParentChild(
rd, rdEnvelope, mdEnvelope2, "GET /TraceLog4j2UsingAgent/traceLog4j2");
SmokeTestExtension.assertParentChild(
rd, rdEnvelope, mdEnvelope3, "GET /TraceLog4j2UsingAgent/traceLog4j2");
assertThat(md4.getMessage()).isEqualTo("This is log4j2 fatal.");
assertThat(md4.getSeverityLevel()).isEqualTo(SeverityLevel.CRITICAL);
assertThat(md4.getProperties()).containsEntry("SourceType", "Logger");
assertThat(md4.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md4.getProperties()).containsKey("ThreadName");
assertThat(md4.getProperties()).hasSize(3);

SmokeTestExtension.assertParentChild(rd, rdEnvelope, mdEnvelope1, TRACE_OPERATION_NAME);
SmokeTestExtension.assertParentChild(rd, rdEnvelope, mdEnvelope2, TRACE_OPERATION_NAME);
SmokeTestExtension.assertParentChild(rd, rdEnvelope, mdEnvelope3, TRACE_OPERATION_NAME);
SmokeTestExtension.assertParentChild(rd, rdEnvelope, mdEnvelope4, TRACE_OPERATION_NAME);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"logging": {
"level": "warn"
}
},
"preview": {
"captureLog4jMarker": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

@WebServlet("/traceLogBack")
public class SimpleTestTraceLogBackServlet extends HttpServlet {
Expand All @@ -24,5 +26,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) {
logger.warn("This is logback warn.");
MDC.remove("MDC key");
logger.error("This is logback error.");

Marker marker = MarkerFactory.getMarker("aMarker");
logger.error(marker, "Log with marker");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ abstract class TraceLogBackTest {

@RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create();

boolean checkLogBackCodeAttributes() {
return true;
// Not really sure that Logback is enabled with Wildfly
// https://anotheria.net/blog/devops/enable-logback-in-jboss/
// https://www.oreilly.com/library/view/wildfly-cookbook/9781784392413/ch04s08.html
boolean isWildflyServer() {
return false;
}

@Test
Expand All @@ -41,7 +44,7 @@ void testTraceLogBack() throws Exception {

Envelope rdEnvelope = rdList.get(0);
String operationId = rdEnvelope.getTags().get("ai.operation.id");
List<Envelope> mdList = testing.mockedIngestion.waitForMessageItemsInRequest(2, operationId);
List<Envelope> mdList = testing.mockedIngestion.waitForMessageItemsInRequest(3, operationId);

Envelope mdEnvelope1 = mdList.get(0);
Envelope mdEnvelope2 = mdList.get(1);
Expand All @@ -57,50 +60,53 @@ void testTraceLogBack() throws Exception {

MessageData md1 = logs.get(0);
MessageData md2 = logs.get(1);
MessageData md3 = logs.get(2);

assertThat(md1.getMessage()).isEqualTo("This is logback warn.");
assertThat(md1.getSeverityLevel()).isEqualTo(SeverityLevel.WARNING);
assertThat(md1.getProperties()).containsEntry("SourceType", "Logger");
assertThat(md1.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md1.getProperties()).containsKey("ThreadName");
assertThat(md1.getProperties()).containsEntry("MDC key", "MDC value");
assertThat(md1.getProperties())
.containsEntry("SourceType", "Logger")
.containsEntry("LoggerName", "smoketestapp")
.containsKey("ThreadName")
.containsEntry("MDC key", "MDC value");

if (checkLogBackCodeAttributes()) {
assertThat(md1.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackServlet.java");
if (!isWildflyServer()) {
assertThat(md1.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackServlet.java")
.containsEntry(
"ClassName",
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackServlet");
assertThat(md1.getProperties()).containsEntry("MethodName", "doGet");
assertThat(md1.getProperties()).containsEntry("LineNumber", "24");

assertThat(md1.getProperties()).hasSize(8);
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackServlet")
.containsEntry("MethodName", "doGet")
.containsEntry("LineNumber", "26")
.hasSize(8);
} else {
assertThat(md1.getProperties()).hasSize(4);
}

assertThat(md2.getMessage()).isEqualTo("This is logback error.");
assertThat(md2.getSeverityLevel()).isEqualTo(SeverityLevel.ERROR);
assertThat(md2.getProperties()).containsEntry("SourceType", "Logger");
assertThat(md2.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(md2.getProperties()).containsKey("ThreadName");
assertThat(md2.getProperties())
.containsEntry("SourceType", "Logger")
.containsEntry("LoggerName", "smoketestapp")
.containsKey("ThreadName");

if (checkLogBackCodeAttributes()) {
assertThat(md2.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackServlet.java");
if (!isWildflyServer()) {
assertThat(md2.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackServlet.java")
.containsEntry(
"ClassName",
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackServlet");
assertThat(md2.getProperties()).containsEntry("MethodName", "doGet");
assertThat(md2.getProperties()).containsEntry("LineNumber", "26");

assertThat(md2.getProperties()).hasSize(7);
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackServlet")
.containsEntry("MethodName", "doGet")
.containsEntry("LineNumber", "28")
.hasSize(7);
} else {
assertThat(md2.getProperties()).hasSize(3);
}

if (!isWildflyServer()) {
assertThat(md3.getProperties()).containsEntry("Marker", "aMarker");
}

SmokeTestExtension.assertParentChild(
rd, rdEnvelope, mdEnvelope1, "GET /TraceLogBackUsingAgent/traceLogBack");
SmokeTestExtension.assertParentChild(
Expand Down Expand Up @@ -128,22 +134,22 @@ void testTraceLogBackWithException() throws Exception {

assertThat(ed.getExceptions().get(0).getMessage()).isEqualTo("Fake Exception");
assertThat(ed.getSeverityLevel()).isEqualTo(SeverityLevel.ERROR);
assertThat(ed.getProperties()).containsEntry("Logger Message", "This is an exception!");
assertThat(ed.getProperties()).containsEntry("SourceType", "Logger");
assertThat(ed.getProperties()).containsEntry("LoggerName", "smoketestapp");
assertThat(ed.getProperties()).containsKey("ThreadName");
assertThat(ed.getProperties()).containsEntry("MDC key", "MDC value");

if (checkLogBackCodeAttributes()) {
assertThat(ed.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackWithExceptionServlet.java");
assertThat(ed.getProperties())
.containsEntry("Logger Message", "This is an exception!")
.containsEntry("SourceType", "Logger")
.containsEntry("LoggerName", "smoketestapp")
.containsKey("ThreadName")
.containsEntry("MDC key", "MDC value");

if (!isWildflyServer()) {
assertThat(ed.getProperties())
.containsEntry("FileName", "SimpleTestTraceLogBackWithExceptionServlet.java")
.containsEntry(
"ClassName",
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackWithExceptionServlet");
assertThat(ed.getProperties()).containsEntry("MethodName", "doGet");
assertThat(ed.getProperties()).containsEntry("LineNumber", "21");
assertThat(ed.getProperties()).hasSize(9);
"com.microsoft.applicationinsights.smoketestapp.SimpleTestTraceLogBackWithExceptionServlet")
.containsEntry("MethodName", "doGet")
.containsEntry("LineNumber", "21")
.hasSize(9);
} else {
assertThat(ed.getProperties()).hasSize(5);
}
Expand Down Expand Up @@ -176,16 +182,16 @@ static class Tomcat8Java19Test extends TraceLogBackTest {}
@Environment(WILDFLY_13_JAVA_8)
static class Wildfly13Java8Test extends TraceLogBackTest {
@Override
boolean checkLogBackCodeAttributes() {
return false;
boolean isWildflyServer() {
return true;
}
}

@Environment(WILDFLY_13_JAVA_8_OPENJ9)
static class Wildfly13Java8OpenJ9Test extends TraceLogBackTest {
@Override
boolean checkLogBackCodeAttributes() {
return false;
boolean isWildflyServer() {
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
}
},
"preview": {
"captureLogbackCodeAttributes": true
"captureLogbackCodeAttributes": true,
"captureLogbackMarker": true
}
}