Skip to content

Capture Logback StructuredArguments (v()/keyValue()) as LogRecord Attributes #14230

Open
@defields923

Description

@defields923

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestneeds triageNew issue that requires triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions