Include status code for HttpUrlConnection exceptions (#2272)

* work in progress

* set span state to error and cleanup

* build test

* gotta get spotless

* cleanup

* move getResponseCode advice to its own advice class.

* remove dynamic type from annotation

* Update instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy

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

* Update instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy

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

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
jason plumb 2021-02-17 12:02:02 -08:00 committed by GitHub
parent e98da2a3a2
commit 3fc1765446
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 1 deletions

View File

@ -18,15 +18,19 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.bytebuddy.asm.Advice;
@ -69,11 +73,16 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
Map<ElementMatcher<MethodDescription>, String> map = new HashMap<>();
map.put(
isMethod()
.and(isPublic())
.and(namedOneOf("connect", "getOutputStream", "getInputStream")),
HttpUrlConnectionInstrumentationModule.class.getName() + "$HttpUrlConnectionAdvice");
map.put(
isMethod().and(isPublic()).and(named("getResponseCode")),
HttpUrlConnectionInstrumentationModule.class.getName() + "$GetResponseCodeAdvice");
return map;
}
}
@ -148,6 +157,23 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
}
}
public static class GetResponseCodeAdvice {
@Advice.OnMethodExit
public static void methodExit(
@Advice.This HttpURLConnection connection, @Advice.Return int returnValue) {
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
HttpUrlState httpUrlState = storage.get(connection);
if (httpUrlState != null) {
Span span = Span.fromContext(httpUrlState.context);
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, returnValue);
span.setStatus(HttpStatusConverter.statusFromHttpStatus(returnValue));
}
}
}
// everything is public since called directly from advice code
// (which is inlined into other packages)
public static class HttpUrlState {

View File

@ -4,6 +4,7 @@
*/
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
import io.opentelemetry.api.trace.Span
@ -294,6 +295,29 @@ class HttpUrlConnectionTest extends HttpClientTest implements AgentTestTrait {
}
}
def "error span"(){
def uri = server.address.resolve("/error")
when:
def url = uri.toURL()
runUnderTrace("parent") {
doRequest(method, uri)
}
then:
def expectedException = new IOException("Server returned HTTP response code: 500 for URL: $url")
thrown(IOException)
assertTraces(1) {
trace(0, 3 + extraClientSpans()) {
basicSpan(it, 0, "parent", null, expectedException)
clientSpan(it, 1, span(0), method, uri, 500, expectedException)
serverSpan(it, 2 + extraClientSpans(), span(1 + extraClientSpans()))
}
}
where:
method = "GET"
}
// This test makes no sense on IBM JVM because there is no HttpsURLConnectionImpl class there
@Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") })
def "Make sure we can load HttpsURLConnectionImpl"() {