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:
parent
e98da2a3a2
commit
3fc1765446
|
@ -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 {
|
||||
|
|
|
@ -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"() {
|
||||
|
|
Loading…
Reference in New Issue