Description
Is your feature request related to a problem? Please describe.
Currently, the OpenTelemetry Java Agent's Logback instrumentation effectively captures structured key-value pairs when using Logback's fluent logging API (e.g., log.atInfo().addKeyValue("key", value).log()). This functionality is enabled via otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true.
However, the agent does not similarly extract key-value pairs provided via net.logstash.logback.argument.StructuredArguments (e.g., v(), keyValue()) when they are passed as message arguments to standard SLF4J logging methods (e.g., log.warn("Message with argument: {}", v("key", value))).
This discrepancy requires users to refactor existing code that uses StructuredArguments for contextual data in order to get it as a distinct OpenTelemetry LogRecord attribute.
Current Behavior
Consider the following Java code using Logback and StructuredArguments:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import static net.logstash.logback.argument.StructuredArguments.v; // or keyValue
@Slf4j
@Service
public class MyService {
public void processData(String customerId) {
// Method 1: Using fluent API (desired behavior)
log.atWarn()
.setMessage("[atWarn] Retrieving user profile by customerId")
.addKeyValue("customer_id", customerId)
.log();
// Method 2: Using StructuredArguments as a message parameter (current issue)
log.warn("[warn] This is a warning log for customerId: {}", v("customer_id", customerId));
}
}
When the OpenTelemetry Java Agent (with otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true enabled) processes these logs and forwards them to an OTel Collector, the following is observed:
LogRecord #0 (from log.atWarn().addKeyValue()):
ObservedTimestamp: 2025-07-11 20:22:04.422883609 +0000 UTC
Timestamp: 2025-07-11 20:22:04.421306496 +0000 UTC
SeverityText: WARN
SeverityNumber: Warn(13)
Body: Str([atWarn] Retrieving user profile by customerId)
Attributes:
-> customer_id: Str(123) <-- ATTRIBUTE PRESENT!
Trace ID: 76718c7b0b7dab7ec6b18bb4767a9970
Span ID: 67b9155609a6eac3
Flags: 1
LogRecord #1 (from log.warn("...", v("customer_id", customerId))):
ObservedTimestamp: 2025-07-11 20:22:04.436046959 +0000 UTC
Timestamp: 2025-07-11 20:22:04.435933794 +0000 UTC
SeverityText: WARN
SeverityNumber: Warn(13)
Body: Str([warn] This is a warning log for customerId: 123) <-- ATTRIBUTE MISSING!
Trace ID: 76718c7b0b7dab7ec6b18bb4767a9970
Span ID: 67b9155609a6eac3
As shown, customer_id is not extracted as a distinct LogRecord attribute when StructuredArguments.v() is used as a message parameter.
Describe the solution you'd like
When otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true is enabled, any StructuredArgument (such as those created by net.logstash.logback.argument.StructuredArguments.v() or keyValue()) passed as a message parameter to SLF4J/Logback logging methods should be recognized and their key-value pairs should be extracted and added as attributes to the OpenTelemetry LogRecord.
For LogRecord #1 in the example above, the desired output would be:
// ... (same as before)
Body: Str([warn] This is a warning log for customerId: 01c08106-2269-41f6-be5c-367c991c92cc)
Attributes:
-> customer_id: Str(123) <-- ATTRIBUTE SHOULD BE PRESENT HERE!
// ... (same as before)
Describe alternatives you've considered
No response
Additional context
Environment
- OpenTelemetry Java Agent Version: [2.17.1]
- logstash-logback-encoder Version: [8.1]
- Java Version: [Java 22]
- JVM Arguments:
otel.instrumentation.logback-appender.experimental.capture-mdc-attributes=customer_id
otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true
otel.instrumentation.logback-appender.experimental.capture-marker-attribute=true