Skip to content

Add extension support for OpenTelemetryAppender so we can add custom formatting and logging. #13969

Open
@jsing120

Description

@jsing120

Is your feature request related to a problem? Please describe.

So the package io.opentelemetry.instrumentation.log4j.appender.v2_17.OpenTelemetryAppender uses io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.LogEventMapper to map a logevent, however multiple initiatives would need capabilities to mask PII data or some custom formatting of data as part of the logevent. A feature to add a custom LogEvent or event a custom Logevent mapper would be nice to have.

Describe the solution you'd like

Make OpenTelemetryAppender extendible

Describe alternatives you've considered

If OpenTelemetryAppender had non private method and with a constructor we could have easily created a wrapper `
public class MaskingOpenTelemetryAppender extends OpenTelemetryAppender {

public MaskingOpenTelemetryAppender(/* all required args */) {
    super(/* pass all args to super */);
}

@Override
protected void emit(OpenTelemetry openTelemetry, LogEvent event) {
    // Mask the message before passing to the mapper
    String originalMessage = event.getMessage().getFormattedMessage();

//Some Custom masking logic
String maskedMessage = MaskLogMessageConverter.maskPII(originalMessage);

    // Create a new LogEvent with the masked message, or wrap the message
    // (You may need to create a new Message implementation with the masked string)
    LogEvent maskedEvent = new MaskedLogEvent(event, maskedMessage);

    // Call super.emit with the masked event
    super.emit(openTelemetry, maskedEvent);
}

}
package com.test.interceptor;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.message.Message;

public class MaskedLogEvent implements LogEvent {

private final LogEvent originalEvent;
private final String maskedMessage;

public MaskedLogEvent(LogEvent originalEvent, String maskedMessage) {
    this.originalEvent = originalEvent;
    this.maskedMessage = maskedMessage;
}

@Override
public Message getMessage() {
    return new Message() {
        @Override
        public String getFormattedMessage() {
            return maskedMessage;
        }

        @Override
        public String getFormat() {
            return originalEvent.getMessage().getFormat();
        }

        @Override
        public Object[] getParameters() {
            return originalEvent.getMessage().getParameters();
        }

        @Override
        public Throwable getThrowable() {
            return originalEvent.getMessage().getThrowable();
        }
    };
}

// Delegate all other methods to the original event
@Override
public LogEvent toImmutable() {
    return originalEvent.toImmutable();
}

@Override
public boolean isIncludeLocation() {
    return originalEvent.isIncludeLocation();
}

@Override
public void setIncludeLocation(boolean locationRequired) {
    originalEvent.setIncludeLocation(locationRequired);
}

@Override
public long getTimeMillis() {
    return originalEvent.getTimeMillis();
}

@Override
public void setTimeMillis(long timeMillis) {
    originalEvent.setTimeMillis(timeMillis);
}

@Override
public String getLoggerName() {
    return originalEvent.getLoggerName();
}

@Override
public org.apache.logging.log4j.Level getLevel() {
    return originalEvent.getLevel();
}

@Override
public Throwable getThrown() {
    return originalEvent.getThrown();
}

@Override
public Marker getMarker() {
    return originalEvent.getMarker();
}

@Override
public String getThreadName() {
    return originalEvent.getThreadName();
}

@Override
public long getThreadId() {
    return originalEvent.getThreadId();
}

@Override
public int getThreadPriority() {
    return originalEvent.getThreadPriority();
}

@Override
public StackTraceElement getSource() {
    return originalEvent.getSource();
}

@Override
public ReadOnlyStringMap getContextData() {
    return originalEvent.getContextData();
}

@Override
public Map<String, String> getContextMap() {
    return originalEvent.getContextMap();
}

@Override
public String getLoggerFqcn() {
    return originalEvent.getLoggerFqcn();
}

@Override
public boolean isEndOfBatch() {
    return originalEvent.isEndOfBatch();
}

@Override
public void setEndOfBatch(boolean endOfBatch) {
    originalEvent.setEndOfBatch(endOfBatch);
}

@Override
public long getNanoTime() {
    return originalEvent.getNanoTime();
}

}`

Additional context

No response

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