Review semantic convention for Http Client spans (#1214)
* Review semantic convention for Http Client spans * Polish * Update instrumentation/google-http-client-1.19/src/test/groovy/AbstractGoogleHttpClientTest.groovy Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
e8b54885c4
commit
beb1901f5f
|
@ -34,3 +34,32 @@ attribute. As either it or `http.url` is required, we set the latter. This, in t
|
|||
|
||||
**[3]:** In case of Armeria, return values are [SessionProtocol](https://github.com/line/armeria/blob/master/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java),
|
||||
not values defined by spec.
|
||||
|
||||
## Http Client
|
||||
|
||||
| Attribute | Required | Implemented? |
|
||||
|---|:---:|:---:|
|
||||
| `http.method` | Y | + |
|
||||
| `http.url` | N | + |
|
||||
| `http.target` | N | - [1] |
|
||||
| `http.host` | N | - [1] |
|
||||
| `http.scheme` | N | - [1] |
|
||||
| `http.status_code` | Y | + |
|
||||
| `http.status_text` | N | - [2] |
|
||||
| `http.flavor` | N | + [3] |
|
||||
| `http.user_agent` | N | + |
|
||||
| `http.request_content_length` | N | - |
|
||||
| `http.request_content_length_uncompressed` | N | - |
|
||||
| `http.response_content_length` | N | - |
|
||||
| `http.response_content_length_uncompressed` | N | - |
|
||||
|
||||
**[1]:** As the majority of Java frameworks don't provide a standard way to obtain "The full request
|
||||
target as passed in a HTTP request line or equivalent.", we don't set `http.target` semantic
|
||||
attribute. As either it or `http.url` is required, we set the latter. This, in turn, makes setting
|
||||
`http.schema` and `http.host` unnecessary duplication. Therefore, we do not set them as well.
|
||||
|
||||
**[2]: TODO** After [this PR](https://github.com/open-telemetry/opentelemetry-specification/issues/950)
|
||||
is merged, remove this line. If it rejected, then implement this attribute.
|
||||
|
||||
**[3]:** In case of Armeria, return values are [SessionProtocol](https://github.com/line/armeria/blob/master/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java),
|
||||
not values defined by spec.
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.SortedSet;
|
||||
|
@ -76,8 +75,6 @@ public class Config {
|
|||
public static final String TRACE_CLASSES_EXCLUDE = "trace.classes.exclude";
|
||||
public static final String HTTP_SERVER_ERROR_STATUSES = "http.server.error.statuses";
|
||||
public static final String HTTP_CLIENT_ERROR_STATUSES = "http.client.error.statuses";
|
||||
public static final String HTTP_SERVER_TAG_QUERY_STRING = "http.server.tag.query-string";
|
||||
public static final String HTTP_CLIENT_TAG_QUERY_STRING = "http.client.tag.query-string";
|
||||
public static final String SCOPE_DEPTH_LIMIT = "trace.scope.depth.limit";
|
||||
public static final String RUNTIME_CONTEXT_FIELD_INJECTION =
|
||||
"trace.runtime.context.field.injection";
|
||||
|
@ -93,13 +90,8 @@ public class Config {
|
|||
|
||||
private static final boolean DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION = true;
|
||||
|
||||
private static final boolean DEFAULT_HTTP_SERVER_TAG_QUERY_STRING = false;
|
||||
private static final boolean DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING = false;
|
||||
private static final int DEFAULT_SCOPE_DEPTH_LIMIT = 100;
|
||||
|
||||
public static final boolean DEFAULT_LOG_INJECTION_ENABLED = false;
|
||||
public static final String DEFAULT_EXPERIMENTAL_LOG_CAPTURE_THRESHOLD = null;
|
||||
|
||||
public static final boolean DEFAULT_KAFKA_CLIENT_PROPAGATION_ENABLED = true;
|
||||
|
||||
public static final boolean DEFAULT_HYSTRIX_TAGS_ENABLED = false;
|
||||
|
@ -119,8 +111,6 @@ public class Config {
|
|||
private final boolean traceEnabled;
|
||||
private final boolean integrationsEnabled;
|
||||
private final List<String> excludedClasses;
|
||||
private final boolean httpServerTagQueryString;
|
||||
private final boolean httpClientTagQueryString;
|
||||
private final Integer scopeDepthLimit;
|
||||
private final boolean runtimeContextFieldInjection;
|
||||
|
||||
|
@ -157,14 +147,6 @@ public class Config {
|
|||
|
||||
excludedClasses = getListSettingFromEnvironment(TRACE_CLASSES_EXCLUDE, null);
|
||||
|
||||
httpServerTagQueryString =
|
||||
getBooleanSettingFromEnvironment(
|
||||
HTTP_SERVER_TAG_QUERY_STRING, DEFAULT_HTTP_SERVER_TAG_QUERY_STRING);
|
||||
|
||||
httpClientTagQueryString =
|
||||
getBooleanSettingFromEnvironment(
|
||||
HTTP_CLIENT_TAG_QUERY_STRING, DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING);
|
||||
|
||||
scopeDepthLimit =
|
||||
getIntegerSettingFromEnvironment(SCOPE_DEPTH_LIMIT, DEFAULT_SCOPE_DEPTH_LIMIT);
|
||||
|
||||
|
@ -213,14 +195,6 @@ public class Config {
|
|||
excludedClasses =
|
||||
getPropertyListValue(properties, TRACE_CLASSES_EXCLUDE, parent.excludedClasses);
|
||||
|
||||
httpServerTagQueryString =
|
||||
getPropertyBooleanValue(
|
||||
properties, HTTP_SERVER_TAG_QUERY_STRING, parent.httpServerTagQueryString);
|
||||
|
||||
httpClientTagQueryString =
|
||||
getPropertyBooleanValue(
|
||||
properties, HTTP_CLIENT_TAG_QUERY_STRING, parent.httpClientTagQueryString);
|
||||
|
||||
scopeDepthLimit =
|
||||
getPropertyIntegerValue(properties, SCOPE_DEPTH_LIMIT, parent.scopeDepthLimit);
|
||||
|
||||
|
@ -492,10 +466,6 @@ public class Config {
|
|||
return properties;
|
||||
}
|
||||
|
||||
private static String toUpper(String str) {
|
||||
return str == null ? null : str.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
// This has to be placed after all other static fields to give them a chance to initialize
|
||||
private static final Config INSTANCE = new Config();
|
||||
|
||||
|
@ -535,14 +505,6 @@ public class Config {
|
|||
return excludedClasses;
|
||||
}
|
||||
|
||||
public boolean isHttpServerTagQueryString() {
|
||||
return httpServerTagQueryString;
|
||||
}
|
||||
|
||||
public boolean isHttpClientTagQueryString() {
|
||||
return httpClientTagQueryString;
|
||||
}
|
||||
|
||||
public Integer getScopeDepthLimit() {
|
||||
return scopeDepthLimit;
|
||||
}
|
||||
|
@ -604,10 +566,6 @@ public class Config {
|
|||
+ integrationsEnabled
|
||||
+ ", excludedClasses="
|
||||
+ excludedClasses
|
||||
+ ", httpServerTagQueryString="
|
||||
+ httpServerTagQueryString
|
||||
+ ", httpClientTagQueryString="
|
||||
+ httpClientTagQueryString
|
||||
+ ", scopeDepthLimit="
|
||||
+ scopeDepthLimit
|
||||
+ ", runtimeContextFieldInjection="
|
||||
|
|
|
@ -24,10 +24,7 @@ import io.opentelemetry.OpenTelemetry;
|
|||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
|
||||
import io.opentelemetry.instrumentation.api.MoreAttributes;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.instrumentation.api.decorator.HttpStatusConverter;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.HttpUrlUtils;
|
||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||
import io.opentelemetry.trace.DefaultSpan;
|
||||
import io.opentelemetry.trace.Span;
|
||||
|
@ -54,6 +51,12 @@ public abstract class HttpClientTracer<REQUEST, CARRIER, RESPONSE> extends BaseT
|
|||
@Nullable
|
||||
protected abstract URI url(REQUEST request) throws URISyntaxException;
|
||||
|
||||
@Nullable
|
||||
protected String flavor(REQUEST request) {
|
||||
// This is de facto standard nowadays, so let us use it, unless overridden
|
||||
return "1.1";
|
||||
}
|
||||
|
||||
protected abstract Integer status(RESPONSE response);
|
||||
|
||||
@Nullable
|
||||
|
@ -138,34 +141,42 @@ public abstract class HttpClientTracer<REQUEST, CARRIER, RESPONSE> extends BaseT
|
|||
protected Span onRequest(Span span, REQUEST request) {
|
||||
assert span != null;
|
||||
if (request != null) {
|
||||
span.setAttribute(SemanticAttributes.HTTP_METHOD.key(), method(request));
|
||||
span.setAttribute(SemanticAttributes.NET_TRANSPORT.key(), "IP.TCP");
|
||||
SemanticAttributes.NET_TRANSPORT.set(span, "IP.TCP");
|
||||
SemanticAttributes.HTTP_METHOD.set(span, method(request));
|
||||
SemanticAttributes.HTTP_USER_AGENT.set(span, requestHeader(request, USER_AGENT));
|
||||
|
||||
String userAgent = requestHeader(request, USER_AGENT);
|
||||
if (userAgent != null) {
|
||||
SemanticAttributes.HTTP_USER_AGENT.set(span, userAgent);
|
||||
}
|
||||
|
||||
try {
|
||||
URI url = url(request);
|
||||
if (url != null && url.getHost() != null) {
|
||||
NetPeerUtils.setNetPeer(span, url.getHost(), null);
|
||||
if (url.getPort() > 0) {
|
||||
span.setAttribute(SemanticAttributes.NET_PEER_PORT.key(), url.getPort());
|
||||
}
|
||||
}
|
||||
HttpUrlUtils.setHttpUrl(span, url);
|
||||
if (Config.get().isHttpClientTagQueryString()) {
|
||||
span.setAttribute(MoreAttributes.HTTP_QUERY, url.getQuery());
|
||||
span.setAttribute(MoreAttributes.HTTP_FRAGMENT, url.getFragment());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Error tagging url", e);
|
||||
}
|
||||
setFlavor(span, request);
|
||||
setUrl(span, request);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
private void setFlavor(Span span, REQUEST request) {
|
||||
String flavor = flavor(request);
|
||||
if (flavor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String httpProtocolPrefix = "HTTP/";
|
||||
if (flavor.startsWith(httpProtocolPrefix)) {
|
||||
flavor = flavor.substring(httpProtocolPrefix.length());
|
||||
}
|
||||
|
||||
SemanticAttributes.HTTP_FLAVOR.set(span, flavor);
|
||||
}
|
||||
|
||||
private void setUrl(Span span, REQUEST request) {
|
||||
try {
|
||||
URI url = url(request);
|
||||
if (url != null) {
|
||||
NetPeerUtils.setNetPeer(span, url.getHost(), null, url.getPort());
|
||||
SemanticAttributes.HTTP_URL.set(span, url.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Error tagging url", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Span onResponse(Span span, RESPONSE response) {
|
||||
assert span != null;
|
||||
if (response != null) {
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
package io.opentelemetry.instrumentation.api.tracer
|
||||
|
||||
import static io.opentelemetry.auto.test.utils.ConfigUtils.withConfigOverride
|
||||
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator
|
||||
import io.opentelemetry.instrumentation.api.decorator.HttpStatusConverter
|
||||
import io.opentelemetry.trace.Span
|
||||
import io.opentelemetry.trace.attributes.SemanticAttributes
|
||||
import spock.lang.Shared
|
||||
|
||||
|
||||
class HttpClientTracerTest extends BaseTracerTest {
|
||||
|
||||
@Shared
|
||||
|
@ -47,6 +47,7 @@ class HttpClientTracerTest extends BaseTracerTest {
|
|||
1 * span.setAttribute(SemanticAttributes.NET_PEER_NAME.key(), req.url.host)
|
||||
1 * span.setAttribute(SemanticAttributes.NET_PEER_PORT.key(), req.url.port)
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_USER_AGENT.key(), req["User-Agent"])
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_FLAVOR.key(), "1.1")
|
||||
}
|
||||
0 * _
|
||||
|
||||
|
@ -78,6 +79,7 @@ class HttpClientTracerTest extends BaseTracerTest {
|
|||
1 * span.setAttribute(SemanticAttributes.NET_PEER_PORT.key(), req.url.port)
|
||||
1 * span.setAttribute("peer.service", "reservation-service")
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_USER_AGENT.key(), req["User-Agent"])
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_FLAVOR.key(), "1.1")
|
||||
}
|
||||
0 * _
|
||||
}
|
||||
|
@ -87,20 +89,16 @@ class HttpClientTracerTest extends BaseTracerTest {
|
|||
def tracer = newTracer()
|
||||
|
||||
when:
|
||||
withConfigOverride(io.opentelemetry.instrumentation.api.config.Config.HTTP_CLIENT_TAG_QUERY_STRING, "$tagQueryString") {
|
||||
tracer.onRequest(span, req)
|
||||
}
|
||||
tracer.onRequest(span, req)
|
||||
|
||||
then:
|
||||
1 * span.setAttribute(SemanticAttributes.NET_TRANSPORT.key(), "IP.TCP")
|
||||
if (expectedUrl) {
|
||||
if (expectedUrl != null) {
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_URL.key(), expectedUrl)
|
||||
}
|
||||
if (expectedUrl && tagQueryString) {
|
||||
1 * span.setAttribute(io.opentelemetry.instrumentation.api.MoreAttributes.HTTP_QUERY, expectedQuery)
|
||||
1 * span.setAttribute(io.opentelemetry.instrumentation.api.MoreAttributes.HTTP_FRAGMENT, expectedFragment)
|
||||
}
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_METHOD.key(), null)
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_FLAVOR.key(), "1.1")
|
||||
1 * span.setAttribute(SemanticAttributes.HTTP_USER_AGENT.key(), null)
|
||||
if (hostname) {
|
||||
1 * span.setAttribute(SemanticAttributes.NET_PEER_NAME.key(), hostname)
|
||||
}
|
||||
|
@ -110,19 +108,13 @@ class HttpClientTracerTest extends BaseTracerTest {
|
|||
0 * _
|
||||
|
||||
where:
|
||||
tagQueryString | url | expectedUrl | expectedQuery | expectedFragment | hostname | port
|
||||
false | null | null | null | null | null | null
|
||||
false | "" | "/" | "" | null | null | null
|
||||
false | "/path?query" | "/path?query" | "" | null | null | null
|
||||
false | "https://host:0" | "https://host/" | "" | null | "host" | null
|
||||
false | "https://host/path" | "https://host/path" | "" | null | "host" | null
|
||||
false | "http://host:99/path?query#fragment" | "http://host:99/path?query#fragment" | "" | null | "host" | 99
|
||||
true | null | null | null | null | null | null
|
||||
true | "" | "/" | null | null | null | null
|
||||
true | "/path?encoded+%28query%29%3F" | "/path?encoded+(query)?" | "encoded+(query)?" | null | null | null
|
||||
true | "https://host:0" | "https://host/" | null | null | "host" | null
|
||||
true | "https://host/path" | "https://host/path" | null | null | "host" | null
|
||||
true | "http://host:99/path?query#encoded+%28fragment%29%3F" | "http://host:99/path?query#encoded+(fragment)?" | "query" | "encoded+(fragment)?" | "host" | 99
|
||||
tagQueryString | url | expectedUrl | expectedQuery | expectedFragment | hostname | port
|
||||
false | null | null | null | null | null | null
|
||||
false | "" | "" | "" | null | null | null
|
||||
false | "/path?query" | "/path?query" | "" | null | null | null
|
||||
false | "https://host:0" | "https://host:0" | "" | null | "host" | null
|
||||
false | "https://host/path" | "https://host/path" | "" | null | "host" | null
|
||||
false | "http://host:99/path?query#fragment" | "http://host:99/path?query#fragment" | "" | null | "host" | 99
|
||||
|
||||
req = [url: url == null ? null : new URI(url)]
|
||||
}
|
||||
|
|
|
@ -48,6 +48,11 @@ public class AkkaHttpClientTracer
|
|||
return new URI(httpRequest.uri().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String flavor(HttpRequest httpRequest) {
|
||||
return httpRequest.protocol().value();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer status(HttpResponse httpResponse) {
|
||||
return httpResponse.status().intValue();
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.http.HttpResponse;
|
|||
import org.apache.http.RequestLine;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class ApacheHttpAsyncClientTracer
|
||||
extends HttpClientTracer<HttpRequest, HttpRequest, HttpResponse> {
|
||||
|
@ -46,6 +47,11 @@ public class ApacheHttpAsyncClientTracer
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(HttpRequest httpRequest) {
|
||||
return httpRequest.getProtocolVersion().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpRequest request) throws URISyntaxException {
|
||||
/*
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.http.Header;
|
|||
import org.apache.http.HttpMessage;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class ApacheHttpClientTracer
|
||||
extends HttpClientTracer<HttpUriRequest, HttpUriRequest, HttpResponse> {
|
||||
|
@ -37,6 +38,11 @@ class ApacheHttpClientTracer
|
|||
return httpRequest.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(HttpUriRequest httpUriRequest) {
|
||||
return httpUriRequest.getProtocolVersion().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpUriRequest request) {
|
||||
return request.getURI();
|
||||
|
|
|
@ -40,6 +40,11 @@ public class ArmeriaClientTracer
|
|||
return ctx.method().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(ClientRequestContext clientRequestContext) {
|
||||
return clientRequestContext.sessionProtocol().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected URI url(ClientRequestContext ctx) throws URISyntaxException {
|
||||
|
|
|
@ -77,6 +77,7 @@ abstract class AbstractArmeriaTest extends InstrumentationSpecification {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "${server.httpUri()}${path}"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method.name()
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" code
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "http"
|
||||
}
|
||||
}
|
||||
span(1) {
|
||||
|
|
|
@ -153,9 +153,10 @@ class AWS1ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"aws.service" { it.contains(service) }
|
||||
|
@ -229,8 +230,9 @@ class AWS1ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "http://localhost:${UNUSABLE_PORT}/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "http://localhost:${UNUSABLE_PORT}"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" 61
|
||||
"aws.service" { it.contains(service) }
|
||||
|
@ -276,8 +278,9 @@ class AWS1ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "https://s3.amazonaws.com/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "https://s3.amazonaws.com"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "HEAD"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "s3.amazonaws.com"
|
||||
"aws.service" "Amazon S3"
|
||||
"aws.endpoint" "https://s3.amazonaws.com"
|
||||
|
@ -324,10 +327,11 @@ class AWS1ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"aws.service" "Amazon S3"
|
||||
"aws.endpoint" "$server.address"
|
||||
"aws.operation" "GetObjectRequest"
|
||||
|
|
|
@ -116,9 +116,10 @@ class AWS0ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"aws.service" { it.contains(service) }
|
||||
|
@ -174,8 +175,9 @@ class AWS0ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "http://localhost:${UNUSABLE_PORT}/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "http://localhost:${UNUSABLE_PORT}"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" 61
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"aws.service" { it.contains(service) }
|
||||
|
@ -221,8 +223,9 @@ class AWS0ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "https://s3.amazonaws.com/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "https://s3.amazonaws.com"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "s3.amazonaws.com"
|
||||
"aws.service" "Amazon S3"
|
||||
"aws.endpoint" "https://s3.amazonaws.com"
|
||||
|
@ -266,8 +269,9 @@ class AWS0ClientTest extends AgentTestRunner {
|
|||
parent()
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"aws.service" "Amazon S3"
|
||||
|
|
|
@ -146,6 +146,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_USER_AGENT.key()}" { it.startsWith("aws-sdk-java/") }
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"aws.service" "$service"
|
||||
"aws.operation" "${operation}"
|
||||
"aws.agent" "java-aws-sdk"
|
||||
|
@ -216,6 +217,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.HTTP_URL.key()}" { it.startsWith("${server.address}${path}") }
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_USER_AGENT.key()}" { it.startsWith("aws-sdk-java/") }
|
||||
"aws.service" "$service"
|
||||
|
@ -241,14 +243,14 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
service | operation | method | path | requestId | builder | call | body
|
||||
"S3" | "CreateBucket" | "PUT" | "/somebucket" | "UNKNOWN" | S3Client.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | ""
|
||||
"S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3Client.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) } | ""
|
||||
"Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | ""
|
||||
"Sqs" | "CreateQueue" | "POST" | "/" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
|
||||
"Kinesis" | "DeleteStream" | "POST" | "" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | ""
|
||||
"Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
|
||||
<CreateQueueResponse>
|
||||
<CreateQueueResult><QueueUrl>https://queue.amazonaws.com/123456789012/MyQueue</QueueUrl></CreateQueueResult>
|
||||
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
|
||||
</CreateQueueResponse>
|
||||
"""
|
||||
"Sqs" | "SendMessage" | "POST" | "/" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
|
||||
"Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
|
||||
<SendMessageResponse>
|
||||
<SendMessageResult>
|
||||
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
|
||||
|
@ -258,14 +260,14 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
|
||||
</SendMessageResponse>
|
||||
"""
|
||||
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2Client.builder() | { c -> c.allocateAddress() } | """
|
||||
"Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2Client.builder() | { c -> c.allocateAddress() } | """
|
||||
<AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||
<publicIp>192.0.2.1</publicIp>
|
||||
<domain>standard</domain>
|
||||
</AllocateAddressResponse>
|
||||
"""
|
||||
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
|
||||
"Rds" | "DeleteOptionGroup" | "POST" | "" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
|
||||
<DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata><RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId></ResponseMetadata>
|
||||
</DeleteOptionGroupResponse>
|
||||
|
@ -303,6 +305,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.HTTP_URL.key()}" { it.startsWith("${server.address}${path}") }
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "$method"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_USER_AGENT.key()}" { it.startsWith("aws-sdk-java/") }
|
||||
"aws.service" "$service"
|
||||
|
@ -330,13 +333,13 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
"S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890"
|
||||
// Kinesis seems to expect an http2 response which is incompatible with our test server.
|
||||
// "Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisAsyncClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | ""
|
||||
"Sqs" | "CreateQueue" | "POST" | "/" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
|
||||
"Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
|
||||
<CreateQueueResponse>
|
||||
<CreateQueueResult><QueueUrl>https://queue.amazonaws.com/123456789012/MyQueue</QueueUrl></CreateQueueResult>
|
||||
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
|
||||
</CreateQueueResponse>
|
||||
"""
|
||||
"Sqs" | "SendMessage" | "POST" | "/" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
|
||||
"Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
|
||||
<SendMessageResponse>
|
||||
<SendMessageResult>
|
||||
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
|
||||
|
@ -346,14 +349,14 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
|
||||
</SendMessageResponse>
|
||||
"""
|
||||
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """
|
||||
"Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """
|
||||
<AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||
<publicIp>192.0.2.1</publicIp>
|
||||
<domain>standard</domain>
|
||||
</AllocateAddressResponse>
|
||||
"""
|
||||
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
|
||||
"Rds" | "DeleteOptionGroup" | "POST" | "" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
|
||||
<DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata><RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId></ResponseMetadata>
|
||||
</DeleteOptionGroupResponse>
|
||||
|
@ -402,6 +405,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
|
|||
"${SemanticAttributes.NET_PEER_PORT.key()}" server.address.port
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$server.address/somebucket/somekey"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"aws.service" "S3"
|
||||
"aws.operation" "GetObject"
|
||||
"aws.agent" "java-aws-sdk"
|
||||
|
|
|
@ -119,6 +119,7 @@ class Elasticsearch5RestClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ class Elasticsearch6RestClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ class Elasticsearch6RestClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ abstract class AbstractGoogleHttpClientTest extends HttpClientTest {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "${uri}"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 500
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
server.distributedRequestSpan(it, 1, span(0))
|
||||
|
|
|
@ -56,8 +56,9 @@ class UrlConnectionTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" UNUSABLE_PORT
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$url/"
|
||||
"${SemanticAttributes.HTTP_URL.key()}" "$url"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,11 @@ import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
|
|||
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
|
||||
import io.opentelemetry.instrumentation.auto.api.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.instrumentation.auto.api.CallDepthThreadLocalMap.Depth;
|
||||
import io.opentelemetry.trace.Span;
|
||||
import io.opentelemetry.trace.attributes.SemanticAttributes;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Version;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
@ -33,7 +36,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class JdkHttpClientTracer extends HttpClientTracer<HttpRequest, HttpRequest, HttpResponse> {
|
||||
public class JdkHttpClientTracer
|
||||
extends HttpClientTracer<HttpRequest, HttpRequest, HttpResponse<?>> {
|
||||
public static final JdkHttpClientTracer TRACER = new JdkHttpClientTracer();
|
||||
|
||||
public Depth getCallDepth() {
|
||||
|
@ -56,7 +60,7 @@ public class JdkHttpClientTracer extends HttpClientTracer<HttpRequest, HttpReque
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Integer status(HttpResponse httpResponse) {
|
||||
protected Integer status(HttpResponse<?> httpResponse) {
|
||||
return httpResponse.statusCode();
|
||||
}
|
||||
|
||||
|
@ -66,10 +70,22 @@ public class JdkHttpClientTracer extends HttpClientTracer<HttpRequest, HttpReque
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String responseHeader(HttpResponse httpResponse, String name) {
|
||||
protected String responseHeader(HttpResponse<?> httpResponse, String name) {
|
||||
return httpResponse.headers().firstValue(name).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span onResponse(Span span, HttpResponse<?> httpResponse) {
|
||||
span = super.onResponse(span, httpResponse);
|
||||
|
||||
if (httpResponse != null) {
|
||||
SemanticAttributes.HTTP_FLAVOR.set(
|
||||
span, httpResponse.version() == Version.HTTP_1_1 ? "1.1" : "2.0");
|
||||
}
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Setter<HttpRequest> getSetter() {
|
||||
return HttpHeadersInjectAdapter.SETTER;
|
||||
|
|
|
@ -14,15 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.base.HttpClientTest
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Timeout
|
||||
import static io.opentelemetry.trace.Span.Kind.CLIENT
|
||||
|
||||
import io.opentelemetry.auto.test.base.HttpClientTest
|
||||
import io.opentelemetry.trace.attributes.SemanticAttributes
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.HttpRequest
|
||||
import java.net.http.HttpResponse
|
||||
import java.time.Duration
|
||||
import java.time.temporal.ChronoUnit
|
||||
import spock.lang.Requires
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Timeout
|
||||
|
||||
@Timeout(5)
|
||||
abstract class JdkHttpClientTest extends HttpClientTest {
|
||||
|
@ -52,4 +55,46 @@ abstract class JdkHttpClientTest extends HttpClientTest {
|
|||
boolean testCircularRedirects() {
|
||||
return false
|
||||
}
|
||||
|
||||
//We override this test below because it produces somewhat different attributes
|
||||
@Override
|
||||
boolean testRemoteConnection() {
|
||||
return false
|
||||
}
|
||||
|
||||
@Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") })
|
||||
def "test https request"() {
|
||||
given:
|
||||
def uri = new URI("https://www.google.com/")
|
||||
|
||||
when:
|
||||
def status = doRequest(method, uri)
|
||||
|
||||
then:
|
||||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 1 + extraClientSpans()) {
|
||||
span(0) {
|
||||
parent()
|
||||
operationName expectedOperationName(method)
|
||||
spanKind CLIENT
|
||||
errored false
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" uri.host
|
||||
"${SemanticAttributes.NET_PEER_IP.key()}" { it == null || it == "127.0.0.1" } // Optional
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" uri.port > 0 ? uri.port : { it == null || it == 443 }
|
||||
"${SemanticAttributes.HTTP_URL.key()}" { it == "${uri}" || it == "${removeFragment(uri)}" }
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "2.0"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
method = "HEAD"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ abstract class JaxRsClientTest extends HttpClientTest {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" "${uri}"
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" statusCode
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
serverSpan(it, 1, span(0))
|
||||
|
|
|
@ -28,6 +28,7 @@ import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
|
|||
import io.opentelemetry.trace.Span;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||
|
@ -55,6 +56,11 @@ public class NettyHttpClientTracer
|
|||
return httpRequest.getMethod().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(HttpRequest httpRequest) {
|
||||
return httpRequest.getProtocolVersion().getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpRequest request) throws URISyntaxException {
|
||||
URI uri = new URI(request.getUri());
|
||||
|
|
|
@ -31,6 +31,7 @@ import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
|
|||
import io.opentelemetry.trace.Span;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class NettyHttpClientTracer
|
||||
extends HttpClientTracer<HttpRequest, HttpHeaders, HttpResponse> {
|
||||
|
@ -55,6 +56,11 @@ public class NettyHttpClientTracer
|
|||
return httpRequest.getMethod().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(HttpRequest httpRequest) {
|
||||
return httpRequest.getProtocolVersion().text();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpRequest request) throws URISyntaxException {
|
||||
URI uri = new URI(request.getUri());
|
||||
|
|
|
@ -26,6 +26,7 @@ import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
|
|||
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class NettyHttpClientTracer
|
||||
extends HttpClientTracer<HttpRequest, HttpHeaders, HttpResponse> {
|
||||
|
@ -36,6 +37,11 @@ public class NettyHttpClientTracer
|
|||
return httpRequest.method().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(HttpRequest httpRequest) {
|
||||
return httpRequest.protocolVersion().text();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpRequest request) throws URISyntaxException {
|
||||
URI uri = new URI(request.uri());
|
||||
|
|
|
@ -86,14 +86,12 @@ class ReactorNettyTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" uri.host
|
||||
"${SemanticAttributes.NET_PEER_IP.key()}" { it == null || it == "127.0.0.1" } // Optional
|
||||
// Optional
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" uri.port > 0 ? uri.port : { it == null || it == 443 }
|
||||
"${SemanticAttributes.HTTP_URL.key()}" uri.toString()
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method
|
||||
"${SemanticAttributes.HTTP_USER_AGENT.key()}" { it.startsWith(userAgent) }
|
||||
if (status) {
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" status
|
||||
}
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,6 +266,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(1) {
|
||||
operationName "MessageCreator.create"
|
||||
spanKind CLIENT
|
||||
childOf(span(0))
|
||||
errored false
|
||||
attributes {
|
||||
"twilio.type" "com.twilio.rest.api.v2010.account.Message"
|
||||
|
@ -277,6 +278,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(2) {
|
||||
operationName expectedOperationName("POST")
|
||||
spanKind CLIENT
|
||||
childOf(span(1))
|
||||
errored false
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
|
@ -284,6 +286,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" String
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" String
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" Long
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,6 +362,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(1) {
|
||||
operationName "MessageCreator.create"
|
||||
spanKind CLIENT
|
||||
childOf(span(0))
|
||||
errored false
|
||||
attributes {
|
||||
"twilio.type" "com.twilio.rest.api.v2010.account.Message"
|
||||
|
@ -370,6 +374,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(2) {
|
||||
operationName expectedOperationName("POST")
|
||||
spanKind CLIENT
|
||||
childOf(span(1))
|
||||
errored true
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
|
@ -377,11 +382,13 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" String
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" String
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" Long
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
span(3) {
|
||||
operationName expectedOperationName("POST")
|
||||
spanKind CLIENT
|
||||
childOf(span(1))
|
||||
errored false
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
|
@ -389,6 +396,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" String
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" String
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" Long
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,6 +479,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(1) {
|
||||
operationName "MessageCreator.createAsync"
|
||||
spanKind CLIENT
|
||||
childOf(span(0))
|
||||
errored false
|
||||
attributes {
|
||||
"twilio.type" "com.twilio.rest.api.v2010.account.Message"
|
||||
|
@ -482,6 +491,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(2) {
|
||||
operationName "MessageCreator.create"
|
||||
spanKind CLIENT
|
||||
childOf(span(1))
|
||||
errored false
|
||||
attributes {
|
||||
"twilio.type" "com.twilio.rest.api.v2010.account.Message"
|
||||
|
@ -493,6 +503,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
span(3) {
|
||||
operationName expectedOperationName("POST")
|
||||
spanKind CLIENT
|
||||
childOf(span(2))
|
||||
errored true
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
|
@ -500,11 +511,13 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" String
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" String
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" Long
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
span(4) {
|
||||
operationName expectedOperationName("POST")
|
||||
spanKind CLIENT
|
||||
childOf(span(2))
|
||||
errored false
|
||||
attributes {
|
||||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
|
@ -512,6 +525,7 @@ class TwilioClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.HTTP_URL.key()}" String
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" String
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" Long
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ public class InMemoryExporter implements SpanProcessor {
|
|||
public void onStart(ReadWriteSpan readWriteSpan) {
|
||||
SpanData sd = readWriteSpan.toSpanData();
|
||||
log.debug(
|
||||
">>> SPAN START: {} id={} traceid={} parent={}, library={}",
|
||||
">>>{} SPAN START: {} id={} traceid={} parent={}, library={}",
|
||||
sd.getStartEpochNanos(),
|
||||
sd.getName(),
|
||||
sd.getSpanId(),
|
||||
sd.getTraceId(),
|
||||
|
@ -89,7 +90,8 @@ public class InMemoryExporter implements SpanProcessor {
|
|||
public void onEnd(ReadableSpan readableSpan) {
|
||||
SpanData sd = readableSpan.toSpanData();
|
||||
log.debug(
|
||||
"<<< SPAN END: {} id={} traceid={} parent={}, library={}, attributes={}",
|
||||
"<<<{} SPAN END: {} id={} traceid={} parent={}, library={}, attributes={}",
|
||||
sd.getEndEpochNanos(),
|
||||
sd.getName(),
|
||||
sd.getSpanId(),
|
||||
sd.getTraceId(),
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package io.opentelemetry.auto.test.base
|
||||
|
||||
import static io.opentelemetry.auto.test.server.http.TestHttpServer.httpServer
|
||||
import static io.opentelemetry.auto.test.utils.ConfigUtils.withConfigOverride
|
||||
import static io.opentelemetry.auto.test.utils.PortUtils.UNUSABLE_PORT
|
||||
import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan
|
||||
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
|
||||
|
@ -27,8 +26,6 @@ import static org.junit.Assume.assumeTrue
|
|||
|
||||
import io.opentelemetry.auto.test.AgentTestRunner
|
||||
import io.opentelemetry.auto.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.api.MoreAttributes
|
||||
import io.opentelemetry.instrumentation.api.config.Config
|
||||
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import io.opentelemetry.trace.attributes.SemanticAttributes
|
||||
|
@ -106,29 +103,21 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
return null
|
||||
}
|
||||
|
||||
def "basic #method request #url - tagQueryString=#tagQueryString"() {
|
||||
def "basic #method request #url"() {
|
||||
when:
|
||||
def status = withConfigOverride(Config.HTTP_CLIENT_TAG_QUERY_STRING, "$tagQueryString") {
|
||||
doRequest(method, url)
|
||||
}
|
||||
def status = doRequest(method, url)
|
||||
|
||||
then:
|
||||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 2 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, tagQueryString, url)
|
||||
clientSpan(it, 0, null, method, url)
|
||||
serverSpan(it, 1 + extraClientSpans(), span(extraClientSpans()))
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path | tagQueryString
|
||||
"/success" | false
|
||||
"/success" | true
|
||||
"/success?with=params" | false
|
||||
"/success?with=params" | true
|
||||
"/success#with+fragment" | true
|
||||
"/success?with=params#and=fragment" | true
|
||||
path << ["/success", "/success?with=params"]
|
||||
|
||||
method = "GET"
|
||||
url = server.address.resolve(path)
|
||||
|
@ -240,7 +229,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 3 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, false, uri)
|
||||
clientSpan(it, 0, null, method, uri)
|
||||
serverSpan(it, 1 + extraClientSpans(), span(extraClientSpans()))
|
||||
serverSpan(it, 2 + extraClientSpans(), span(extraClientSpans()))
|
||||
}
|
||||
|
@ -262,7 +251,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 4 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, false, uri)
|
||||
clientSpan(it, 0, null, method, uri)
|
||||
serverSpan(it, 1 + extraClientSpans(), span(extraClientSpans()))
|
||||
serverSpan(it, 2 + extraClientSpans(), span(extraClientSpans()))
|
||||
serverSpan(it, 3 + extraClientSpans(), span(extraClientSpans()))
|
||||
|
@ -288,7 +277,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 3 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, false, uri, statusOnRedirectError(), thrownException)
|
||||
clientSpan(it, 0, null, method, uri, statusOnRedirectError(), thrownException)
|
||||
serverSpan(it, 1 + extraClientSpans(), span(extraClientSpans()))
|
||||
serverSpan(it, 2 + extraClientSpans(), span(extraClientSpans()))
|
||||
}
|
||||
|
@ -311,7 +300,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 3 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, false, uri)
|
||||
clientSpan(it, 0, null, method, uri)
|
||||
serverSpan(it, 1 + extraClientSpans(), span(extraClientSpans()))
|
||||
serverSpan(it, 2 + extraClientSpans(), span(extraClientSpans()))
|
||||
}
|
||||
|
@ -339,7 +328,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
assertTraces(1) {
|
||||
trace(0, 2 + extraClientSpans()) {
|
||||
basicSpan(it, 0, "parent", null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, false, uri, null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, uri, null, thrownException)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +353,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
assertTraces(1) {
|
||||
trace(0, 2 + extraClientSpans()) {
|
||||
basicSpan(it, 0, "parent", null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, false, uri, null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, uri, null, thrownException)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,7 +377,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
assertTraces(1) {
|
||||
trace(0, 2 + extraClientSpans()) {
|
||||
basicSpan(it, 0, "parent", null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, false, uri, null, thrownException)
|
||||
clientSpan(it, 1, span(0), method, uri, null, thrownException)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,7 +399,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
status == 200
|
||||
assertTraces(1) {
|
||||
trace(0, 1 + extraClientSpans()) {
|
||||
clientSpan(it, 0, null, method, false, uri)
|
||||
clientSpan(it, 0, null, method, uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,7 +408,7 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
}
|
||||
|
||||
// parent span must be cast otherwise it breaks debugging classloading (junit loads it early)
|
||||
void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", boolean tagQueryString = false, URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null) {
|
||||
void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null, String httpFlavor = "1.1") {
|
||||
def userAgent = userAgent()
|
||||
trace.span(index) {
|
||||
if (parentSpan == null) {
|
||||
|
@ -437,20 +426,16 @@ abstract class HttpClientTest extends AgentTestRunner {
|
|||
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
|
||||
"${SemanticAttributes.NET_PEER_NAME.key()}" uri.host
|
||||
"${SemanticAttributes.NET_PEER_IP.key()}" { it == null || it == "127.0.0.1" } // Optional
|
||||
// Optional
|
||||
"${SemanticAttributes.NET_PEER_PORT.key()}" uri.port > 0 ? uri.port : { it == null || it == 443 }
|
||||
"${SemanticAttributes.HTTP_URL.key()}" { it == "${uri}" || it == "${removeFragment(uri)}" }
|
||||
"${SemanticAttributes.HTTP_METHOD.key()}" method
|
||||
"${SemanticAttributes.HTTP_FLAVOR.key()}" httpFlavor
|
||||
if (userAgent) {
|
||||
"${SemanticAttributes.HTTP_USER_AGENT.key()}" { it.startsWith(userAgent) }
|
||||
}
|
||||
if (status) {
|
||||
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" status
|
||||
}
|
||||
if (tagQueryString) {
|
||||
"$MoreAttributes.HTTP_QUERY" uri.query
|
||||
"$MoreAttributes.HTTP_FRAGMENT" { it == null || it == uri.fragment } // Optional
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue