com.undefinedlabs.scope.rules.HttpUrlConnectionScopeAgentRule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scope-rule-javanet Show documentation
Show all versions of scope-rule-javanet Show documentation
Scope is a APM for tests to give engineering teams unprecedented visibility into their CI process to quickly
identify, troubleshoot and fix failed builds.
This artifact contains the classes to instrument the Java NET package.
package com.undefinedlabs.scope.rules;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.undefinedlabs.scope.logger.ScopeLogger;
import com.undefinedlabs.scope.logger.ScopeLoggerResolver;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class HttpUrlConnectionScopeAgentRule extends AbstractScopeAgentRule {
public static final ScopeLogger LOGGER = ScopeLoggerResolver.INSTANCE.get();
@Override
protected ElementMatcher super TypeDescription> typeMatcher() {
return hasSuperType(named("java.net.HttpURLConnection"));
}
@Override
protected ElementMatcher classLoaderMatcher() {
return isBootstrapClassLoader();
}
@Override
protected Map extends ElementMatcher super MethodDescription>, String> transformers() {
final Map, String> transformers = new HashMap<>();
transformers.put(
named("getResponseCode"),
HttpUrlConnectionScopeAgentRule.class.getName() + "$HttpUrlGetResponseCodeAdvice");
return transformers;
}
public Object getSpanByUuid(final String uuid) {
return UrlConnectionScopeUtils.getSpanByUuid().get(uuid);
}
public void removeSpanByUuid(final String uuid) {
UrlConnectionScopeUtils.getSpanByUuid().remove(uuid);
}
public void logInfo(final String message) {
LOGGER.info(message);
}
public static class HttpUrlGetResponseCodeAdvice {
@Advice.OnMethodExit(onThrowable = IOException.class)
public static void exit(
@Advice.This final HttpURLConnection httpUrlConnection,
@Advice.Return final int responseCode,
@Advice.Thrown final Throwable throwable) {
try {
final String spanUuid =
httpUrlConnection.getRequestProperty(
UrlConnectionScopeUtils.SCOPE_OPEN_CONNECTION_CUUID_KEY);
if (spanUuid == null) {
return;
}
final Class reflectionContextClass =
Class.forName(
"com.undefinedlabs.scope.jdk.reflection.ReflectionContext",
false,
ClassLoader.getSystemClassLoader());
final Object reflCtxtInstance =
reflectionContextClass.getDeclaredField("INSTANCE").get(null);
final Method getScopeMethod =
reflectionContextClass.getMethod(
"getScopeMethod", String.class, String.class, Class[].class);
final Method getScopeClass =
reflectionContextClass.getMethod("getScopeClass", String.class);
final Class> ruleClass =
Class.forName(
"com.undefinedlabs.scope.rules.HttpUrlConnectionScopeAgentRule",
false,
ClassLoader.getSystemClassLoader());
final Object ruleInstance = ruleClass.newInstance();
// It is necessary to load Scope classes using Reflection with SystemClassLoader because
// java.net classes are loaded by BootstrapClassloader.
final Method getSpanByUuidMethod = ruleClass.getMethod("getSpanByUuid", String.class);
final Method removeSpanByUuidMethod = ruleClass.getMethod("removeSpanByUuid", String.class);
final Object span = getSpanByUuidMethod.invoke(ruleInstance, spanUuid);
if (span == null) {
return;
}
final Class> opentracingSpanClass =
(Class>) getScopeClass.invoke(reflCtxtInstance, "io.opentracing.Span");
final Class> spanClass =
(Class>) getScopeClass.invoke(reflCtxtInstance, span.getClass().getName());
final Method setOperationNameMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
spanClass.getName(),
"setOperationName",
new Class[] {String.class});
final Method setTagStringStringMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
spanClass.getName(),
"setTag",
new Class[] {String.class, String.class});
final Method setTagStringBooleanMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
spanClass.getName(),
"setTag",
new Class[] {String.class, boolean.class});
final Method setTagStringNumberMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
spanClass.getName(),
"setTag",
new Class[] {String.class, Number.class});
final Method logFieldsSpanMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance, spanClass.getName(), "log", new Class[] {Map.class});
final Method finishMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance, spanClass.getName(), "finish", new Class[] {});
final Class> tagKeysClass =
(Class>)
getScopeClass.invoke(reflCtxtInstance, "com.undefinedlabs.scope.utils.tag.TagKeys");
final Class> tagKeysNetworkClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.tag.TagKeys$Network");
final Class> tagValuesNetworkClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.tag.TagValues$Network");
final String operationName = String.format("HTTP %s", httpUrlConnection.getRequestMethod());
setOperationNameMethod.invoke(span, operationName);
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_METHOD").get(null),
httpUrlConnection.getRequestMethod());
final Class> adapterClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance,
"com.undefinedlabs.scope.rules.headers.UrlConnectionHeadersAdapter");
final Class> adapterInterfaceClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.rules.http.HttpHeadersAdapter");
final Object adapterObj =
adapterClass.getConstructor(Map.class).newInstance(httpUrlConnection.getHeaderFields());
final Class> headersExtractorClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance,
"com.undefinedlabs.scope.rules.http.HttpHeadersExtractorForSpan");
final Method extractMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
headersExtractorClass.getName(),
"extract",
new Class[] {adapterInterfaceClass});
final Object headersExtractorObj =
headersExtractorClass.getDeclaredField("DEFAULT").get(null);
final Map httpResHeaders =
(Map) extractMethod.invoke(headersExtractorObj, adapterObj);
final Class> spanUtilsClass =
(Class>)
getScopeClass.invoke(reflCtxtInstance, "com.undefinedlabs.scope.utils.SpanUtils");
final Method setTagObjectMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
spanUtilsClass.getName(),
"setTagObject",
new Class[] {opentracingSpanClass, String.class, Object.class});
final Object spanUtilsObj = spanUtilsClass.getDeclaredField("INSTANCE").get(null);
setTagObjectMethod.invoke(
spanUtilsObj,
span,
tagKeysNetworkClass.getDeclaredField("HTTP_RESPONSE_HEADERS").get(null),
httpResHeaders);
final Class> scopeSettingsResolverClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.settings.ScopeSettingsResolver");
final Object scopeSettingsResolverInstance =
scopeSettingsResolverClass.getDeclaredField("INSTANCE").get(null);
final Method scopeSettingsGetMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance, scopeSettingsResolverClass.getName(), "get", new Class[] {});
final Object scopeSettingsObj =
scopeSettingsGetMethod.invoke(scopeSettingsResolverInstance);
final Method scopeSettingsGetSettingMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
scopeSettingsObj.getClass().getName(),
"getSetting",
new Class[] {String.class});
final Object scopeInstrumentationHttpPayloadsKey =
scopeSettingsObj
.getClass()
.getDeclaredField("SCOPE_INSTRUMENTATION_HTTP_PAYLOADS")
.get(null);
final boolean httpPayloadsActive =
(boolean)
scopeSettingsGetSettingMethod.invoke(
scopeSettingsObj, scopeInstrumentationHttpPayloadsKey);
final Object scopeInstrumentationHttpStacktraceKey =
scopeSettingsObj
.getClass()
.getDeclaredField("SCOPE_INSTRUMENTATION_HTTP_STACKTRACE")
.get(null);
final boolean httpStacktrace =
(boolean)
scopeSettingsGetSettingMethod.invoke(
scopeSettingsObj, scopeInstrumentationHttpStacktraceKey);
if (httpStacktrace) {
final Class> tagKeysStacktraceClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.tag.TagKeys$Stacktrace");
final Class> stacktraceUtilsClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.StacktraceUtils");
final Method createStackTraceFieldsMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
stacktraceUtilsClass.getName(),
"createStackTraceFields",
new Class[] {StackTraceElement[].class});
final Object stacktraceUtilsObj =
stacktraceUtilsClass.getDeclaredField("INSTANCE").get(null);
final Object stacktraceFields =
createStackTraceFieldsMethod.invoke(
stacktraceUtilsObj, (Object) Thread.currentThread().getStackTrace());
setTagObjectMethod.invoke(
spanUtilsObj,
span,
tagKeysStacktraceClass.getDeclaredField("STACKTRACE").get(null),
stacktraceFields);
}
// *
// Request payload is not accessible without disturbing Streams.
// *
if (httpPayloadsActive) {
if ("GET".equalsIgnoreCase(httpUrlConnection.getRequestMethod())) {
setTagStringStringMethod.invoke(
span, tagKeysNetworkClass.getDeclaredField("HTTP_REQUEST_PAYLOAD").get(null), "");
} else {
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_REQUEST_PAYLOAD_UNAVAILABLE").get(null),
tagValuesNetworkClass.getDeclaredField("HTTP_PAYLOAD_NOT_ACCESSIBLE").get(null));
}
} else {
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_REQUEST_PAYLOAD_UNAVAILABLE").get(null),
tagValuesNetworkClass.getDeclaredField("HTTP_PAYLOAD_DISABLED").get(null));
}
if (throwable == null) {
setTagStringNumberMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_STATUS_CODE").get(null),
responseCode);
setTagStringBooleanMethod.invoke(
span, tagKeysClass.getDeclaredField("ERROR").get(null), (responseCode >= 400));
if (httpPayloadsActive) {
final Class> scopeIOUtilsClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.ScopeIOUtils");
final Method getSubstringMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
scopeIOUtilsClass.getName(),
"getSubstring",
new Class[] {InputStream.class});
final Object responseStr =
httpUrlConnection.getInputStream() != null
&& httpUrlConnection.getInputStream().markSupported()
? getSubstringMethod.invoke(null, httpUrlConnection.getInputStream())
: null;
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_RESPONSE_PAYLOAD_UNAVAILABLE").get(null),
(responseStr != null && !"".equals(responseStr))
? null
: tagValuesNetworkClass
.getDeclaredField("HTTP_PAYLOAD_NOT_ACCESSIBLE")
.get(null));
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_RESPONSE_PAYLOAD").get(null),
(responseStr != null && !"".equals(responseStr)) ? responseStr : null);
} else {
setTagStringStringMethod.invoke(
span,
tagKeysNetworkClass.getDeclaredField("HTTP_RESPONSE_PAYLOAD_UNAVAILABLE").get(null),
tagValuesNetworkClass.getDeclaredField("HTTP_PAYLOAD_DISABLED").get(null));
}
} else {
final Class> throwableEventClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.events.exception.ThrowableEvent");
final Method newBuilderMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
throwableEventClass.getName(),
"newBuilder",
new Class[] {});
final Object throwableBuilder = newBuilderMethod.invoke(null);
final Class> throwableBuilderClass = throwableBuilder.getClass();
final Method withEventTypeMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
throwableBuilderClass.getName(),
"withEventType",
new Class[] {String.class});
final Method withThrowableMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
throwableBuilderClass.getName(),
"withThrowable",
new Class[] {Throwable.class});
final Method withSourceMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
throwableBuilderClass.getName(),
"withSource",
new Class[] {String.class});
final Method buildMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance, throwableBuilderClass.getName(), "build", new Class[] {});
final Class> eventValuesGeneralClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.utils.event.EventValues$General");
final Class> sourceCodeFactoryClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance,
"com.undefinedlabs.scope.utils.sourcecode.SourceCodeFrameFactory");
final Object sourceCodeFactoryInstance =
sourceCodeFactoryClass.getDeclaredField("INSTANCE").get(null);
final Method createFrameMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
sourceCodeFactoryClass.getName(),
"createFrame",
new Class[] {StackTraceElement[].class});
final Class> eventFieldsFactoryClass =
(Class>)
getScopeClass.invoke(
reflCtxtInstance, "com.undefinedlabs.scope.events.EventFieldsFactory");
final Object eventFieldsFactoryInstance =
eventFieldsFactoryClass.getDeclaredField("INSTANCE").get(null);
final Method createFieldsMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
eventFieldsFactoryClass.getName(),
"createFields",
new Class[] {throwableEventClass});
setTagStringBooleanMethod.invoke(
span, tagKeysClass.getDeclaredField("ERROR").get(null), true);
withEventTypeMethod.invoke(
throwableBuilder, eventValuesGeneralClass.getDeclaredField("ERROR").get(null));
withThrowableMethod.invoke(throwableBuilder, throwable);
final Object sourceCodeFrameInstance =
createFrameMethod.invoke(
sourceCodeFactoryInstance, new Object[] {throwable.getStackTrace()});
final Method getLinkPathMethod =
(Method)
getScopeMethod.invoke(
reflCtxtInstance,
sourceCodeFrameInstance.getClass().getName(),
"getLinkPathWithMethodLine",
new Class[] {});
withSourceMethod.invoke(
throwableBuilder, getLinkPathMethod.invoke(sourceCodeFrameInstance));
final Object throwableEventInstance = buildMethod.invoke(throwableBuilder);
final Object throwableEventFields =
createFieldsMethod.invoke(eventFieldsFactoryInstance, throwableEventInstance);
logFieldsSpanMethod.invoke(span, throwableEventFields);
}
finishMethod.invoke(span);
removeSpanByUuidMethod.invoke(ruleInstance, spanUuid);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy