Description
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