Skip to content

Add code attributes for log4j1 #13947

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

Draft
wants to merge 24 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
442a9e0
Create sync.yml
oliver-zhang Sep 23, 2024
9dc2d2f
Delete .github/workflows/sync.yml
oliver-zhang Sep 23, 2024
1f7de02
Create sync.yml
oliver-zhang Sep 24, 2024
8c704f8
Update sync.yml
oliver-zhang Sep 24, 2024
356f36b
Update sync.yml
oliver-zhang Sep 24, 2024
2994158
Merge branch 'open-telemetry:main' into main
oliver-zhang May 29, 2025
e56fd19
capture code attributes
oliver-zhang May 29, 2025
41f3d84
delete sync.yml
oliver-zhang May 29, 2025
1b9194f
add unit test
oliver-zhang May 30, 2025
857cc02
add unit test
oliver-zhang May 30, 2025
ac48be6
Merge branch 'open-telemetry:main' into main
oliver-zhang Jun 26, 2025
fdc97a7
capture code attributes
oliver-zhang May 29, 2025
51494f9
delete sync.yml
oliver-zhang May 29, 2025
ba2fd50
add unit test
oliver-zhang May 30, 2025
e865995
add unit test
oliver-zhang May 30, 2025
7560a51
semconv stable code
oliver-zhang Jun 26, 2025
8cc0987
Merge remote-tracking branch 'origin/log4j-appender' into log4j-appender
oliver-zhang Jun 26, 2025
fdb78cb
Merge branch 'main' into log4j-appender
oliver-zhang Jun 26, 2025
e6aa3ef
code review
oliver-zhang Jun 27, 2025
21f7825
Merge remote-tracking branch 'origin/log4j-appender' into log4j-appender
oliver-zhang Jun 27, 2025
ab2efb8
Merge branch 'main' into log4j-appender
SylvainJuge Jul 11, 2025
d39a7eb
Merge branch 'main' into log4j-appender
trask Jul 17, 2025
73d71a9
Merge branch 'main' into log4j-appender
oliver-zhang Jul 21, 2025
7145461
Merge branch 'main' into log4j-appender
oliver-zhang Jul 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ configurations {
tasks.withType<Test>().configureEach {
// TODO run tests both with and without experimental log attributes
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-mdc-attributes=*")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-code-attributes=true")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static class ForcedLogAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This Category logger,
@Advice.Argument(0) String fqcn,
@Advice.Argument(1) Priority level,
@Advice.Argument(2) Object message,
@Advice.Argument(3) Throwable t,
Expand All @@ -56,7 +57,7 @@ public static void methodEnter(
// framework delegates to another
callDepth = CallDepth.forClass(LoggerProvider.class);
if (callDepth.getAndIncrement() == 0) {
LogEventMapper.INSTANCE.capture(logger, level, message, t);
LogEventMapper.INSTANCE.capture(fqcn, logger, level, message, t);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.api.internal.cache.Cache;
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
import io.opentelemetry.semconv.CodeAttributes;
import io.opentelemetry.semconv.ExceptionAttributes;
import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes;
import java.io.PrintWriter;
Expand All @@ -28,13 +30,19 @@
import org.apache.log4j.Category;
import org.apache.log4j.MDC;
import org.apache.log4j.Priority;
import org.apache.log4j.spi.LocationInfo;

public final class LogEventMapper {

private static final Cache<String, AttributeKey<String>> mdcAttributeKeys = Cache.bounded(100);

public static final LogEventMapper INSTANCE = new LogEventMapper();

private static final AttributeKey<String> CODE_FILEPATH = AttributeKey.stringKey("code.filepath");
private static final AttributeKey<String> CODE_FUNCTION = AttributeKey.stringKey("code.function");
private static final AttributeKey<Long> CODE_LINENO = AttributeKey.longKey("code.lineno");
private static final AttributeKey<String> CODE_NAMESPACE =
AttributeKey.stringKey("code.namespace");
// copied from org.apache.log4j.Level because it was only introduced in 1.2.12
private static final int TRACE_INT = 5000;

Expand All @@ -60,7 +68,13 @@ private LogEventMapper() {
captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*");
}

public void capture(Category logger, Priority level, Object message, Throwable throwable) {
boolean captureCodeAttributes =
AgentInstrumentationConfig.get()
.getBoolean(
"otel.instrumentation.log4j-appender.experimental.capture-code-attributes", false);

public void capture(
String fqcn, Category logger, Priority level, Object message, Throwable throwable) {
String instrumentationName = logger.getName();
if (instrumentationName == null || instrumentationName.isEmpty()) {
instrumentationName = "ROOT";
Expand Down Expand Up @@ -104,6 +118,47 @@ public void capture(Category logger, Priority level, Object message, Throwable t
attributes.put(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId());
}

if (captureCodeAttributes) {
LocationInfo locationInfo = new LocationInfo(new Throwable(), fqcn);
String fileName = locationInfo.getFileName();
if (fileName != null) {
if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(CodeAttributes.CODE_FILE_PATH, fileName);
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_FILEPATH, fileName);
}
}

if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(
CodeAttributes.CODE_FUNCTION_NAME,
locationInfo.getClassName() + "." + locationInfo.getMethodName());
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_NAMESPACE, locationInfo.getClassName());
attributes.put(CODE_FUNCTION, locationInfo.getMethodName());
}

String lineNumber = locationInfo.getLineNumber();
int codeLineNo = 0;
if (!lineNumber.equals("?")) {
try {
codeLineNo = Integer.parseInt(lineNumber);
} catch (NumberFormatException e) {
// ignore
}
}
if (codeLineNo > 0) {
if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(CodeAttributes.CODE_LINE_NUMBER, codeLineNo);
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_LINENO, codeLineNo);
}
}
}

builder.setAllAttributes(attributes.build());

// span context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FILEPATH;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_LINENO;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID;
import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import io.opentelemetry.api.common.AttributeKey;
Expand All @@ -31,13 +37,15 @@
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.helpers.Loader;
import org.assertj.core.api.AbstractLongAssert;
import org.assertj.core.api.AssertAccess;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

@SuppressWarnings("deprecation") // using deprecated semconv
Copy link
Contributor

Choose a reason for hiding this comment

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

[minor] probably not needed anymore

class Log4j1Test {

static {
Expand Down Expand Up @@ -67,6 +75,25 @@ private static Stream<Arguments> provideParameters() {
Arguments.of(true, true));
}

@Test
public void testCodeAttributes() {
logger.info("this is test message");
testing.waitAndAssertLogRecords(
logRecord ->
logRecord
.hasBody("this is test message")
.hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build())
.hasSeverity(Severity.INFO)
.hasSeverityText("INFO")
.hasAttributesSatisfyingExactly(
equalTo(THREAD_NAME, Thread.currentThread().getName()),
equalTo(THREAD_ID, Thread.currentThread().getId()),
equalTo(CODE_NAMESPACE, Log4j1Test.class.getName()),
equalTo(CODE_FUNCTION, "testCodeAttributes"),
satisfies(CODE_LINENO, AbstractLongAssert::isPositive),
equalTo(CODE_FILEPATH, "Log4j1Test.java")));
}

@ParameterizedTest
@MethodSource("provideParameters")
public void test(boolean logException, boolean withParent) throws InterruptedException {
Expand Down
Loading