diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/InstrumenterCustomizer.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/InstrumenterCustomizer.java index e616829e602e..ab47b3092b74 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/InstrumenterCustomizer.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/InstrumenterCustomizer.java @@ -11,6 +11,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor; import java.util.function.Function; import java.util.function.UnaryOperator; @@ -106,6 +107,16 @@ default InstrumenterCustomizer setSpanNameExtractor( InstrumenterCustomizer setSpanNameExtractor( UnaryOperator> spanNameExtractor); + /** + * Sets a transformer function that will modify the {@link SpanStatusExtractor}. This allows + * customizing how span statuses are generated for the instrumented operations. + * + * @param spanStatusExtractor function that transforms the original span status extractor + * @return this InstrumenterCustomizer for method chaining + */ + InstrumenterCustomizer setSpanStatusExtractor( + UnaryOperator> spanStatusExtractor); + /** Types of instrumentation. */ enum InstrumentationType { HTTP_CLIENT, diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/internal/InstrumenterCustomizerImpl.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/internal/InstrumenterCustomizerImpl.java index 7b3a8d65e163..a45862652156 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/internal/InstrumenterCustomizerImpl.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/instrumenter/internal/InstrumenterCustomizerImpl.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.internal.InternalInstrumenterCustomizer; import io.opentelemetry.instrumentation.api.internal.SpanKey; import java.util.HashMap; @@ -87,4 +88,11 @@ public InstrumenterCustomizer setSpanNameExtractor( customizer.setSpanNameExtractor(spanNameExtractor); return this; } + + @Override + public InstrumenterCustomizer setSpanStatusExtractor( + UnaryOperator> spanStatusExtractor) { + customizer.setSpanStatusExtractor(spanStatusExtractor); + return this; + } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java index 0d476656b9e5..eb80bd8dd1d7 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java @@ -442,6 +442,14 @@ public void setSpanNameExtractor( builder.spanNameExtractor = spanNameExtractorTransformer.apply(builder.spanNameExtractor); } + + @Override + public void setSpanStatusExtractor( + UnaryOperator> + spanStatusExtractorTransformer) { + builder.spanStatusExtractor = + spanStatusExtractorTransformer.apply(builder.spanStatusExtractor); + } }); } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/InternalInstrumenterCustomizer.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/InternalInstrumenterCustomizer.java index d71fde7dd145..6f88bd8c225e 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/InternalInstrumenterCustomizer.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/InternalInstrumenterCustomizer.java @@ -9,6 +9,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import java.util.function.UnaryOperator; /** @@ -32,4 +33,8 @@ void addAttributesExtractors( void setSpanNameExtractor( UnaryOperator> spanNameExtractorTransformer); + + void setSpanStatusExtractor( + UnaryOperator> + spanStatusExtractorTransformer); } diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumentationCustomizerTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumentationCustomizerTest.java index ae86620e62a5..725f71726c1c 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumentationCustomizerTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumentationCustomizerTest.java @@ -21,6 +21,7 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizer.InstrumentationType; import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizerProvider; @@ -359,6 +360,50 @@ void testSetSpanNameExtractor() { .hasAttributes(Attributes.empty()))); } + @Test + void testSetSpanStatusExtractor() { + AtomicBoolean customizerCalled = new AtomicBoolean(); + setCustomizer( + customizer -> { + customizerCalled.set(true); + customizer.setSpanStatusExtractor( + unused -> + (spanStatusBuilder, request, response, error) -> + spanStatusBuilder.setStatus(StatusCode.OK)); + }); + + Instrumenter, Map> instrumenter = + Instrumenter., Map>builder( + otelTesting.getOpenTelemetry(), "test", unused -> "span") + .setSpanStatusExtractor( + (spanStatusBuilder, request, response, error) -> + spanStatusBuilder.setStatus(StatusCode.ERROR)) + .buildInstrumenter(); + + assertThat(customizerCalled).isTrue(); + + Context context = instrumenter.start(Context.root(), REQUEST); + SpanContext spanContext = Span.fromContext(context).getSpanContext(); + assertThat(spanContext.isValid()).isTrue(); + + instrumenter.end(context, REQUEST, RESPONSE, null); + + otelTesting + .assertTraces() + .hasTracesSatisfyingExactly( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("span") + .hasKind(SpanKind.INTERNAL) + .hasInstrumentationScopeInfo(InstrumentationScopeInfo.create("test")) + .hasTraceId(spanContext.getTraceId()) + .hasSpanId(spanContext.getSpanId()) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.ok()) + .hasAttributes(Attributes.empty()))); + } + static class AttributesExtractor1 implements AttributesExtractor, Map> {