Skip to content

Commit d06b0e9

Browse files
committed
Add default ExceptionEventExtractor
1 parent 5732f14 commit d06b0e9

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder;
1717
import io.opentelemetry.api.logs.LogRecordBuilder;
1818
import io.opentelemetry.api.logs.Logger;
19+
import io.opentelemetry.api.logs.Severity;
1920
import io.opentelemetry.api.trace.Span;
2021
import io.opentelemetry.api.trace.SpanBuilder;
2122
import io.opentelemetry.api.trace.SpanKind;
@@ -104,10 +105,6 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
104105
Instrumenter(InstrumenterBuilder<REQUEST, RESPONSE> builder) {
105106
this.instrumentationName = builder.instrumentationName;
106107
this.tracer = builder.buildTracer();
107-
this.logger =
108-
emitExceptionAsLogs() && builder.exceptionEventExtractor != null
109-
? builder.buildLogger()
110-
: null;
111108
this.spanNameExtractor = builder.spanNameExtractor;
112109
this.spanKindExtractor = builder.spanKindExtractor;
113110
this.spanStatusExtractor = builder.spanStatusExtractor;
@@ -118,10 +115,20 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
118115
this.operationListenerAttributesExtractors =
119116
builder.operationListenerAttributesExtractors.toArray(new AttributesExtractor[0]);
120117
this.errorCauseExtractor = builder.errorCauseExtractor;
121-
this.exceptionEventExtractor = builder.exceptionEventExtractor;
122118
this.propagateOperationListenersToOnEnd = builder.propagateOperationListenersToOnEnd;
123119
this.enabled = builder.enabled;
124120
this.spanSuppressor = builder.buildSpanSuppressor();
121+
122+
if (emitExceptionAsLogs()) {
123+
this.logger = builder.buildLogger();
124+
this.exceptionEventExtractor =
125+
builder.exceptionEventExtractor != null
126+
? builder.exceptionEventExtractor
127+
: defaultExceptionEventExtractor(builder.instrumentationName);
128+
} else {
129+
this.logger = null;
130+
this.exceptionEventExtractor = builder.exceptionEventExtractor;
131+
}
125132
}
126133

127134
/**
@@ -350,6 +357,15 @@ private void emitExceptionLog(Context context, Throwable throwable, REQUEST requ
350357
logRecordBuilder.emit();
351358
}
352359

360+
private static <REQUEST> InternalExceptionEventExtractor<REQUEST> defaultExceptionEventExtractor(
361+
String instrumentationName) {
362+
String eventName = instrumentationName + ".exception";
363+
return (logRecordBuilder, context, request) -> {
364+
logRecordBuilder.setEventName(eventName);
365+
logRecordBuilder.setSeverity(Severity.WARN);
366+
};
367+
}
368+
353369
private static long getNanos(@Nullable Instant time) {
354370
if (time == null) {
355371
return System.nanoTime();

instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,44 @@ void consumer_error_with_explicit_severity() {
449449
}
450450
}
451451

452+
@Test
453+
void error_default_exception_event_extractor() {
454+
// When no ExceptionEventExtractor is explicitly set, a default should be used
455+
// that sets event name to instrumentationName + ".exception" and severity to WARN.
456+
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =
457+
Instrumenter.<Map<String, String>, Map<String, String>>builder(
458+
otelTesting.getOpenTelemetry(), "test", unused -> "span")
459+
.buildInstrumenter();
460+
461+
Context context = instrumenter.start(Context.root(), REQUEST);
462+
assertThat(Span.fromContext(context).getSpanContext().isValid()).isTrue();
463+
464+
IllegalStateException error = new IllegalStateException("test");
465+
instrumenter.end(context, REQUEST, RESPONSE, error);
466+
467+
otelTesting
468+
.assertTraces()
469+
.hasTracesSatisfyingExactly(
470+
trace ->
471+
trace.hasSpansSatisfyingExactly(
472+
span ->
473+
span.hasName("span")
474+
.hasStatus(StatusData.error())
475+
.hasException(emitExceptionAsSpanEvents() ? error : null)));
476+
477+
if (emitExceptionAsLogs()) {
478+
List<LogRecordData> logs = otelTesting.getLogRecords();
479+
assertThat(logs).hasSize(1);
480+
LogRecordData log = logs.get(0);
481+
assertThat(log.getSeverity()).isEqualTo(Severity.WARN);
482+
assertThat(log.getEventName()).isEqualTo("test.exception");
483+
assertThat(log.getAttributes().get(EXCEPTION_TYPE))
484+
.isEqualTo("java.lang.IllegalStateException");
485+
assertThat(log.getAttributes().get(EXCEPTION_MESSAGE)).isEqualTo("test");
486+
assertThat(log.getAttributes().get(EXCEPTION_STACKTRACE)).isNotNull();
487+
}
488+
}
489+
452490
@Test
453491
void client_parent() {
454492
Instrumenter<Map<String, String>, Map<String, String>> instrumenter =

0 commit comments

Comments
 (0)