
com.undefinedlabs.scope.rules.HttpURLConnectionScopeAgentRule Maven / Gradle / Ivy
package com.undefinedlabs.scope.rules;
import com.undefinedlabs.scope.logger.ScopeLogger;
import com.undefinedlabs.scope.logger.ScopeLoggerResolver;
import com.undefinedlabs.scope.rules.transformer.ScopeAgentAdvicedTransformer;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.Collections;
import java.util.Map;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class HttpURLConnectionScopeAgentRule extends AbstractScopeAgentRule {
public static final ScopeLogger LOGGER = ScopeLoggerResolver.INSTANCE.get();
@Override
protected Iterable extends AgentBuilder> transformers() {
final Class instrumentedClass = lookUp("java.net.HttpURLConnection", getBootstrapClassLoader());
return (instrumentedClass != null) ?
Collections.singleton(newAgentBuilder().type(isSubTypeOf(instrumentedClass), isBootstrapClassLoader()).transform(new ScopeAgentAdvicedTransformer(HttpURLGetResponseCodeAdvice.class, named("getResponseCode"))))
: emptyCollection();
}
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 HttpURLConnection httpURLConnection, @Advice.Return int responseCode, @Advice.Thrown 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 Method logInfoMethod = ruleClass.getMethod("logInfo", 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 tagValuesClass = (Class) getScopeClass.invoke(reflCtxtInstance, "com.undefinedlabs.scope.utils.tag.TagValues");
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 (Exception e) {
throw new RuntimeException(e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy