diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts b/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts index 0fd4232118d5..493bf9a34c12 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts +++ b/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts @@ -54,4 +54,5 @@ tasks.withType().configureEach { // TODO run tests both with and without experimental log attributes jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=*") jvmArgs("-Dotel.instrumentation.logback-appender.experimental-log-attributes=true") + jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-code-attributes=true") } diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java index 93c6aedf8c91..6789d3567ab0 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java @@ -21,12 +21,17 @@ public final class LogbackSingletons { boolean captureExperimentalAttributes = config.getBoolean( "otel.instrumentation.logback-appender.experimental-log-attributes", false); + boolean captureCodeAttributes = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-code-attributes", false); List captureMdcAttributes = config.getList( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", emptyList()); - mapper = new LoggingEventMapper(captureExperimentalAttributes, captureMdcAttributes); + mapper = + new LoggingEventMapper( + captureExperimentalAttributes, captureMdcAttributes, captureCodeAttributes); } public static LoggingEventMapper mapper() { diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy b/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy index fc62df876ab4..8e7af56cb922 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy @@ -44,6 +44,13 @@ class LogbackTest extends AgentInstrumentationSpecification { waitForTraces(1) } + String jvmVersion = System.getProperty("java.vm.specification.version") + int codeAttributes = 3 + boolean jvmVersionGreaterThanOrEqualTo18 = !jvmVersion.startsWith("1.8") && Integer.parseInt(jvmVersion) >= 18 + if (jvmVersionGreaterThanOrEqualTo18) { + codeAttributes = 4 // Java 18 specificity on line number (lineNumber > 0 check) + } + if (severity != null) { await() .untilAsserted( @@ -56,12 +63,12 @@ class LogbackTest extends AgentInstrumentationSpecification { assertThat(log.getSeverity()).isEqualTo(severity) assertThat(log.getSeverityText()).isEqualTo(severityText) if (exception) { - assertThat(log.getAttributes().size()).isEqualTo(5) + assertThat(log.getAttributes().size()).isEqualTo(5 + codeAttributes) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isEqualTo(IllegalStateException.getName()) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isEqualTo("hello") assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).contains(LogbackTest.name) } else { - assertThat(log.getAttributes().size()).isEqualTo(2) + assertThat(log.getAttributes().size()).isEqualTo(2 + codeAttributes) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).isNull() @@ -123,7 +130,7 @@ class LogbackTest extends AgentInstrumentationSpecification { assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) assertThat(log.getSeverityText()).isEqualTo("INFO") - assertThat(log.getAttributes().size()).isEqualTo(4) + assertThat(log.getAttributes().size()).isEqualTo(3 + 3) // 3 code attributes assertThat(log.getAttributes().get(AttributeKey.stringKey("logback.mdc.key1"))).isEqualTo("val1") assertThat(log.getAttributes().get(AttributeKey.stringKey("logback.mdc.key2"))).isEqualTo("val2") assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java index 24b7ebcb2f52..2f2c13760f6e 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java @@ -25,6 +25,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase captureMdcAttributes = emptyList(); private volatile LoggingEventMapper mapper; @@ -33,7 +34,9 @@ public OpenTelemetryAppender() {} @Override public void start() { - mapper = new LoggingEventMapper(captureExperimentalAttributes, captureMdcAttributes); + mapper = + new LoggingEventMapper( + captureExperimentalAttributes, captureMdcAttributes, captureCodeAttributes); super.start(); } @@ -62,6 +65,18 @@ public void setCaptureExperimentalAttributes(boolean captureExperimentalAttribut this.captureExperimentalAttributes = captureExperimentalAttributes; } + /** + * Sets whether the code attributes (file name, class name, method name and line number) should be + * set to logs. Enabling these attributes can potentially impact performance (see + * https://logback.qos.ch/manual/layouts.html). + * + * @param captureCodeAttributes To enable or disable the code attributes (file name, class name, + * method name and line number) + */ + public void setCaptureCodeAttributes(boolean captureCodeAttributes) { + this.captureCodeAttributes = captureCodeAttributes; + } + /** Configures the {@link MDC} attributes that will be copied to logs. */ public void setCaptureMdcAttributes(String attributes) { if (attributes != null) { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index e2469fe26340..576e32395237 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -34,10 +34,14 @@ public final class LoggingEventMapper { private final boolean captureExperimentalAttributes; private final List captureMdcAttributes; private final boolean captureAllMdcAttributes; + private final boolean captureCodeAttributes; public LoggingEventMapper( - boolean captureExperimentalAttributes, List captureMdcAttributes) { + boolean captureExperimentalAttributes, + List captureMdcAttributes, + boolean captureCodeAttributes) { this.captureExperimentalAttributes = captureExperimentalAttributes; + this.captureCodeAttributes = captureCodeAttributes; this.captureMdcAttributes = captureMdcAttributes; this.captureAllMdcAttributes = captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*"); @@ -104,6 +108,23 @@ private void mapLoggingEvent(LogRecordBuilder builder, ILoggingEvent loggingEven attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } + if (captureCodeAttributes) { + StackTraceElement[] callerData = loggingEvent.getCallerData(); + if (callerData != null && callerData.length > 0) { + StackTraceElement firstStackElement = callerData[0]; + String fileName = firstStackElement.getFileName(); + if (fileName != null) { + attributes.put(SemanticAttributes.CODE_FILEPATH, fileName); + } + attributes.put(SemanticAttributes.CODE_NAMESPACE, firstStackElement.getClassName()); + attributes.put(SemanticAttributes.CODE_FUNCTION, firstStackElement.getMethodName()); + int lineNumber = firstStackElement.getLineNumber(); + if (lineNumber > 0) { + attributes.put(SemanticAttributes.CODE_LINENO, lineNumber); + } + } + } + builder.setAllAttributes(attributes.build()); // span context diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java index c8be519d719a..06853ba75784 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Scope; @@ -71,7 +70,7 @@ void logNoSpan() { assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); - assertThat(logData.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(logData.getAttributes().size()).isEqualTo(4); // 4 code attributes } @Test @@ -115,13 +114,27 @@ void logWithExtras() { .isLessThan(TimeUnit.MILLISECONDS.toNanos(Instant.now().toEpochMilli())); assertThat(logData.getSeverity()).isEqualTo(Severity.INFO); assertThat(logData.getSeverityText()).isEqualTo("INFO"); - assertThat(logData.getAttributes().size()).isEqualTo(3); + assertThat(logData.getAttributes().size()).isEqualTo(3 + 4); // 4 code attributes assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)) .isEqualTo(IllegalStateException.class.getName()); assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)) .isEqualTo("Error!"); assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)) .contains("logWithExtras"); + + String file = logData.getAttributes().get(SemanticAttributes.CODE_FILEPATH); + assertThat(file).isEqualTo("OpenTelemetryAppenderConfigTest.java"); + + String codeClass = logData.getAttributes().get(SemanticAttributes.CODE_NAMESPACE); + assertThat(codeClass) + .isEqualTo( + "io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppenderConfigTest"); + + String method = logData.getAttributes().get(SemanticAttributes.CODE_FUNCTION); + assertThat(method).isEqualTo("logWithExtras"); + + Long lineNumber = logData.getAttributes().get(SemanticAttributes.CODE_LINENO); + assertThat(lineNumber).isGreaterThan(1); } @Test @@ -140,7 +153,7 @@ void logContextData() { assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); - assertThat(logData.getAttributes().size()).isEqualTo(2); + assertThat(logData.getAttributes().size()).isEqualTo(2 + 4); // 4 code attributes AssertionsForClassTypes.assertThat( logData.getAttributes().get(AttributeKey.stringKey("logback.mdc.key1"))) .isEqualTo("val1"); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java index 915f62f05bf7..f4b6ac530412 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java @@ -22,7 +22,7 @@ class LoggingEventMapperTest { @Test void testDefault() { // given - LoggingEventMapper mapper = new LoggingEventMapper(false, emptyList()); + LoggingEventMapper mapper = new LoggingEventMapper(false, emptyList(), false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -38,7 +38,7 @@ void testDefault() { @Test void testSome() { // given - LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("key2")); + LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("key2"), false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -55,7 +55,7 @@ void testSome() { @Test void testAll() { // given - LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("*")); + LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("*"), false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml index ddefe47c3a4d..d213483122ee 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml @@ -11,6 +11,7 @@ false + true *