Instrumenter API improvements (#2860)

* Instrumenter API improvements

* Move HTTP & net classes to separate packages
* Remove `db` prefix from method names in `DbAttributesExtractor`
* Add request-only net attributes extractor (it'll be needed in spring-sleuth-otel once we decide to use Instrumenters there)

* One NetAttributesExtractor class, javadocs

* add missing @Nullable

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* spotless

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Mateusz Rzeszutek 2021-05-06 11:35:38 +02:00 committed by GitHub
parent a568daaf0a
commit c9a3793bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 276 additions and 195 deletions

View File

@ -7,6 +7,9 @@ package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
/**
* Extractor of {@link io.opentelemetry.api.common.Attributes} for a given request and response.
@ -16,6 +19,7 @@ import io.opentelemetry.api.common.AttributesBuilder;
* lifecycle. It is best to populate as much as possible in {@link #onStart(AttributesBuilder,
* Object)} to have it available during sampling.
*
* @see DbAttributesExtractor
* @see HttpAttributesExtractor
* @see NetAttributesExtractor
*/

View File

@ -1,28 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
final class HttpSpanNameExtractor<REQUEST> implements SpanNameExtractor<REQUEST> {
private final HttpAttributesExtractor<REQUEST, ?> attributesExtractor;
HttpSpanNameExtractor(HttpAttributesExtractor<REQUEST, ?> attributesExtractor) {
this.attributesExtractor = attributesExtractor;
}
@Override
public String extract(REQUEST request) {
String route = attributesExtractor.route(request);
if (route != null) {
return route;
}
String method = attributesExtractor.method(request);
if (method != null) {
return "HTTP " + method;
}
return "HTTP request";
}
}

View File

@ -1,29 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
final class HttpSpanStatusExtractor<REQUEST, RESPONSE>
implements SpanStatusExtractor<REQUEST, RESPONSE> {
private final HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor;
protected HttpSpanStatusExtractor(
HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor) {
this.attributesExtractor = attributesExtractor;
}
@Override
public StatusCode extract(REQUEST request, RESPONSE response, Throwable error) {
Long statusCode = attributesExtractor.statusCode(request, response);
if (statusCode != null) {
return HttpStatusConverter.statusFromHttpStatus((int) (long) statusCode);
}
return SpanStatusExtractor.getDefault().extract(request, response, error);
}
}

View File

@ -14,15 +14,6 @@ package io.opentelemetry.instrumentation.api.instrumenter;
@FunctionalInterface
public interface SpanNameExtractor<REQUEST> {
/**
* Returns a {@link SpanNameExtractor} which should be used for HTTP requests. HTTP attributes
* will be examined to determine the name of the span.
*/
static <REQUEST> SpanNameExtractor<REQUEST> http(
HttpAttributesExtractor<REQUEST, ?> attributesExtractor) {
return new HttpSpanNameExtractor<>(attributesExtractor);
}
/** Returns the span name. */
String extract(REQUEST request);
}

View File

@ -24,16 +24,6 @@ public interface SpanStatusExtractor<REQUEST, RESPONSE> {
return (SpanStatusExtractor<REQUEST, RESPONSE>) DefaultSpanStatusExtractor.INSTANCE;
}
/**
* 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.
*/
static <REQUEST, RESPONSE> SpanStatusExtractor<REQUEST, RESPONSE> http(
HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor) {
return new HttpSpanStatusExtractor<>(attributesExtractor);
}
/** Returns the {@link StatusCode}. */
StatusCode extract(REQUEST request, RESPONSE response, @Nullable Throwable error);
}

View File

@ -22,32 +22,32 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class DbAttributesExtractor<REQUEST> extends AttributesExtractor<REQUEST, Void> {
@Override
protected void onStart(AttributesBuilder attributes, REQUEST request) {
set(attributes, SemanticAttributes.DB_SYSTEM, dbSystem(request));
set(attributes, SemanticAttributes.DB_USER, dbUser(request));
set(attributes, SemanticAttributes.DB_NAME, dbName(request));
set(attributes, SemanticAttributes.DB_CONNECTION_STRING, dbConnectionString(request));
set(attributes, SemanticAttributes.DB_STATEMENT, dbStatement(request));
set(attributes, SemanticAttributes.DB_OPERATION, dbOperation(request));
set(attributes, SemanticAttributes.DB_SYSTEM, system(request));
set(attributes, SemanticAttributes.DB_USER, user(request));
set(attributes, SemanticAttributes.DB_NAME, name(request));
set(attributes, SemanticAttributes.DB_CONNECTION_STRING, connectionString(request));
set(attributes, SemanticAttributes.DB_STATEMENT, statement(request));
set(attributes, SemanticAttributes.DB_OPERATION, operation(request));
}
@Override
protected final void onEnd(AttributesBuilder attributes, REQUEST request, Void unused) {}
@Nullable
protected abstract String dbSystem(REQUEST request);
protected abstract String system(REQUEST request);
@Nullable
protected abstract String dbUser(REQUEST request);
protected abstract String user(REQUEST request);
@Nullable
protected abstract String dbName(REQUEST request);
protected abstract String name(REQUEST request);
@Nullable
protected abstract String dbConnectionString(REQUEST request);
protected abstract String connectionString(REQUEST request);
@Nullable
protected abstract String dbStatement(REQUEST request);
protected abstract String statement(REQUEST request);
@Nullable
protected abstract String dbOperation(REQUEST request);
protected abstract String operation(REQUEST request);
}

View File

@ -11,11 +11,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class DbSpanNameExtractor<REQUEST> implements SpanNameExtractor<REQUEST> {
/**
* Returns a {@link SpanNameExtractor} that constructs the span name according to DB semantic
* conventions: {@code <db.operation> <db.name><table>}.
* conventions: {@code <db.operation> <db.name>.<table>}.
*
* @see DbAttributesExtractor#dbOperation(Object) used to extract {@code <db.operation>}.
* @see DbAttributesExtractor#dbName(Object) used to extract {@code <db.name>}.
* @see SqlAttributesExtractor#dbTable(Object) used to extract {@code <db.table>}.
* @see DbAttributesExtractor#operation(Object) used to extract {@code <db.operation>}.
* @see DbAttributesExtractor#name(Object) used to extract {@code <db.name>}.
* @see SqlAttributesExtractor#table(Object) used to extract {@code <db.table>}.
*/
public static <REQUEST> SpanNameExtractor<REQUEST> create(
DbAttributesExtractor<REQUEST> attributesExtractor) {
@ -32,8 +32,8 @@ public final class DbSpanNameExtractor<REQUEST> implements SpanNameExtractor<REQ
@Override
public String extract(REQUEST request) {
String operation = attributesExtractor.dbOperation(request);
String dbName = attributesExtractor.dbName(request);
String operation = attributesExtractor.operation(request);
String dbName = attributesExtractor.name(request);
if (operation == null) {
return dbName == null ? DEFAULT_SPAN_NAME : dbName;
}
@ -58,7 +58,7 @@ public final class DbSpanNameExtractor<REQUEST> implements SpanNameExtractor<REQ
@Nullable
private String getTableName(REQUEST request) {
if (attributesExtractor instanceof SqlAttributesExtractor) {
return ((SqlAttributesExtractor<REQUEST>) attributesExtractor).dbTable(request);
return ((SqlAttributesExtractor<REQUEST>) attributesExtractor).table(request);
}
return null;
}

View File

@ -29,35 +29,35 @@ public abstract class SqlAttributesExtractor<REQUEST> extends DbAttributesExtrac
super.onStart(attributes, request);
AttributeKey<String> dbTable = dbTableAttribute();
if (dbTable != null) {
set(attributes, dbTable, dbTable(request));
set(attributes, dbTable, table(request));
}
}
@Nullable
@Override
protected final String dbStatement(REQUEST request) {
protected final String statement(REQUEST request) {
return sanitize(request).getFullStatement();
}
@Nullable
@Override
protected final String dbOperation(REQUEST request) {
protected final String operation(REQUEST request) {
return sanitize(request).getOperation();
}
@Nullable
protected final String dbTable(REQUEST request) {
protected final String table(REQUEST request) {
return sanitize(request).getTable();
}
private SqlStatementInfo sanitize(REQUEST request) {
// sanitized statement is cached
return SqlStatementSanitizer.sanitize(rawDbStatement(request));
return SqlStatementSanitizer.sanitize(rawStatement(request));
}
@Nullable
protected abstract AttributeKey<String> dbTableAttribute();
@Nullable
protected abstract String rawDbStatement(REQUEST request);
protected abstract String rawStatement(REQUEST request);
}

View File

@ -3,9 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.http;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import org.checkerframework.checker.nullness.qual.Nullable;

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.http;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
/**
* Extractor of the <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name">HTTP
* span name</a>. Instrumentation of HTTP server or client frameworks should use this class to
* comply with OpenTelemetry HTTP semantic conventions.
*/
public final class HttpSpanNameExtractor<REQUEST> implements SpanNameExtractor<REQUEST> {
/**
* Returns a {@link SpanNameExtractor} which should be used for HTTP requests. HTTP attributes
* will be examined to determine the name of the span.
*/
public static <REQUEST> SpanNameExtractor<REQUEST> create(
HttpAttributesExtractor<REQUEST, ?> attributesExtractor) {
return new HttpSpanNameExtractor<>(attributesExtractor);
}
private final HttpAttributesExtractor<REQUEST, ?> attributesExtractor;
private HttpSpanNameExtractor(HttpAttributesExtractor<REQUEST, ?> attributesExtractor) {
this.attributesExtractor = attributesExtractor;
}
@Override
public String extract(REQUEST request) {
String route = attributesExtractor.route(request);
if (route != null) {
return route;
}
String method = attributesExtractor.method(request);
if (method != null) {
return "HTTP " + method;
}
return "HTTP request";
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.http;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
/**
* Extractor of the <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status">HTTP
* span status</a>. Instrumentation of HTTP server or client frameworks should use this class to
* comply with OpenTelemetry HTTP semantic conventions.
*/
public final class HttpSpanStatusExtractor<REQUEST, RESPONSE>
implements SpanStatusExtractor<REQUEST, RESPONSE> {
/**
* 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(
HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor) {
return new HttpSpanStatusExtractor<>(attributesExtractor);
}
private final HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor;
private HttpSpanStatusExtractor(HttpAttributesExtractor<REQUEST, RESPONSE> attributesExtractor) {
this.attributesExtractor = attributesExtractor;
}
@Override
public StatusCode extract(REQUEST request, RESPONSE response, Throwable error) {
Long statusCode = attributesExtractor.statusCode(request, response);
if (statusCode != null) {
return HttpStatusConverter.statusFromHttpStatus(statusCode.intValue());
}
return SpanStatusExtractor.getDefault().extract(request, response, error);
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.net;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -19,12 +19,17 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class InetSocketAddressNetAttributesExtractor<REQUEST, RESPONSE>
extends NetAttributesExtractor<REQUEST, RESPONSE> {
/**
* This method will be called twice: both when the request starts ({@code response} is always null
* then) and when the response ends. This way it is possible to capture net attributes in both
* phases of processing.
*/
@Nullable
protected abstract InetSocketAddress getAddress(REQUEST request, RESPONSE response);
protected abstract InetSocketAddress getAddress(REQUEST request, @Nullable RESPONSE response);
@Override
@Nullable
protected final String peerName(REQUEST request, RESPONSE response) {
protected final String peerName(REQUEST request, @Nullable RESPONSE response) {
InetSocketAddress address = getAddress(request, response);
if (address == null) {
return null;
@ -37,7 +42,7 @@ public abstract class InetSocketAddressNetAttributesExtractor<REQUEST, RESPONSE>
@Override
@Nullable
protected final Long peerPort(REQUEST request, RESPONSE response) {
protected final Long peerPort(REQUEST request, @Nullable RESPONSE response) {
InetSocketAddress address = getAddress(request, response);
if (address == null) {
return null;
@ -47,7 +52,7 @@ public abstract class InetSocketAddressNetAttributesExtractor<REQUEST, RESPONSE>
@Override
@Nullable
protected final String peerIp(REQUEST request, RESPONSE response) {
protected final String peerIp(REQUEST request, @Nullable RESPONSE response) {
InetSocketAddress address = getAddress(request, response);
if (address == null) {
return null;

View File

@ -3,9 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.net;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -21,15 +22,14 @@ public abstract class NetAttributesExtractor<REQUEST, RESPONSE>
@Override
protected final void onStart(AttributesBuilder attributes, REQUEST request) {
set(attributes, SemanticAttributes.NET_TRANSPORT, transport(request));
set(attributes, SemanticAttributes.NET_PEER_IP, peerIp(request, null));
set(attributes, SemanticAttributes.NET_PEER_NAME, peerName(request, null));
set(attributes, SemanticAttributes.NET_PEER_PORT, peerPort(request, null));
}
@Override
protected final void onEnd(AttributesBuilder attributes, REQUEST request, RESPONSE response) {
set(attributes, SemanticAttributes.NET_PEER_IP, peerIp(request, response));
// TODO(anuraaga): Clients don't have peer information available during the request usually.
// By only resolving them after the response, we can simplify the code a lot but sacrifice
// having them available during sampling on the server side. Revisit if that seems important.
set(attributes, SemanticAttributes.NET_PEER_NAME, peerName(request, response));
set(attributes, SemanticAttributes.NET_PEER_PORT, peerPort(request, response));
}
@ -37,12 +37,27 @@ public abstract class NetAttributesExtractor<REQUEST, RESPONSE>
@Nullable
protected abstract String transport(REQUEST request);
/**
* This method will be called twice: both when the request starts ({@code response} is always null
* then) and when the response ends. This way it is possible to capture net attributes in both
* phases of processing.
*/
@Nullable
protected abstract String peerName(REQUEST request, RESPONSE response);
protected abstract String peerName(REQUEST request, @Nullable RESPONSE response);
/**
* This method will be called twice: both when the request starts ({@code response} is always null
* then) and when the response ends. This way it is possible to capture net attributes in both
* phases of processing.
*/
@Nullable
protected abstract Long peerPort(REQUEST request, RESPONSE response);
protected abstract Long peerPort(REQUEST request, @Nullable RESPONSE response);
/**
* This method will be called twice: both when the request starts ({@code response} is always null
* then) and when the response ends. This way it is possible to capture net attributes in both
* phases of processing.
*/
@Nullable
protected abstract String peerIp(REQUEST request, RESPONSE response);
protected abstract String peerIp(REQUEST request, @Nullable RESPONSE response);
}

View File

@ -20,32 +20,32 @@ class DbAttributesExtractorTest {
DbAttributesExtractor<Map<String, String>> underTest =
new DbAttributesExtractor<Map<String, String>>() {
@Override
protected String dbSystem(Map<String, String> map) {
protected String system(Map<String, String> map) {
return map.get("db.system");
}
@Override
protected String dbUser(Map<String, String> map) {
protected String user(Map<String, String> map) {
return map.get("db.user");
}
@Override
protected String dbName(Map<String, String> map) {
protected String name(Map<String, String> map) {
return map.get("db.name");
}
@Override
protected String dbConnectionString(Map<String, String> map) {
protected String connectionString(Map<String, String> map) {
return map.get("db.connection_string");
}
@Override
protected String dbStatement(Map<String, String> map) {
protected String statement(Map<String, String> map) {
return map.get("db.statement");
}
@Override
protected String dbOperation(Map<String, String> map) {
protected String operation(Map<String, String> map) {
return map.get("db.operation");
}
};

View File

@ -25,8 +25,8 @@ class DbSpanNameExtractorTest {
DbRequest dbRequest = new DbRequest();
// cannot stub dbOperation() and dbTable() because they're final
given(sqlAttributesExtractor.rawDbStatement(dbRequest)).willReturn("SELECT * FROM table");
given(sqlAttributesExtractor.dbName(dbRequest)).willReturn("database");
given(sqlAttributesExtractor.rawStatement(dbRequest)).willReturn("SELECT * FROM table");
given(sqlAttributesExtractor.name(dbRequest)).willReturn("database");
SpanNameExtractor<DbRequest> underTest = DbSpanNameExtractor.create(sqlAttributesExtractor);
@ -43,7 +43,7 @@ class DbSpanNameExtractorTest {
DbRequest dbRequest = new DbRequest();
// cannot stub dbOperation() and dbTable() because they're final
given(sqlAttributesExtractor.rawDbStatement(dbRequest)).willReturn("SELECT * FROM table");
given(sqlAttributesExtractor.rawStatement(dbRequest)).willReturn("SELECT * FROM table");
SpanNameExtractor<DbRequest> underTest = DbSpanNameExtractor.create(sqlAttributesExtractor);
@ -59,8 +59,8 @@ class DbSpanNameExtractorTest {
// given
DbRequest dbRequest = new DbRequest();
given(dbAttributesExtractor.dbOperation(dbRequest)).willReturn("SELECT");
given(dbAttributesExtractor.dbName(dbRequest)).willReturn("database");
given(dbAttributesExtractor.operation(dbRequest)).willReturn("SELECT");
given(dbAttributesExtractor.name(dbRequest)).willReturn("database");
SpanNameExtractor<DbRequest> underTest = DbSpanNameExtractor.create(dbAttributesExtractor);
@ -76,7 +76,7 @@ class DbSpanNameExtractorTest {
// given
DbRequest dbRequest = new DbRequest();
given(dbAttributesExtractor.dbOperation(dbRequest)).willReturn("SELECT");
given(dbAttributesExtractor.operation(dbRequest)).willReturn("SELECT");
SpanNameExtractor<DbRequest> underTest = DbSpanNameExtractor.create(dbAttributesExtractor);
@ -92,7 +92,7 @@ class DbSpanNameExtractorTest {
// given
DbRequest dbRequest = new DbRequest();
given(dbAttributesExtractor.dbName(dbRequest)).willReturn("database");
given(dbAttributesExtractor.name(dbRequest)).willReturn("database");
SpanNameExtractor<DbRequest> underTest = DbSpanNameExtractor.create(dbAttributesExtractor);

View File

@ -28,27 +28,27 @@ class SqlAttributesExtractorTest {
}
@Override
protected String rawDbStatement(Map<String, String> map) {
protected String rawStatement(Map<String, String> map) {
return map.get("db.statement");
}
@Override
protected String dbSystem(Map<String, String> map) {
protected String system(Map<String, String> map) {
return map.get("db.system");
}
@Override
protected String dbUser(Map<String, String> map) {
protected String user(Map<String, String> map) {
return map.get("db.user");
}
@Override
protected String dbName(Map<String, String> map) {
protected String name(Map<String, String> map) {
return map.get("db.name");
}
@Override
protected String dbConnectionString(Map<String, String> map) {
protected String connectionString(Map<String, String> map) {
return map.get("db.connection_string");
}
};

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyMap;
@ -28,20 +28,20 @@ class HttpSpanNameExtractorTest {
void routeAndMethod() {
when(extractor.route(anyMap())).thenReturn("/cats/{id}");
when(extractor.method(anyMap())).thenReturn("GET");
assertThat(SpanNameExtractor.http(extractor).extract(Collections.emptyMap()))
assertThat(HttpSpanNameExtractor.create(extractor).extract(Collections.emptyMap()))
.isEqualTo("/cats/{id}");
}
@Test
void method() {
when(extractor.method(anyMap())).thenReturn("GET");
assertThat(SpanNameExtractor.http(extractor).extract(Collections.emptyMap()))
assertThat(HttpSpanNameExtractor.create(extractor).extract(Collections.emptyMap()))
.isEqualTo("HTTP GET");
}
@Test
void nothing() {
assertThat(SpanNameExtractor.http(extractor).extract(Collections.emptyMap()))
assertThat(HttpSpanNameExtractor.create(extractor).extract(Collections.emptyMap()))
.isEqualTo("HTTP request");
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyMap;
@ -30,7 +30,7 @@ class HttpSpanStatusExtractorTest {
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(statusCode);
assertThat(
SpanStatusExtractor.http(extractor)
HttpSpanStatusExtractor.create(extractor)
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
.isEqualTo(HttpStatusConverter.statusFromHttpStatus((int) statusCode));
}
@ -42,7 +42,7 @@ class HttpSpanStatusExtractorTest {
// Presence of exception has no effect.
assertThat(
SpanStatusExtractor.http(extractor)
HttpSpanStatusExtractor.create(extractor)
.extract(
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
.isEqualTo(HttpStatusConverter.statusFromHttpStatus((int) statusCode));
@ -53,7 +53,7 @@ class HttpSpanStatusExtractorTest {
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
assertThat(
SpanStatusExtractor.http(extractor)
HttpSpanStatusExtractor.create(extractor)
.extract(Collections.emptyMap(), Collections.emptyMap(), null))
.isEqualTo(StatusCode.UNSET);
}
@ -63,7 +63,7 @@ class HttpSpanStatusExtractorTest {
when(extractor.statusCode(anyMap(), anyMap())).thenReturn(null);
assertThat(
SpanStatusExtractor.http(extractor)
HttpSpanStatusExtractor.create(extractor)
.extract(
Collections.emptyMap(), Collections.emptyMap(), new IllegalStateException()))
.isEqualTo(StatusCode.ERROR);

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.net;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
@ -19,18 +19,20 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class InetSocketAddressNetAttributesExtractorTest {
private final InetSocketAddressNetAttributesExtractor<InetSocketAddress, Void> extractor =
new InetSocketAddressNetAttributesExtractor<InetSocketAddress, Void>() {
@Override
protected InetSocketAddress getAddress(InetSocketAddress inetSocketAddress, Void unused) {
return inetSocketAddress;
}
private final InetSocketAddressNetAttributesExtractor<InetSocketAddress, InetSocketAddress>
extractor =
new InetSocketAddressNetAttributesExtractor<InetSocketAddress, InetSocketAddress>() {
@Override
protected InetSocketAddress getAddress(
InetSocketAddress request, InetSocketAddress response) {
return response != null ? response : request;
}
@Override
protected String transport(InetSocketAddress inetSocketAddress) {
return SemanticAttributes.NetTransportValues.IP_TCP.getValue();
}
};
@Override
protected String transport(InetSocketAddress inetSocketAddress) {
return SemanticAttributes.NetTransportValues.IP_TCP.getValue();
}
};
@Test
void noInetSocketAddress() {
@ -46,19 +48,19 @@ class InetSocketAddressNetAttributesExtractorTest {
@Test
void fullAddress() {
// given
InetSocketAddress address = new InetSocketAddress("github.com", 123);
assertThat(address.getAddress().getHostAddress()).isNotNull();
AttributesBuilder attributes = Attributes.builder();
extractor.onStart(attributes, address);
assertThat(attributes.build())
.containsOnly(
entry(
SemanticAttributes.NET_TRANSPORT,
SemanticAttributes.NetTransportValues.IP_TCP.getValue()));
// when
AttributesBuilder startAttributes = Attributes.builder();
extractor.onStart(startAttributes, address);
extractor.onEnd(attributes, address, null);
assertThat(attributes.build())
AttributesBuilder endAttributes = Attributes.builder();
extractor.onEnd(endAttributes, null, address);
// then
assertThat(startAttributes.build())
.containsOnly(
entry(
SemanticAttributes.NET_TRANSPORT,
@ -66,28 +68,39 @@ class InetSocketAddressNetAttributesExtractorTest {
entry(SemanticAttributes.NET_PEER_IP, address.getAddress().getHostAddress()),
entry(SemanticAttributes.NET_PEER_NAME, "github.com"),
entry(SemanticAttributes.NET_PEER_PORT, 123L));
assertThat(endAttributes.build())
.containsOnly(
entry(SemanticAttributes.NET_PEER_IP, address.getAddress().getHostAddress()),
entry(SemanticAttributes.NET_PEER_NAME, "github.com"),
entry(SemanticAttributes.NET_PEER_PORT, 123L));
}
@Test
void unresolved() {
// given
InetSocketAddress address = InetSocketAddress.createUnresolved("github.com", 123);
assertThat(address.getAddress()).isNull();
AttributesBuilder attributes = Attributes.builder();
extractor.onStart(attributes, address);
assertThat(attributes.build())
.containsOnly(
entry(
SemanticAttributes.NET_TRANSPORT,
SemanticAttributes.NetTransportValues.IP_TCP.getValue()));
// when
AttributesBuilder startAttributes = Attributes.builder();
extractor.onStart(startAttributes, address);
extractor.onEnd(attributes, address, null);
assertThat(attributes.build())
AttributesBuilder endAttributes = Attributes.builder();
extractor.onEnd(endAttributes, null, address);
// then
assertThat(startAttributes.build())
.containsOnly(
entry(
SemanticAttributes.NET_TRANSPORT,
SemanticAttributes.NetTransportValues.IP_TCP.getValue()),
entry(SemanticAttributes.NET_PEER_NAME, "github.com"),
entry(SemanticAttributes.NET_PEER_PORT, 123L));
assertThat(endAttributes.build())
.containsOnly(
entry(SemanticAttributes.NET_PEER_NAME, "github.com"),
entry(SemanticAttributes.NET_PEER_PORT, 123L));
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter;
package io.opentelemetry.instrumentation.api.instrumenter.net;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
@ -11,7 +11,6 @@ import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
@ -28,37 +27,64 @@ class NetAttributesExtractorTest {
@Override
protected String peerName(Map<String, String> request, Map<String, String> response) {
if (response != null) {
return response.get("peerName");
}
return request.get("peerName");
}
@Override
protected Long peerPort(Map<String, String> request, Map<String, String> response) {
return Long.parseLong(request.get("peerPort"));
if (response != null) {
return Long.valueOf(response.get("peerPort"));
}
return Long.valueOf(request.get("peerPort"));
}
@Override
protected String peerIp(Map<String, String> request, Map<String, String> response) {
if (response != null) {
return response.get("peerIp");
}
return request.get("peerIp");
}
}
@Test
void normal() {
// given
Map<String, String> request = new HashMap<>();
request.put("transport", "TCP");
request.put("peerName", "github.com");
request.put("peerPort", "123");
request.put("peerIp", "1.2.3.4");
AttributesBuilder attributes = Attributes.builder();
Map<String, String> response = new HashMap<>();
response.put("peerName", "opentelemetry.io");
response.put("peerPort", "42");
response.put("peerIp", "4.3.2.1");
TestNetAttributesExtractor extractor = new TestNetAttributesExtractor();
extractor.onStart(attributes, request);
assertThat(attributes.build()).containsOnly(entry(SemanticAttributes.NET_TRANSPORT, "TCP"));
extractor.onEnd(attributes, request, Collections.emptyMap());
assertThat(attributes.build())
// when
AttributesBuilder startAttributes = Attributes.builder();
extractor.onStart(startAttributes, request);
AttributesBuilder endAttributes = Attributes.builder();
extractor.onEnd(endAttributes, request, response);
// then
assertThat(startAttributes.build())
.containsOnly(
entry(SemanticAttributes.NET_TRANSPORT, "TCP"),
entry(SemanticAttributes.NET_PEER_NAME, "github.com"),
entry(SemanticAttributes.NET_PEER_PORT, 123L),
entry(SemanticAttributes.NET_PEER_IP, "1.2.3.4"));
assertThat(endAttributes.build())
.containsOnly(
entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io"),
entry(SemanticAttributes.NET_PEER_PORT, 42L),
entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1"));
}
}

View File

@ -12,7 +12,7 @@ import com.linecorp.armeria.common.RequestContext;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.logging.RequestLog;
import com.linecorp.armeria.server.ServiceRequestContext;
import io.opentelemetry.instrumentation.api.instrumenter.HttpAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import org.checkerframework.checker.nullness.qual.Nullable;

View File

@ -7,7 +7,7 @@ package io.opentelemetry.instrumentation.armeria.v1_3;
import com.linecorp.armeria.common.RequestContext;
import com.linecorp.armeria.common.logging.RequestLog;
import io.opentelemetry.instrumentation.api.instrumenter.InetSocketAddressNetAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetAttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@ -23,7 +23,8 @@ final class ArmeriaNetAttributesExtractor
@Override
@Nullable
protected InetSocketAddress getAddress(RequestContext requestContext, RequestLog requestLog) {
protected InetSocketAddress getAddress(
RequestContext requestContext, @Nullable RequestLog requestLog) {
SocketAddress address = requestContext.remoteAddress();
if (address instanceof InetSocketAddress) {
return (InetSocketAddress) address;

View File

@ -15,6 +15,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@ -62,9 +64,9 @@ public final class ArmeriaTracingBuilder {
ArmeriaNetAttributesExtractor netAttributesExtractor = new ArmeriaNetAttributesExtractor();
SpanNameExtractor<? super RequestContext> spanNameExtractor =
SpanNameExtractor.http(httpAttributesExtractor);
HttpSpanNameExtractor.create(httpAttributesExtractor);
SpanStatusExtractor<? super RequestContext, ? super RequestLog> spanStatusExtractor =
statusExtractorTransformer.apply(SpanStatusExtractor.http(httpAttributesExtractor));
statusExtractorTransformer.apply(HttpSpanStatusExtractor.create(httpAttributesExtractor));
InstrumenterBuilder<ClientRequestContext, RequestLog> clientInstrumenterBuilder =
Instrumenter.newBuilder(openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor);

View File

@ -13,26 +13,26 @@ import org.checkerframework.checker.nullness.qual.Nullable;
final class JdbcAttributesExtractor extends SqlAttributesExtractor<DbRequest> {
@Nullable
@Override
protected String dbSystem(DbRequest dbRequest) {
protected String system(DbRequest dbRequest) {
return dbRequest.getDbInfo().getSystem();
}
@Nullable
@Override
protected String dbUser(DbRequest dbRequest) {
protected String user(DbRequest dbRequest) {
return dbRequest.getDbInfo().getUser();
}
@Nullable
@Override
protected String dbName(DbRequest dbRequest) {
protected String name(DbRequest dbRequest) {
DbInfo dbInfo = dbRequest.getDbInfo();
return dbInfo.getName() == null ? dbInfo.getDb() : dbInfo.getName();
}
@Nullable
@Override
protected String dbConnectionString(DbRequest dbRequest) {
protected String connectionString(DbRequest dbRequest) {
return dbRequest.getDbInfo().getShortUrl();
}
@ -43,7 +43,7 @@ final class JdbcAttributesExtractor extends SqlAttributesExtractor<DbRequest> {
@Nullable
@Override
protected String rawDbStatement(DbRequest dbRequest) {
protected String rawStatement(DbRequest dbRequest) {
return dbRequest.getStatement();
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.jdbc;
import io.opentelemetry.instrumentation.api.instrumenter.NetAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
import org.checkerframework.checker.nullness.qual.Nullable;
final class JdbcNetAttributesExtractor extends NetAttributesExtractor<DbRequest, Void> {
@ -18,20 +18,20 @@ final class JdbcNetAttributesExtractor extends NetAttributesExtractor<DbRequest,
@Nullable
@Override
protected String peerName(DbRequest request, Void unused) {
protected String peerName(DbRequest request, @Nullable Void response) {
return request.getDbInfo().getHost();
}
@Nullable
@Override
protected Long peerPort(DbRequest request, Void unused) {
protected Long peerPort(DbRequest request, @Nullable Void response) {
Integer port = request.getDbInfo().getPort();
return port == null ? null : port.longValue();
}
@Nullable
@Override
protected String peerIp(DbRequest request, Void unused) {
protected String peerIp(DbRequest request, @Nullable Void response) {
return null;
}
}