Skip to content

Commit

Permalink
Added setOpenTelemetry method to appender (open-telemetry#8231)
Browse files Browse the repository at this point in the history
Co-authored-by: Trask Stalnaker <[email protected]>
  • Loading branch information
rupinder10 and trask committed Jun 14, 2023
1 parent 80d50cc commit 06f22a3
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor;
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.LogEventMapper;
Expand Down Expand Up @@ -43,6 +44,7 @@ public class OpenTelemetryAppender extends AbstractAppender {
static final String PLUGIN_NAME = "OpenTelemetry";

private final LogEventMapper<ReadOnlyStringMap> mapper;
@Nullable private OpenTelemetry openTelemetry;

@PluginBuilderFactory
public static <B extends Builder<B>> B builder() {
Expand All @@ -57,6 +59,8 @@ public static class Builder<B extends Builder<B>> extends AbstractAppender.Build
@PluginBuilderAttribute private boolean captureMarkerAttribute;
@PluginBuilderAttribute private String captureContextDataAttributes;

@Nullable private OpenTelemetry openTelemetry;

/**
* Sets whether experimental attributes should be set to logs. These attributes may be changed
* or removed in the future, so only enable this if you know you do not require attributes
Expand Down Expand Up @@ -93,6 +97,12 @@ public B setCaptureContextDataAttributes(String captureContextDataAttributes) {
return asBuilder();
}

@CanIgnoreReturnValue
public B setOpenTelemetry(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
return asBuilder();
}

@Override
public OpenTelemetryAppender build() {
return new OpenTelemetryAppender(
Expand All @@ -104,7 +114,8 @@ public OpenTelemetryAppender build() {
captureExperimentalAttributes,
captureMapMessageAttributes,
captureMarkerAttribute,
captureContextDataAttributes);
captureContextDataAttributes,
openTelemetry);
}
}

Expand All @@ -117,7 +128,8 @@ private OpenTelemetryAppender(
boolean captureExperimentalAttributes,
boolean captureMapMessageAttributes,
boolean captureMarkerAttribute,
String captureContextDataAttributes) {
String captureContextDataAttributes,
OpenTelemetry openTelemetry) {

super(name, filter, layout, ignoreExceptions, properties);
this.mapper =
Expand All @@ -127,6 +139,7 @@ private OpenTelemetryAppender(
captureMapMessageAttributes,
captureMarkerAttribute,
splitAndFilterBlanksAndNulls(captureContextDataAttributes));
this.openTelemetry = openTelemetry;
}

private static List<String> splitAndFilterBlanksAndNulls(String value) {
Expand All @@ -139,14 +152,23 @@ private static List<String> splitAndFilterBlanksAndNulls(String value) {
.collect(Collectors.toList());
}

public void setOpenTelemetry(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}

private OpenTelemetry getOpenTelemetry() {
return openTelemetry == null ? GlobalOpenTelemetry.get() : openTelemetry;
}

@Override
public void append(LogEvent event) {
String instrumentationName = event.getLoggerName();
if (instrumentationName == null || instrumentationName.isEmpty()) {
instrumentationName = "ROOT";
}

LogRecordBuilder builder =
GlobalOpenTelemetry.get()
getOpenTelemetry()
.getLogsBridge()
.loggerBuilder(instrumentationName)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,23 @@

package io.opentelemetry.instrumentation.log4j.appender.v2_17;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.FormattedMessage;
import org.apache.logging.log4j.message.StringMapMessage;
import org.apache.logging.log4j.message.StructuredDataMessage;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class OpenTelemetryAppenderConfigTest {

private static final Logger logger = LogManager.getLogger("TestLogger");

private static InMemoryLogRecordExporter logRecordExporter;
private static Resource resource;
private static InstrumentationScopeInfo instrumentationScopeInfo;
class OpenTelemetryAppenderConfigTest extends OpenTelemetryAppenderConfigTestBase {

@BeforeAll
static void setupAll() {
Expand All @@ -65,12 +39,6 @@ static void setupAll() {
GlobalOpenTelemetry.set(OpenTelemetrySdk.builder().setLoggerProvider(loggerProvider).build());
}

@BeforeEach
void setup() {
logRecordExporter.reset();
ThreadContext.clearAll();
}

@Test
void initializeWithBuilder() {
OpenTelemetryAppender appender =
Expand All @@ -86,149 +54,4 @@ void initializeWithBuilder() {
assertThat(logDataList)
.satisfiesExactly(logRecordData -> assertThat(logDataList.get(0)).hasBody("log message 1"));
}

@Test
void logNoSpan() {
logger.info("log message 1");

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
.hasAttributes(Attributes.empty());
}

@Test
void logWithSpan() {
Span span1 = runWithSpan("span1", () -> logger.info("log message 1"));

logger.info("log message 2");

Span span2 = runWithSpan("span2", () -> logger.info("log message 3"));

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(3);
assertThat(logDataList.get(0).getSpanContext()).isEqualTo(span1.getSpanContext());
assertThat(logDataList.get(1).getSpanContext()).isEqualTo(SpanContext.getInvalid());
assertThat(logDataList.get(2).getSpanContext()).isEqualTo(span2.getSpanContext());
}

private static Span runWithSpan(String spanName, Runnable runnable) {
Span span = SdkTracerProvider.builder().build().get("tracer").spanBuilder(spanName).startSpan();
try (Scope ignored = span.makeCurrent()) {
runnable.run();
} finally {
span.end();
}
return span;
}

@Test
void logWithExtras() {
Instant start = Instant.now();
logger.info("log message 1", new IllegalStateException("Error!"));

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
.hasSeverity(Severity.INFO)
.hasSeverityText("INFO")
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.EXCEPTION_TYPE, IllegalStateException.class.getName()),
equalTo(SemanticAttributes.EXCEPTION_MESSAGE, "Error!"),
satisfies(SemanticAttributes.EXCEPTION_STACKTRACE, v -> v.contains("logWithExtras")));

assertThat(logDataList.get(0).getTimestampEpochNanos())
.isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(start.toEpochMilli()))
.isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(Instant.now().toEpochMilli()));
}

@Test
void logContextData() {
ThreadContext.put("key1", "val1");
ThreadContext.put("key2", "val2");
try {
logger.info("log message 1");
} finally {
ThreadContext.clearMap();
}

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
.hasAttributesSatisfyingExactly(
equalTo(stringKey("log4j.context_data.key1"), "val1"),
equalTo(stringKey("log4j.context_data.key2"), "val2"));
}

@Test
void logStringMapMessage() {
StringMapMessage message = new StringMapMessage();
message.put("key1", "val1");
message.put("key2", "val2");
logger.info(message);

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasAttributesSatisfyingExactly(
equalTo(stringKey("log4j.map_message.key1"), "val1"),
equalTo(stringKey("log4j.map_message.key2"), "val2"));
}

@Test
void logStringMapMessageWithSpecialAttribute() {
StringMapMessage message = new StringMapMessage();
message.put("key1", "val1");
message.put("message", "val2");
logger.info(message);

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("val2")
.hasAttributesSatisfyingExactly(equalTo(stringKey("log4j.map_message.key1"), "val1"));
}

@Test
void testCaptureMarkerAttribute() {
String markerName = "aMarker";
Marker marker = MarkerManager.getMarker(markerName);

logger.info(marker, "Message");

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
LogRecordData logData = logDataList.get(0);
assertThat(logData.getAttributes().get(stringKey("log4j.marker"))).isEqualTo(markerName);
}

@Test
void logStructuredDataMessage() {
StructuredDataMessage message = new StructuredDataMessage("an id", "a message", "a type");
message.put("key1", "val1");
message.put("key2", "val2");
logger.info(message);

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
assertThat(logDataList.get(0))
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("a message")
.hasAttributesSatisfyingExactly(
equalTo(stringKey("log4j.map_message.key1"), "val1"),
equalTo(stringKey("log4j.map_message.key2"), "val2"));
}
}
Loading

0 comments on commit 06f22a3

Please sign in to comment.