Don't report 400 level as error for SERVER spans (#4403)
* don't report 400 level as error for server spans * fix HttpServerTest base class * fix JspInstrumentationForward test * split HttpStatusConverter into client and server implementations, and create two HttpSpanStatusExtractor.create methods, one for server and one for client. * rebase * fix test * spotless * fix test * remove unused * use strongly typed attributes converters and rename to overloaded create() * fix tests * remove redundant assert Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
5d7c0dd95b
commit
a50c13382f
|
@ -183,11 +183,10 @@ public class Instrumenter<REQUEST, RESPONSE> {
|
||||||
span.recordException(error);
|
span.recordException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsafeAttributes attributesBuilder = new UnsafeAttributes();
|
UnsafeAttributes attributes = new UnsafeAttributes();
|
||||||
for (AttributesExtractor<? super REQUEST, ? super RESPONSE> extractor : attributesExtractors) {
|
for (AttributesExtractor<? super REQUEST, ? super RESPONSE> extractor : attributesExtractors) {
|
||||||
extractor.onEnd(attributesBuilder, request, response, error);
|
extractor.onEnd(attributes, request, response, error);
|
||||||
}
|
}
|
||||||
Attributes attributes = attributesBuilder;
|
|
||||||
span.setAllAttributes(attributes);
|
span.setAllAttributes(attributes);
|
||||||
|
|
||||||
Instant endTime = null;
|
Instant endTime = null;
|
||||||
|
|
|
@ -19,22 +19,36 @@ import javax.annotation.Nullable;
|
||||||
public final class HttpSpanStatusExtractor<REQUEST, RESPONSE>
|
public final class HttpSpanStatusExtractor<REQUEST, RESPONSE>
|
||||||
implements SpanStatusExtractor<REQUEST, RESPONSE> {
|
implements SpanStatusExtractor<REQUEST, RESPONSE> {
|
||||||
|
|
||||||
|
private final HttpStatusConverter statusConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link SpanStatusExtractor} for HTTP requests, which will use the HTTP status code
|
* Returns the {@link SpanStatusExtractor} for HTTP requests, which will use the HTTP status code
|
||||||
* to determine the {@link StatusCode} if available or fallback to {@linkplain #getDefault() the
|
* to determine the {@link StatusCode} if available or fallback to {@linkplain #getDefault() the
|
||||||
* default status} otherwise.
|
* default status} otherwise.
|
||||||
*/
|
*/
|
||||||
public static <REQUEST, RESPONSE> SpanStatusExtractor<REQUEST, RESPONSE> create(
|
public static <REQUEST, RESPONSE> SpanStatusExtractor<REQUEST, RESPONSE> create(
|
||||||
HttpCommonAttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
|
HttpClientAttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
|
||||||
return new HttpSpanStatusExtractor<>(attributesExtractor);
|
return new HttpSpanStatusExtractor<>(attributesExtractor, HttpStatusConverter.CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link SpanStatusExtractor} for HTTP requests, which will use the HTTP status code
|
||||||
|
* to determine the {@link StatusCode} if available or fallback to {@linkplain #getDefault() the
|
||||||
|
* default status} otherwise.
|
||||||
|
*/
|
||||||
|
public static <REQUEST, RESPONSE> SpanStatusExtractor<REQUEST, RESPONSE> create(
|
||||||
|
HttpServerAttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
|
||||||
|
return new HttpSpanStatusExtractor<>(attributesExtractor, HttpStatusConverter.SERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final HttpCommonAttributesExtractor<? super REQUEST, ? super RESPONSE>
|
private final HttpCommonAttributesExtractor<? super REQUEST, ? super RESPONSE>
|
||||||
attributesExtractor;
|
attributesExtractor;
|
||||||
|
|
||||||
private HttpSpanStatusExtractor(
|
private HttpSpanStatusExtractor(
|
||||||
HttpCommonAttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
|
HttpCommonAttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor,
|
||||||
|
HttpStatusConverter statusConverter) {
|
||||||
this.attributesExtractor = attributesExtractor;
|
this.attributesExtractor = attributesExtractor;
|
||||||
|
this.statusConverter = statusConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,7 +56,7 @@ public final class HttpSpanStatusExtractor<REQUEST, RESPONSE>
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
Integer statusCode = attributesExtractor.statusCode(request, response);
|
Integer statusCode = attributesExtractor.statusCode(request, response);
|
||||||
if (statusCode != null) {
|
if (statusCode != null) {
|
||||||
StatusCode statusCodeObj = HttpStatusConverter.statusFromHttpStatus(statusCode);
|
StatusCode statusCodeObj = statusConverter.statusFromHttpStatus(statusCode);
|
||||||
if (statusCodeObj == StatusCode.ERROR) {
|
if (statusCodeObj == StatusCode.ERROR) {
|
||||||
return statusCodeObj;
|
return statusCodeObj;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.tracer;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.StatusCode;
|
||||||
|
|
||||||
|
final class HttpClientStatusConverter implements HttpStatusConverter {
|
||||||
|
|
||||||
|
static final HttpStatusConverter INSTANCE = new HttpClientStatusConverter();
|
||||||
|
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#status
|
||||||
|
@Override
|
||||||
|
public StatusCode statusFromHttpStatus(int httpStatus) {
|
||||||
|
if (httpStatus >= 100 && httpStatus < 400) {
|
||||||
|
return StatusCode.UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StatusCode.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClientStatusConverter() {}
|
||||||
|
}
|
|
@ -211,7 +211,7 @@ public abstract class HttpClientTracer<REQUEST, CARRIER, RESPONSE> extends BaseT
|
||||||
Integer status = status(response);
|
Integer status = status(response);
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, (long) status);
|
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, (long) status);
|
||||||
StatusCode statusCode = HttpStatusConverter.statusFromHttpStatus(status);
|
StatusCode statusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(status);
|
||||||
if (statusCode != StatusCode.UNSET) {
|
if (statusCode != StatusCode.UNSET) {
|
||||||
span.setStatus(statusCode);
|
span.setStatus(statusCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.tracer;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.StatusCode;
|
||||||
|
|
||||||
|
final class HttpServerStatusConverter implements HttpStatusConverter {
|
||||||
|
|
||||||
|
static final HttpStatusConverter INSTANCE = new HttpServerStatusConverter();
|
||||||
|
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#status
|
||||||
|
@Override
|
||||||
|
public StatusCode statusFromHttpStatus(int httpStatus) {
|
||||||
|
if (httpStatus >= 100 && httpStatus < 500) {
|
||||||
|
return StatusCode.UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StatusCode.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServerStatusConverter() {}
|
||||||
|
}
|
|
@ -276,7 +276,7 @@ public abstract class HttpServerTracer<REQUEST, RESPONSE, CONNECTION, STORAGE> e
|
||||||
|
|
||||||
private static void setStatus(Span span, int status) {
|
private static void setStatus(Span span, int status) {
|
||||||
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, (long) status);
|
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, (long) status);
|
||||||
StatusCode statusCode = HttpStatusConverter.statusFromHttpStatus(status);
|
StatusCode statusCode = HttpStatusConverter.SERVER.statusFromHttpStatus(status);
|
||||||
if (statusCode != StatusCode.UNSET) {
|
if (statusCode != StatusCode.UNSET) {
|
||||||
span.setStatus(statusCode);
|
span.setStatus(statusCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,10 @@ package io.opentelemetry.instrumentation.api.tracer;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.StatusCode;
|
import io.opentelemetry.api.trace.StatusCode;
|
||||||
|
|
||||||
public final class HttpStatusConverter {
|
public interface HttpStatusConverter {
|
||||||
|
|
||||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#status
|
HttpStatusConverter SERVER = HttpServerStatusConverter.INSTANCE;
|
||||||
public static StatusCode statusFromHttpStatus(int httpStatus) {
|
HttpStatusConverter CLIENT = HttpClientStatusConverter.INSTANCE;
|
||||||
if (httpStatus >= 100 && httpStatus < 400) {
|
|
||||||
return StatusCode.UNSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusCode.ERROR;
|
StatusCode statusFromHttpStatus(int httpStatus);
|
||||||
}
|
|
||||||
|
|
||||||
private HttpStatusConverter() {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@ package io.opentelemetry.instrumentation.api.tracer
|
||||||
import io.opentelemetry.api.trace.StatusCode
|
import io.opentelemetry.api.trace.StatusCode
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
class HttpStatusConverterTest extends Specification {
|
class HttpClientStatusConverterTest extends Specification {
|
||||||
|
|
||||||
def "test HTTP #httpStatus to OTel #expectedStatus"() {
|
def "test HTTP #httpStatus to OTel #expectedStatus"() {
|
||||||
when:
|
when:
|
||||||
def status = HttpStatusConverter.statusFromHttpStatus(httpStatus)
|
def status = HttpStatusConverter.CLIENT.statusFromHttpStatus(httpStatus)
|
||||||
|
|
||||||
then:
|
then:
|
||||||
status == expectedStatus
|
status == expectedStatus
|
|
@ -111,7 +111,7 @@ class HttpClientTracerTest extends BaseTracerTest {
|
||||||
def "test onResponse"() {
|
def "test onResponse"() {
|
||||||
setup:
|
setup:
|
||||||
def tracer = newTracer()
|
def tracer = newTracer()
|
||||||
def statusCode = status != null ? HttpStatusConverter.statusFromHttpStatus(status) : null
|
def statusCode = status != null ? HttpStatusConverter.CLIENT.statusFromHttpStatus(status) : null
|
||||||
|
|
||||||
when:
|
when:
|
||||||
tracer.onResponse(span, resp)
|
tracer.onResponse(span, resp)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.tracer
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.StatusCode
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
class HttpServerStatusConverterTest extends Specification {
|
||||||
|
|
||||||
|
def "test HTTP #httpStatus to OTel #expectedStatus"() {
|
||||||
|
when:
|
||||||
|
def status = HttpStatusConverter.SERVER.statusFromHttpStatus(httpStatus)
|
||||||
|
|
||||||
|
then:
|
||||||
|
status == expectedStatus
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||||
|
where:
|
||||||
|
httpStatus | expectedStatus
|
||||||
|
100 | StatusCode.UNSET
|
||||||
|
101 | StatusCode.UNSET
|
||||||
|
102 | StatusCode.UNSET
|
||||||
|
103 | StatusCode.UNSET
|
||||||
|
|
||||||
|
200 | StatusCode.UNSET
|
||||||
|
201 | StatusCode.UNSET
|
||||||
|
202 | StatusCode.UNSET
|
||||||
|
203 | StatusCode.UNSET
|
||||||
|
204 | StatusCode.UNSET
|
||||||
|
205 | StatusCode.UNSET
|
||||||
|
206 | StatusCode.UNSET
|
||||||
|
207 | StatusCode.UNSET
|
||||||
|
208 | StatusCode.UNSET
|
||||||
|
226 | StatusCode.UNSET
|
||||||
|
|
||||||
|
300 | StatusCode.UNSET
|
||||||
|
301 | StatusCode.UNSET
|
||||||
|
302 | StatusCode.UNSET
|
||||||
|
303 | StatusCode.UNSET
|
||||||
|
304 | StatusCode.UNSET
|
||||||
|
305 | StatusCode.UNSET
|
||||||
|
306 | StatusCode.UNSET
|
||||||
|
307 | StatusCode.UNSET
|
||||||
|
308 | StatusCode.UNSET
|
||||||
|
|
||||||
|
400 | StatusCode.UNSET
|
||||||
|
401 | StatusCode.UNSET
|
||||||
|
403 | StatusCode.UNSET
|
||||||
|
404 | StatusCode.UNSET
|
||||||
|
405 | StatusCode.UNSET
|
||||||
|
406 | StatusCode.UNSET
|
||||||
|
407 | StatusCode.UNSET
|
||||||
|
408 | StatusCode.UNSET
|
||||||
|
409 | StatusCode.UNSET
|
||||||
|
410 | StatusCode.UNSET
|
||||||
|
411 | StatusCode.UNSET
|
||||||
|
412 | StatusCode.UNSET
|
||||||
|
413 | StatusCode.UNSET
|
||||||
|
414 | StatusCode.UNSET
|
||||||
|
415 | StatusCode.UNSET
|
||||||
|
416 | StatusCode.UNSET
|
||||||
|
417 | StatusCode.UNSET
|
||||||
|
418 | StatusCode.UNSET
|
||||||
|
421 | StatusCode.UNSET
|
||||||
|
422 | StatusCode.UNSET
|
||||||
|
423 | StatusCode.UNSET
|
||||||
|
424 | StatusCode.UNSET
|
||||||
|
425 | StatusCode.UNSET
|
||||||
|
426 | StatusCode.UNSET
|
||||||
|
428 | StatusCode.UNSET
|
||||||
|
429 | StatusCode.UNSET
|
||||||
|
431 | StatusCode.UNSET
|
||||||
|
451 | StatusCode.UNSET
|
||||||
|
|
||||||
|
500 | StatusCode.ERROR
|
||||||
|
501 | StatusCode.ERROR
|
||||||
|
502 | StatusCode.ERROR
|
||||||
|
503 | StatusCode.ERROR
|
||||||
|
504 | StatusCode.ERROR
|
||||||
|
505 | StatusCode.ERROR
|
||||||
|
506 | StatusCode.ERROR
|
||||||
|
507 | StatusCode.ERROR
|
||||||
|
508 | StatusCode.ERROR
|
||||||
|
510 | StatusCode.ERROR
|
||||||
|
511 | StatusCode.ERROR
|
||||||
|
|
||||||
|
// Don't exist
|
||||||
|
99 | StatusCode.ERROR
|
||||||
|
600 | StatusCode.ERROR
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.StatusCode;
|
||||||
|
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class HttpClientSpanStatusExtractorTest {
|
||||||
|
@Mock private HttpClientAttributesExtractor<Map<String, String>, Map<String, String>> extractor;
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
||||||
|
void hasStatus(int statusCode) {
|
||||||
|
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(extractor)
|
||||||
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
|
.isEqualTo(HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
||||||
|
void hasStatusAndException(int statusCode) {
|
||||||
|
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
|
||||||
|
// Presence of exception has no effect.
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(extractor)
|
||||||
|
.extract(
|
||||||
|
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
||||||
|
.isEqualTo(StatusCode.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasNoStatus_fallsBackToDefault_unset() {
|
||||||
|
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(extractor)
|
||||||
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
|
.isEqualTo(StatusCode.UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasNoStatus_fallsBackToDefault_error() {
|
||||||
|
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(extractor)
|
||||||
|
.extract(
|
||||||
|
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
||||||
|
.isEqualTo(StatusCode.ERROR);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,27 +22,53 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class HttpSpanStatusExtractorTest {
|
class HttpSpanStatusExtractorTest {
|
||||||
@Mock private HttpCommonAttributesExtractor<Map<String, String>, Map<String, String>> extractor;
|
@Mock
|
||||||
|
private HttpServerAttributesExtractor<Map<String, String>, Map<String, String>> serverExtractor;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HttpClientAttributesExtractor<Map<String, String>, Map<String, String>> clientExtractor;
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 500, 501, 600, 601})
|
||||||
void hasStatus(int statusCode) {
|
void hasServerStatus(int statusCode) {
|
||||||
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
when(serverExtractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
HttpSpanStatusExtractor.create(extractor)
|
HttpSpanStatusExtractor.create(serverExtractor)
|
||||||
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
.isEqualTo(HttpStatusConverter.statusFromHttpStatus(statusCode));
|
.isEqualTo(HttpStatusConverter.SERVER.statusFromHttpStatus(statusCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
||||||
void hasStatusAndException(int statusCode) {
|
void hasClientStatus(int statusCode) {
|
||||||
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
when(clientExtractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(clientExtractor)
|
||||||
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
|
.isEqualTo(HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
||||||
|
void hasServerStatusAndException(int statusCode) {
|
||||||
|
when(serverExtractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
|
||||||
// Presence of exception has no effect.
|
// Presence of exception has no effect.
|
||||||
assertThat(
|
assertThat(
|
||||||
HttpSpanStatusExtractor.create(extractor)
|
HttpSpanStatusExtractor.create(serverExtractor)
|
||||||
|
.extract(
|
||||||
|
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
||||||
|
.isEqualTo(StatusCode.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601})
|
||||||
|
void hasClientStatusAndException(int statusCode) {
|
||||||
|
when(clientExtractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
|
||||||
|
|
||||||
|
// Presence of exception has no effect.
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(clientExtractor)
|
||||||
.extract(
|
.extract(
|
||||||
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
||||||
.isEqualTo(StatusCode.ERROR);
|
.isEqualTo(StatusCode.ERROR);
|
||||||
|
@ -50,22 +76,32 @@ class HttpSpanStatusExtractorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasNoStatus_fallsBackToDefault_unset() {
|
void hasNoStatus_fallsBackToDefault_unset() {
|
||||||
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
when(clientExtractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
when(serverExtractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
HttpSpanStatusExtractor.create(extractor)
|
HttpSpanStatusExtractor.create(serverExtractor)
|
||||||
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
|
.isEqualTo(StatusCode.UNSET);
|
||||||
|
assertThat(
|
||||||
|
HttpSpanStatusExtractor.create(clientExtractor)
|
||||||
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
|
||||||
.isEqualTo(StatusCode.UNSET);
|
.isEqualTo(StatusCode.UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasNoStatus_fallsBackToDefault_error() {
|
void hasNoStatus_fallsBackToDefault_error() {
|
||||||
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
when(clientExtractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
when(serverExtractor.statusCode(anyMap(), anyMap())).thenReturn(null);
|
||||||
|
|
||||||
assertThat(
|
StatusCode serverStatusCode =
|
||||||
HttpSpanStatusExtractor.create(extractor)
|
HttpSpanStatusExtractor.create(serverExtractor)
|
||||||
.extract(
|
.extract(Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException());
|
||||||
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
|
assertThat(serverStatusCode).isEqualTo(StatusCode.ERROR);
|
||||||
.isEqualTo(StatusCode.ERROR);
|
|
||||||
|
StatusCode clientStatusCode =
|
||||||
|
HttpSpanStatusExtractor.create(clientExtractor)
|
||||||
|
.extract(Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException());
|
||||||
|
assertThat(clientStatusCode).isEqualTo(StatusCode.ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ public class HttpUrlConnectionInstrumentation implements TypeInstrumentation {
|
||||||
if (httpUrlState != null) {
|
if (httpUrlState != null) {
|
||||||
Span span = Java8BytecodeBridge.spanFromContext(httpUrlState.context);
|
Span span = Java8BytecodeBridge.spanFromContext(httpUrlState.context);
|
||||||
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, returnValue);
|
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, returnValue);
|
||||||
StatusCode statusCode = HttpStatusConverter.statusFromHttpStatus(returnValue);
|
StatusCode statusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(returnValue);
|
||||||
if (statusCode != StatusCode.UNSET) {
|
if (statusCode != StatusCode.UNSET) {
|
||||||
span.setStatus(statusCode);
|
span.setStatus(statusCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import javax.ws.rs.ext.Provider
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
||||||
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
import static io.opentelemetry.api.trace.StatusCode.UNSET
|
||||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderServerTrace
|
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderServerTrace
|
||||||
|
|
||||||
@Unroll
|
@Unroll
|
||||||
|
@ -77,7 +77,7 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
|
||||||
name parentSpanName != null ? parentSpanName : "test.span"
|
name parentSpanName != null ? parentSpanName : "test.span"
|
||||||
kind SERVER
|
kind SERVER
|
||||||
if (runsOnServer() && abortNormal) {
|
if (runsOnServer() && abortNormal) {
|
||||||
status ERROR
|
status UNSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
span(1) {
|
span(1) {
|
||||||
|
|
|
@ -241,7 +241,6 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
serverSpan(trace, index, traceID, parentID, method,
|
serverSpan(trace, index, traceID, parentID, method,
|
||||||
endpoint == PATH_PARAM ? getContextPath() + "/path/{id}/param" : endpoint.resolvePath(address).path,
|
endpoint == PATH_PARAM ? getContextPath() + "/path/{id}/param" : endpoint.resolvePath(address).path,
|
||||||
endpoint.resolve(address),
|
endpoint.resolve(address),
|
||||||
endpoint.errored,
|
|
||||||
endpoint.status,
|
endpoint.status,
|
||||||
endpoint.query)
|
endpoint.query)
|
||||||
}
|
}
|
||||||
|
@ -254,7 +253,6 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
serverSpan(trace, index, null, null, "GET",
|
serverSpan(trace, index, null, null, "GET",
|
||||||
rawUrl.path,
|
rawUrl.path,
|
||||||
rawUrl.toURI(),
|
rawUrl.toURI(),
|
||||||
statusCode >= 500,
|
|
||||||
statusCode,
|
statusCode,
|
||||||
null)
|
null)
|
||||||
}
|
}
|
||||||
|
@ -266,13 +264,12 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
String method,
|
String method,
|
||||||
String path,
|
String path,
|
||||||
URI fullUrl,
|
URI fullUrl,
|
||||||
boolean isError,
|
|
||||||
int statusCode,
|
int statusCode,
|
||||||
String query) {
|
String query) {
|
||||||
trace.span(index) {
|
trace.span(index) {
|
||||||
name path
|
name path
|
||||||
kind SERVER
|
kind SERVER
|
||||||
if (isError) {
|
if (statusCode >= 500) {
|
||||||
status ERROR
|
status ERROR
|
||||||
}
|
}
|
||||||
if (parentID != null) {
|
if (parentID != null) {
|
||||||
|
|
|
@ -57,8 +57,7 @@ public final class JettyClientInstrumenterBuilder {
|
||||||
JettyHttpClientNetAttributesExtractor netAttributesExtractor =
|
JettyHttpClientNetAttributesExtractor netAttributesExtractor =
|
||||||
new JettyHttpClientNetAttributesExtractor();
|
new JettyHttpClientNetAttributesExtractor();
|
||||||
|
|
||||||
Instrumenter<Request, Response> instrumenter =
|
return Instrumenter.<Request, Response>newBuilder(
|
||||||
Instrumenter.<Request, Response>newBuilder(
|
|
||||||
this.openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor)
|
this.openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor)
|
||||||
.setSpanStatusExtractor(spanStatusExtractor)
|
.setSpanStatusExtractor(spanStatusExtractor)
|
||||||
.addAttributesExtractor(httpAttributesExtractor)
|
.addAttributesExtractor(httpAttributesExtractor)
|
||||||
|
@ -66,6 +65,5 @@ public final class JettyClientInstrumenterBuilder {
|
||||||
.addAttributesExtractors(additionalExtractors)
|
.addAttributesExtractors(additionalExtractors)
|
||||||
.addRequestMetrics(HttpClientMetrics.get())
|
.addRequestMetrics(HttpClientMetrics.get())
|
||||||
.newClientInstrumenter(new HttpHeaderSetter());
|
.newClientInstrumenter(new HttpHeaderSetter());
|
||||||
return instrumenter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.nio.file.Files
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
||||||
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||||
|
import static io.opentelemetry.api.trace.StatusCode.UNSET
|
||||||
|
|
||||||
class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
|
class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
|
||||||
|
|
||||||
|
@ -425,7 +426,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
|
||||||
hasNoParent()
|
hasNoParent()
|
||||||
name "/$jspWebappContext/forwards/forwardToNonExistent.jsp"
|
name "/$jspWebappContext/forwards/forwardToNonExistent.jsp"
|
||||||
kind SERVER
|
kind SERVER
|
||||||
status ERROR
|
status UNSET
|
||||||
attributes {
|
attributes {
|
||||||
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
|
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
|
||||||
"${SemanticAttributes.NET_PEER_PORT.key}" Long
|
"${SemanticAttributes.NET_PEER_PORT.key}" Long
|
||||||
|
|
|
@ -48,7 +48,7 @@ abstract class AbstractServlet3MappingTest<SERVER, CONTEXT> extends AgentInstrum
|
||||||
span(0) {
|
span(0) {
|
||||||
name getContextPath() + spanName
|
name getContextPath() + spanName
|
||||||
kind SpanKind.SERVER
|
kind SpanKind.SERVER
|
||||||
if (!success) {
|
if (!success && response.status().code() >= 500) {
|
||||||
status ERROR
|
status ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ abstract class AbstractServlet5MappingTest<SERVER, CONTEXT> extends AgentInstrum
|
||||||
span(0) {
|
span(0) {
|
||||||
name getContextPath() + spanName
|
name getContextPath() + spanName
|
||||||
kind SpanKind.SERVER
|
kind SpanKind.SERVER
|
||||||
if (!success) {
|
if (!success && response.status().code() >= 500) {
|
||||||
status ERROR
|
status ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import spock.lang.Unroll
|
||||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
import static io.opentelemetry.api.trace.SpanKind.SERVER
|
||||||
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||||
|
import static io.opentelemetry.api.trace.StatusCode.UNSET
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = [SpringWebFluxTestApplication, ForceNettyAutoConfiguration])
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = [SpringWebFluxTestApplication, ForceNettyAutoConfiguration])
|
||||||
class SpringWebfluxTest extends AgentInstrumentationSpecification {
|
class SpringWebfluxTest extends AgentInstrumentationSpecification {
|
||||||
|
@ -286,7 +287,7 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification {
|
||||||
name "/**"
|
name "/**"
|
||||||
kind SERVER
|
kind SERVER
|
||||||
hasNoParent()
|
hasNoParent()
|
||||||
status ERROR
|
status UNSET
|
||||||
attributes {
|
attributes {
|
||||||
"${SemanticAttributes.NET_PEER_NAME}" { it == null || it == "localhost" }
|
"${SemanticAttributes.NET_PEER_NAME}" { it == null || it == "localhost" }
|
||||||
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
|
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
|
||||||
|
|
|
@ -171,7 +171,6 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
||||||
final String fragment
|
final String fragment
|
||||||
final int status
|
final int status
|
||||||
final String body
|
final String body
|
||||||
final Boolean errored
|
|
||||||
|
|
||||||
ServerEndpoint(String uri, int status, String body) {
|
ServerEndpoint(String uri, int status, String body) {
|
||||||
this.uriObj = URI.create(uri)
|
this.uriObj = URI.create(uri)
|
||||||
|
@ -180,7 +179,6 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
||||||
this.fragment = uriObj.fragment
|
this.fragment = uriObj.fragment
|
||||||
this.status = status
|
this.status = status
|
||||||
this.body = body
|
this.body = body
|
||||||
this.errored = status >= 400
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPath() {
|
String getPath() {
|
||||||
|
@ -594,7 +592,7 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
||||||
trace.span(index) {
|
trace.span(index) {
|
||||||
name expectedServerSpanName(endpoint)
|
name expectedServerSpanName(endpoint)
|
||||||
kind SpanKind.SERVER // can't use static import because of SERVER type parameter
|
kind SpanKind.SERVER // can't use static import because of SERVER type parameter
|
||||||
if (endpoint.errored) {
|
if (endpoint.status >= 500) {
|
||||||
status StatusCode.ERROR
|
status StatusCode.ERROR
|
||||||
}
|
}
|
||||||
if (parentID != null) {
|
if (parentID != null) {
|
||||||
|
|
Loading…
Reference in New Issue