diff --git a/.gitmodules b/.gitmodules index 27fc232a993..593a3fb913e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "dd-java-agent/agent-jmxfetch/integrations-core"] path = dd-java-agent/agent-jmxfetch/integrations-core - url = https://github.com/DataDog/integrations-core.git + url = https://github.com/GuanceCloud/integrations-core.git diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 49b424361f3..58175af85a3 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -59,6 +59,7 @@ tasks.compileKotlin { } tasks.test { - useJUnitPlatform() - enabled = project.hasProperty("runBuildSrcTests") + // useJUnitPlatform() + // dependsOn(":call-site-instrumentation-plugin:build") + // enabled = !project.hasProperty("skipBuildSrcTest") } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index 238f96ece27..a31864fa3af 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -1,19 +1,5 @@ package datadog.trace.bootstrap; -import static datadog.trace.api.ConfigDefaults.DEFAULT_STARTUP_LOGS_ENABLED; -import static datadog.trace.api.Platform.getRuntimeVendor; -import static datadog.trace.api.Platform.isJavaVersionAtLeast; -import static datadog.trace.api.Platform.isOracleJDK8; -import static datadog.trace.bootstrap.Library.WILDFLY; -import static datadog.trace.bootstrap.Library.detectLibraries; -import static datadog.trace.util.AgentThreadFactory.AgentThread.JMX_STARTUP; -import static datadog.trace.util.AgentThreadFactory.AgentThread.PROFILER_STARTUP; -import static datadog.trace.util.AgentThreadFactory.AgentThread.TRACE_STARTUP; -import static datadog.trace.util.AgentThreadFactory.newAgentThread; -import static datadog.trace.util.Strings.getResourceName; -import static datadog.trace.util.Strings.propertyNameToSystemPropertyName; -import static datadog.trace.util.Strings.toEnvVar; - import datadog.trace.api.Config; import datadog.trace.api.Platform; import datadog.trace.api.StatsDClientManager; @@ -41,6 +27,9 @@ import datadog.trace.util.AgentTaskScheduler; import datadog.trace.util.AgentThreadFactory.AgentThread; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.lang.instrument.Instrumentation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -52,8 +41,20 @@ import java.util.EnumSet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static datadog.trace.api.ConfigDefaults.DEFAULT_STARTUP_LOGS_ENABLED; +import static datadog.trace.api.Platform.getRuntimeVendor; +import static datadog.trace.api.Platform.isJavaVersionAtLeast; +import static datadog.trace.api.Platform.isOracleJDK8; +import static datadog.trace.bootstrap.Library.WILDFLY; +import static datadog.trace.bootstrap.Library.detectLibraries; +import static datadog.trace.util.AgentThreadFactory.AgentThread.JMX_STARTUP; +import static datadog.trace.util.AgentThreadFactory.AgentThread.PROFILER_STARTUP; +import static datadog.trace.util.AgentThreadFactory.AgentThread.TRACE_STARTUP; +import static datadog.trace.util.AgentThreadFactory.newAgentThread; +import static datadog.trace.util.Strings.getResourceName; +import static datadog.trace.util.Strings.propertyNameToSystemPropertyName; +import static datadog.trace.util.Strings.toEnvVar; /** * Agent start up logic. @@ -476,10 +477,10 @@ public void execute() { AGENT_CLASSLOADER.loadClass("datadog.communication.ddagent.SharedCommunicationObjects"); sco = scoClass.getConstructor().newInstance(); } catch (ClassNotFoundException - | NoSuchMethodException - | InstantiationException - | IllegalAccessException - | InvocationTargetException e) { + | NoSuchMethodException + | InstantiationException + | IllegalAccessException + | InvocationTargetException e) { throw new UndeclaredThrowableException(e); } @@ -661,15 +662,17 @@ private static synchronized void registerDeadlockDetectionEvent() { final Method registerMethod = deadlockFactoryClass.getMethod("registerEvents"); registerMethod.invoke(null); } catch (final NoClassDefFoundError - | ClassNotFoundException - | UnsupportedClassVersionError ignored) { + | ClassNotFoundException + | UnsupportedClassVersionError ignored) { log.debug("JMX deadlock detection not supported"); } catch (final Throwable ex) { log.error("Unable to initialize JMX thread deadlock detector", ex); } } - /** Enable JMX based system access provider once it is safe to touch JMX */ + /** + * Enable JMX based system access provider once it is safe to touch JMX + */ private static synchronized void initializeJmxSystemAccessProvider( final ClassLoader classLoader) { if (log.isDebugEnabled()) { @@ -1058,7 +1061,9 @@ private static boolean isDebugMode() { return false; } - /** @return {@code true} if the agent feature is enabled */ + /** + * @return {@code true} if the agent feature is enabled + */ private static boolean isFeatureEnabled(AgentFeature feature) { // must be kept in sync with logic from Config! final String featureEnabledSysprop = feature.getSystemProp(); @@ -1200,7 +1205,9 @@ private static boolean isAppUsingCustomJMXBuilder(final EnumSet librari return false; } - /** Looks for the "dd." system property first then the "DD_" environment variable equivalent. */ + /** + * Looks for the "dd." system property first then the "DD_" environment variable equivalent. + */ private static String ddGetProperty(final String sysProp) { String value = System.getProperty(sysProp); if (null == value) { @@ -1209,7 +1216,9 @@ private static String ddGetProperty(final String sysProp) { return value; } - /** Looks for the "DD_" environment variable equivalent of the given "dd." system property. */ + /** + * Looks for the "DD_" environment variable equivalent of the given "dd." system property. + */ private static String ddGetEnv(final String sysProp) { return System.getenv(toEnvVar(sysProp)); } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/BaseDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/BaseDecorator.java index 71e3927d90a..5c991765414 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/BaseDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/BaseDecorator.java @@ -1,8 +1,5 @@ package datadog.trace.bootstrap.instrumentation.decorator; -import static datadog.trace.api.cache.RadixTreeCache.PORTS; -import static datadog.trace.api.cache.RadixTreeCache.UNSET_PORT; - import datadog.trace.api.Config; import datadog.trace.api.DDTags; import datadog.trace.api.Functions; @@ -10,6 +7,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.Tags; + import java.lang.reflect.Method; import java.net.Inet4Address; import java.net.Inet6Address; @@ -18,6 +16,9 @@ import java.util.concurrent.ExecutionException; import java.util.function.Function; +import static datadog.trace.api.cache.RadixTreeCache.PORTS; +import static datadog.trace.api.cache.RadixTreeCache.UNSET_PORT; + public abstract class BaseDecorator { private static final QualifiedClassNameCache CLASS_NAMES = @@ -38,15 +39,17 @@ public String apply(Class clazz) { protected final boolean traceAnalyticsEnabled; protected final Double traceAnalyticsSampleRate; + protected final CharSequence version; protected BaseDecorator() { final Config config = Config.get(); final String[] instrumentationNames = instrumentationNames(); - this.traceAnalyticsEnabled = + traceAnalyticsEnabled = instrumentationNames.length > 0 && config.isTraceAnalyticsIntegrationEnabled( traceAnalyticsDefault(), instrumentationNames); - this.traceAnalyticsSampleRate = + version = config.getVersion(); + traceAnalyticsSampleRate = (double) config.getInstrumentationAnalyticsSampleRate(instrumentationNames); } @@ -65,6 +68,9 @@ public AgentSpan afterStart(final AgentSpan span) { span.setSpanType(spanType()); } span.setTag(Tags.COMPONENT, component()); + if (version != ""){ + span.setTag(Tags.DD_VERSION,version); + } if (traceAnalyticsEnabled) { span.setMetric(DDTags.ANALYTICS_SAMPLE_RATE, traceAnalyticsSampleRate); } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBQueryInfo.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBQueryInfo.java index 71744b60741..4488ca8b161 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBQueryInfo.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBQueryInfo.java @@ -1,5 +1,6 @@ package datadog.trace.bootstrap.instrumentation.jdbc; +import datadog.trace.api.Config; import datadog.trace.api.cache.DDCache; import datadog.trace.api.cache.DDCaches; import datadog.trace.api.normalize.SQLNormalizer; @@ -7,6 +8,11 @@ import java.util.function.Function; import java.util.function.ToIntFunction; +import java.util.HashMap; +import java.util.Map; + +import static java.nio.charset.StandardCharsets.UTF_8; + public final class DBQueryInfo { private static final int COMBINED_SQL_LIMIT = 2 * 1024 * 1024; // characters @@ -26,9 +32,20 @@ public static DBQueryInfo ofPreparedStatement(String sql) { private final UTF8BytesString operation; private final UTF8BytesString sql; + private Map vals; + private UTF8BytesString originSql; + + public boolean SqlObfuscation = Config.get().getJdbcSqlObfuscation(); public DBQueryInfo(String sql) { this.sql = SQLNormalizer.normalize(sql); + + if (SqlObfuscation) { + this.originSql = UTF8BytesString.create(sql.getBytes(UTF_8)); + } else { + this.originSql = UTF8BytesString.EMPTY; + } + this.vals = new HashMap<>(); this.operation = UTF8BytesString.create(extractOperation(this.sql)); } @@ -36,13 +53,25 @@ public UTF8BytesString getOperation() { return operation; } + public Map getVals() { + return vals; + } + + public void setVal(int index, String val) { + vals.put(index, val); + } + public UTF8BytesString getSql() { return sql; } + int weight() { return sql.length(); } + public UTF8BytesString getOriginSql() { + return originSql; + } public static CharSequence extractOperation(CharSequence sql) { if (null == sql) { diff --git a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java index dcb9dfa69f1..bb5f373e1d2 100644 --- a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java +++ b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java @@ -50,7 +50,7 @@ private static void run(final StatsDClientManager statsDClientManager, final Con // Reduce noisiness of jmxfetch logging. System.setProperty("org.slf4j.simpleLogger.log.org.datadog.jmxfetch", "warn"); } - + System.setProperty("dd.jmxfetch.jmx_metric_fetch.enabled", "true"); final String jmxFetchConfigDir = config.getJmxFetchConfigDir(); final List jmxFetchConfigs = config.getJmxFetchConfigs(); final List internalMetricsConfigs = getInternalMetricFiles(); diff --git a/dd-java-agent/instrumentation/axis-1/build.gradle b/dd-java-agent/instrumentation/axis-1/build.gradle new file mode 100644 index 00000000000..9ff9b765645 --- /dev/null +++ b/dd-java-agent/instrumentation/axis-1/build.gradle @@ -0,0 +1,16 @@ +muzzle { + pass { + group = "org.apache.axis" + module = "axis" + versions = "[1.4,)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + + +dependencies { + compileOnly group: 'org.apache.axis', name: 'axis', version: '1.4' + compileOnly group: 'axis', name: 'axis-jaxrpc', version: '1.4' +} + diff --git a/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisClientInstrumentation.java b/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisClientInstrumentation.java new file mode 100644 index 00000000000..a9f99845b32 --- /dev/null +++ b/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisClientInstrumentation.java @@ -0,0 +1,99 @@ +package datadog.trace.instrumentation.axis1; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.axis1.AxisMessageDecorator.AXIS2_MESSAGE; +import static datadog.trace.instrumentation.axis1.AxisMessageDecorator.DECORATE; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.axis.MessageContext; + +@AutoService(Instrumenter.class) +public final class AxisClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public AxisClientInstrumentation() {super("axis1");} + + + @Override + public String hierarchyMarkerType() { + return "org.apache.axis.Handler"; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".AxisMessageDecorator", + }; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(named("invoke")) + .and(takesArgument(0, named("org.apache.axis.MessageContext"))), + getClass().getName() + "$ClientInvokeMessageAdvice"); + } + + public static final class ClientInvokeMessageAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope beginInvoke( + @Advice.Argument(0) final MessageContext message) { + if (null == message ){ + return null; + } + + if (null == message.getOperation()){ + return null; + } + + AgentScope scope = activeScope(); + if (null != scope) { + if (!DECORATE.sameTrace(scope.span(), message)) { + AgentSpan span = startSpan(AXIS2_MESSAGE); + DECORATE.afterStart(span); + DECORATE.onMessage(span, message); + return activateSpan(span); + } + } + return null; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class,suppress = Throwable.class) + public static void endInvoke( + @Advice.Enter final AgentScope scope, + @Advice.Argument(0) final MessageContext message, + @Advice.Thrown final Throwable error){ + if (null == message){ + return ; + } + if (null == scope) { + return; + } + System.out.println("getSoapAction name"+message.getOperation().getSoapAction()); + AgentSpan span = scope.span(); + if (null != error) { + DECORATE.onError(span, error); + } + DECORATE.beforeFinish(span, message); + scope.close(); + span.finish(); + } + } +} diff --git a/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisMessageDecorator.java b/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisMessageDecorator.java new file mode 100644 index 00000000000..0dc89273468 --- /dev/null +++ b/dd-java-agent/instrumentation/axis-1/src/main/java/datadog/trace/instrumentation/axis1/AxisMessageDecorator.java @@ -0,0 +1,59 @@ +package datadog.trace.instrumentation.axis1; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import org.apache.axis.MessageContext; +import org.apache.axis.description.OperationDesc; + +import static datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes.SOAP; + +public class AxisMessageDecorator extends BaseDecorator { + public static final AxisMessageDecorator DECORATE = new AxisMessageDecorator(); + + public static final CharSequence AXIS = UTF8BytesString.create("axis"); + public static final CharSequence AXIS2_MESSAGE = UTF8BytesString.create("axis.message"); + + @Override + protected String[] instrumentationNames() { + return new String[] {"axis"}; + } + + @Override + protected CharSequence spanType() { + return SOAP; + } + + @Override + protected CharSequence component() { + return AXIS; + } + + public void onMessage(final AgentSpan span, final MessageContext message) { + span.setResourceName(soapAction(message)); + } + + public void beforeFinish(final AgentSpan span, final MessageContext message) { + super.beforeFinish(span); + } + + public boolean sameTrace(final AgentSpan span, final MessageContext message) { + return AXIS2_MESSAGE.equals(span.getSpanName()) + && span.getResourceName().equals(soapAction(message)); + } + + private static String soapAction(final MessageContext message) { + OperationDesc operation = message.getOperation(); + if(null != operation){ + return operation.getName(); + } + String action = message.getSOAPActionURI(); + if (null != action && !action.isEmpty()) { + return action; + } + if (message.getTransportName() != null) { + return message.getTransportName(); + } + return null; + } +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/build.gradle b/dd-java-agent/instrumentation/dubbo-alibaba/build.gradle new file mode 100644 index 00000000000..390c4231cf8 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/build.gradle @@ -0,0 +1,16 @@ +apply from: "$rootDir/gradle/java.gradle" + +muzzle { + pass { + group = "com.alibaba" + module = "dubbo" + versions = "[2.6.0,2.8.4]" + assertInverse = true + } +} + + +dependencies { + compileOnly group: 'com.alibaba', name: 'dubbo', version: '2.6.9' +} + diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboConstants.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboConstants.java new file mode 100644 index 00000000000..8bfb5c11761 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboConstants.java @@ -0,0 +1,26 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + +/** + * @Description + * @Author liurui + * @Date 2023/3/29 18:17 + */ +public class DubboConstants { + + public static final String TAG_PREFIX = "dubbo_"; + public static final String TAG_SHORT_URL = TAG_PREFIX + "short_url"; + public static final String TAG_URL = TAG_PREFIX + "url"; + public static final String TAG_METHOD = TAG_PREFIX + "method"; + public static final String TAG_VERSION = TAG_PREFIX + "version"; + public static final String TAG_SIDE = TAG_PREFIX + "side"; + + public static final String CONSUMER_SIDE = "consumer"; + public static final String PROVIDER_SIDE = "provider"; + + public static final String SIDE_KEY = "side"; + + public static final String GROUP_KEY = "group"; + + public static final String VERSION = "release"; + +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboDecorator.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboDecorator.java new file mode 100644 index 00000000000..ea4397f502a --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboDecorator.java @@ -0,0 +1,133 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.utils.StringUtils; +import com.alibaba.dubbo.rpc.Invocation; +import com.alibaba.dubbo.rpc.Invoker; +import com.alibaba.dubbo.rpc.RpcContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.alibaba.dubbo.DubboConstants.*; +import static datadog.trace.instrumentation.alibaba.dubbo.DubboHeadersExtractAdapter.GETTER; +import static datadog.trace.instrumentation.alibaba.dubbo.DubboHeadersInjectAdapter.SETTER; + +public class DubboDecorator extends BaseDecorator { + private static final Logger log = LoggerFactory.getLogger(DubboDecorator.class); + public static final CharSequence DUBBO_REQUEST = UTF8BytesString.create("dubbo"); + + public static final CharSequence DUBBO_SERVER = UTF8BytesString.create("alibaba-dubbo"); + + public static final DubboDecorator DECORATE = new DubboDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{"apache-dubbo"}; + } + + @Override + protected CharSequence spanType() { + return DUBBO_SERVER; + } + + @Override + protected CharSequence component() { + return DUBBO_SERVER; + } + + public AgentSpan startDubboSpan(Invoker invoker, Invocation invocation) { + URL url = invoker.getUrl(); + boolean isConsumer = isConsumerSide(url); + + String methodName = invocation.getMethodName(); + String resourceName = generateOperationName(url,invocation); + String shortUrl = generateRequestURL(url,invocation); + if (log.isDebugEnabled()) { + log.debug("isConsumer:{},method:{},resourceName:{},shortUrl:{},longUrl:{},version:{}", + isConsumer, + methodName, + resourceName, + shortUrl, + url.toString(), + getVersion(url) + ); + } + AgentSpan span; + RpcContext rpcContext = RpcContext.getContext(); + if (isConsumer){ + // this is consumer + span = startSpan(DUBBO_REQUEST); + }else{ + // this is provider + AgentSpan.Context parentContext = propagate().extract(rpcContext, GETTER); + span = startSpan(DUBBO_REQUEST,parentContext); + } + span.setTag(TAG_URL, url.toString()); + span.setTag(TAG_SHORT_URL, shortUrl); + span.setTag(TAG_METHOD, methodName); + span.setTag(TAG_VERSION,getVersion(url)); + span.setTag(TAG_SIDE,isConsumer?CONSUMER_SIDE:PROVIDER_SIDE); + afterStart(span); + + withMethod(span, resourceName); +// if (isConsumer){ + propagate().inject(span, rpcContext, SETTER); +// } + return span; + } + + public void withMethod(final AgentSpan span, final String methodName) { + span.setResourceName(methodName); + } + + @Override + public AgentSpan afterStart(AgentSpan span) { + return super.afterStart(span); + } + + + private String generateOperationName(URL requestURL, Invocation invocation) { + StringBuilder operationName = new StringBuilder(); + String groupStr = requestURL.getParameter(GROUP_KEY); + groupStr = StringUtils.isEmpty(groupStr) ? "" : groupStr + "/"; + operationName.append(groupStr); + operationName.append(requestURL.getPath()); + operationName.append("." + invocation.getMethodName() + "("); + for (Class classes : invocation.getParameterTypes()) { + operationName.append(classes.getSimpleName() + ","); + } + if (invocation.getParameterTypes().length > 0) { + operationName.delete(operationName.length() - 1, operationName.length()); + } + operationName.append(")"); + return operationName.toString(); + } + + private String generateRequestURL(URL url, Invocation invocation) { + StringBuilder requestURL = new StringBuilder(); + requestURL.append(url.getProtocol() + "://"); + requestURL.append(url.getHost()); + requestURL.append(":" + url.getPort() + "/"); + requestURL.append(generateOperationName(url, invocation)); + return requestURL.toString(); + } + + public boolean isConsumerSide(URL url) { + return url.getParameter(SIDE_KEY, PROVIDER_SIDE).equals(CONSUMER_SIDE); + } + + public AgentScope buildSpan(Invoker invoker, Invocation invocation) { + AgentSpan span = startDubboSpan(invoker,invocation); + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + private String getVersion(URL url){ + return url.getParameter(VERSION); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersExtractAdapter.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersExtractAdapter.java new file mode 100644 index 00000000000..e5d2c06085a --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersExtractAdapter.java @@ -0,0 +1,28 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import com.alibaba.dubbo.rpc.RpcContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class DubboHeadersExtractAdapter implements AgentPropagation.ContextVisitor{ + private static final Logger log = LoggerFactory.getLogger(DubboHeadersExtractAdapter.class); + public static final DubboHeadersExtractAdapter GETTER = new DubboHeadersExtractAdapter(); + @Override + public void forEachKey(RpcContext carrier, AgentPropagation.KeyClassifier classifier) { + Map objectAttachments = carrier.getAttachments(); + if (log.isDebugEnabled()) { + log.debug("Extract size: {}",objectAttachments.entrySet().size()); + } + for (Map.Entry entry : objectAttachments.entrySet()){ + // log.debug("Extract "+entry.getKey()+"\t"+entry.getValue()); + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersInjectAdapter.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersInjectAdapter.java new file mode 100644 index 00000000000..842834fdbbb --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboHeadersInjectAdapter.java @@ -0,0 +1,20 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import com.alibaba.dubbo.rpc.RpcContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DubboHeadersInjectAdapter implements AgentPropagation.Setter { + public static final DubboHeadersInjectAdapter SETTER = new DubboHeadersInjectAdapter(); + private static final Logger log = LoggerFactory.getLogger(DubboHeadersInjectAdapter.class); + @Override + public void set(RpcContext carrier, String key, String value) { + if (log.isDebugEnabled()) { +// System.out.println("dubbo Inject " + key + ":\t" + value); + log.debug("dubbo Inject {} :\t {}" , key , value); + } + carrier.setAttachment(key, value); +// carrier.getAttachments().put(key, value); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboInstrumentation.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboInstrumentation.java new file mode 100644 index 00000000000..a514d9704f6 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/DubboInstrumentation.java @@ -0,0 +1,65 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import java.util.Map; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class DubboInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public DubboInstrumentation() { + super("alibaba-dubbo"); + } + + public static final String CLASS_NAME = "com.alibaba.dubbo.monitor.support.MonitorFilter"; + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(CLASS_NAME)); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("invoke")) + .and(takesArguments(2)) + .and(takesArgument(0, named("com.alibaba.dubbo.rpc.Invoker"))) + .and(takesArgument(1, named("com.alibaba.dubbo.rpc.Invocation"))), + packageName + ".RequestAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".DubboDecorator", + packageName + ".DubboConstants", + packageName + ".RequestAdvice", + packageName + ".DubboHeadersExtractAdapter", + packageName + ".DubboHeadersInjectAdapter" + }; + } + + @Override + public Map contextStore() { + return singletonMap("com.alibaba.dubbo.rpc.RpcContext", AgentSpan.class.getName()); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/RequestAdvice.java b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/RequestAdvice.java new file mode 100644 index 00000000000..b51f4ed1045 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-alibaba/src/main/java/datadog/trace/instrumentation/alibaba/dubbo/RequestAdvice.java @@ -0,0 +1,44 @@ +package datadog.trace.instrumentation.alibaba.dubbo; + +import com.alibaba.dubbo.rpc.Filter; +import com.alibaba.dubbo.rpc.Invocation; +import com.alibaba.dubbo.rpc.Invoker; +import com.alibaba.dubbo.rpc.RpcContext; +import datadog.trace.bootstrap.CallDepthThreadLocalMap; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.instrumentation.alibaba.dubbo.DubboDecorator.DECORATE; + +public class RequestAdvice { + public static final Logger logger = LoggerFactory.getLogger(RequestAdvice.class); + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope beginRequest(@Advice.This Filter filter, @Advice.Argument(0) final Invoker invoker, + @Advice.Argument(1) final Invocation invocation) { + //logger.info("dubboFilterName:"+filter.getClass().getName()); + +// final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(RpcContext.class); +// if (callDepth > 0) { +// return null; +// } + AgentScope agentScope = DECORATE.buildSpan(invoker, invocation); + return agentScope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATE.onError(scope.span(), throwable); + DECORATE.beforeFinish(scope.span()); + + scope.close(); + scope.span().finish(); +// CallDepthThreadLocalMap.reset(RpcContext.class); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/build.gradle b/dd-java-agent/instrumentation/dubbo-apache-2.7/build.gradle new file mode 100644 index 00000000000..cfcec49fff5 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/build.gradle @@ -0,0 +1,15 @@ +apply from: "$rootDir/gradle/java.gradle" + +muzzle { + pass { + group = "org.apache.dubbo" + module = "dubbo" + versions = "[2.7.0,)" + } +} + + +dependencies { + compileOnly group: 'org.apache.dubbo', name: 'dubbo', version: '2.7.0' +} + diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConstants.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConstants.java new file mode 100644 index 00000000000..c204d26cddb --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConstants.java @@ -0,0 +1,29 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +/** + * @Description + * @Author liurui + * @Date 2023/3/29 18:17 + */ +public class DubboConstants { + + public static final String TAG_PREFIX = "dubbo_"; + public static final String TAG_SHORT_URL = TAG_PREFIX + "short_url"; + public static final String TAG_URL = TAG_PREFIX + "url"; + public static final String TAG_METHOD = TAG_PREFIX + "method"; + public static final String TAG_VERSION = TAG_PREFIX + "version"; + public static final String TAG_SIDE = TAG_PREFIX + "side"; + public static final String TAG_HOST = TAG_PREFIX + "host"; + + public static final String CONSUMER_SIDE = "consumer"; + public static final String PROVIDER_SIDE = "provider"; + + public static final String SIDE_KEY = "side"; + + public static final String GROUP_KEY = "group"; + + public static final String VERSION = "release"; + + public static final String META_RESOURCE="org.apache.dubbo.metadata.MetadataService"; + +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerAdvice.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerAdvice.java new file mode 100644 index 00000000000..bba2a087e70 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerAdvice.java @@ -0,0 +1,31 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.protocol.AbstractInvoker; + +import static datadog.trace.instrumentation.dubbo_2_7x.DubboDecorator.DECORATE; + +public class DubboConsumerAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope beginRequest(@Advice.This AbstractInvoker invoker, + @Advice.Argument(0) final Invocation invocation + ) { + AgentScope scope = DECORATE.buildSpan(invoker, invocation); + return scope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATE.onError(scope.span(), throwable); + DECORATE.beforeFinish(scope.span()); + + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerInstrumentation.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerInstrumentation.java new file mode 100644 index 00000000000..a1f24aaadb9 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboConsumerInstrumentation.java @@ -0,0 +1,58 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class DubboConsumerInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public DubboConsumerInstrumentation() { + super("apache-dubbo"); + } + + public static final String CLASS_NAME = "org.apache.dubbo.rpc.protocol.AbstractInvoker"; + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(CLASS_NAME)); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("invoke")) + .and(takesArguments(1)) + .and(takesArgument(0, named("org.apache.dubbo.rpc.Invocation"))), + packageName + ".DubboConsumerAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".DubboDecorator", + packageName + ".DubboTraceInfo", + packageName + ".HostAndPort", + packageName + ".DubboConstants", + packageName + ".DubboConsumerAdvice", + packageName + ".DubboHeadersExtractAdapter", + packageName + ".DubboHeadersInjectAdapter" + }; + } + +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboDecorator.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboDecorator.java new file mode 100644 index 00000000000..a1aa303769f --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboDecorator.java @@ -0,0 +1,180 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import datadog.trace.api.Config; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.dubbo_2_7x.DubboConstants.*; +import static datadog.trace.instrumentation.dubbo_2_7x.DubboHeadersExtractAdapter.GETTER; +import static datadog.trace.instrumentation.dubbo_2_7x.DubboHeadersInjectAdapter.SETTER; + +public class DubboDecorator extends BaseDecorator { + private static final Logger log = LoggerFactory.getLogger(DubboDecorator.class); + public static final CharSequence DUBBO_REQUEST = UTF8BytesString.create("dubbo"); + + public static final CharSequence DUBBO_SERVER = UTF8BytesString.create("apache-dubbo"); + + public static final DubboDecorator DECORATE = new DubboDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{"apache-dubbo"}; + } + + @Override + protected CharSequence spanType() { + return DUBBO_SERVER; + } + + @Override + protected CharSequence component() { + return DUBBO_SERVER; + } + + public AgentSpan startDubboSpan(Invoker invoker,Invocation invocation) { + URL url = invoker.getUrl(); + boolean isConsumer = isConsumerSide(url); + log.debug("isConsumer:{},invoker name:{}",isConsumer,invoker.getClass().getName()); + log.debug("isConsumer:{},invocation:{}",isConsumer,invocation.getClass().getName()); + + String methodName = invocation.getMethodName(); + String resourceName = generateOperationName(invoker,url,invocation); + if (!isConsumer&& META_RESOURCE.equals(invoker.getInterface().getName())){ + log.debug("skip span because dubbo resourceName:{}",META_RESOURCE); + return activeSpan(); + } + String shortUrl = generateRequestURL(invoker,url,invocation); + if (log.isDebugEnabled()) { + log.debug("isConsumer:{},method:{},resourceName:{},shortUrl:{},longUrl:{},version:{}", + isConsumer, + methodName, + resourceName, + shortUrl, + url.toString(), + getVersion(url) + ); + } + AgentSpan span; + + DubboTraceInfo dubboTraceInfo = new DubboTraceInfo((RpcInvocation) invocation,RpcContext.getContext()); + + if (isConsumer){ + // this is consumer + span = startSpan(DUBBO_REQUEST); + propagate().inject(span, dubboTraceInfo, SETTER); + }else{ + // this is provider + AgentSpan.Context parentContext = propagate().extract(dubboTraceInfo, GETTER); + span = startSpan(DUBBO_REQUEST,parentContext); + if (Config.get().isDubboProviderPropagateEnabled()){ + propagate().inject(span, dubboTraceInfo, SETTER); + } + } + span.setTag(TAG_URL, url.toString()); + span.setTag(TAG_SHORT_URL, shortUrl); + span.setTag(TAG_METHOD, methodName); + span.setTag(TAG_VERSION,getVersion(url)); + + span.setTag(TAG_SIDE,isConsumer?CONSUMER_SIDE:PROVIDER_SIDE); + + afterStart(span); + + withMethod(span, resourceName); + return span; + } + + public void withMethod(final AgentSpan span, final String methodName) { + span.setResourceName(methodName); + } + + @Override + public AgentSpan afterStart(AgentSpan span) { + return super.afterStart(span); + } + + + private String providerResourceName(Invoker invoker,Invocation invocation){ + StringBuilder operationName = new StringBuilder(); + // operationName.append(invoker.getInterface().getName()); + if(invoker.getInterface()!=null){ + operationName.append(invoker.getInterface().getName()); + }else{ + operationName.append(invoker.getClass().getName()); + } + + operationName.append("." + invocation.getMethodName() + "("); + for (Class classes : invocation.getParameterTypes()) { + operationName.append(classes.getSimpleName() + ","); + } + if (invocation.getParameterTypes().length > 0) { + operationName.delete(operationName.length() - 1, operationName.length()); + } + operationName.append(")"); + return operationName.toString(); + } + + private String generateOperationName(Invoker invoker,URL requestURL, Invocation invocation) { + boolean isConsumer = isConsumerSide(requestURL); + if (isConsumer) { + StringBuilder operationName = new StringBuilder(); + String groupStr = requestURL.getParameter(GROUP_KEY); + groupStr = StringUtils.isEmpty(groupStr) ? "" : groupStr + "/"; + operationName.append(groupStr); + operationName.append(requestURL.getPath()); + operationName.append("." + invocation.getMethodName() + "("); + for (Class classes : invocation.getParameterTypes()) { + operationName.append(classes.getSimpleName() + ","); + } + if (invocation.getParameterTypes().length > 0) { + operationName.delete(operationName.length() - 1, operationName.length()); + } + operationName.append(")"); + return operationName.toString(); + }else{ + return providerResourceName(invoker,invocation); + } + + } + + private String generateRequestURL(Invoker invoker,URL url, Invocation invocation) { + StringBuilder requestURL = new StringBuilder(); + requestURL.append(url.getProtocol() + "://"); + requestURL.append(url.getHost()); + requestURL.append(":" + url.getPort() + "/"); + requestURL.append(generateOperationName(invoker,url, invocation)); + return requestURL.toString(); + } + + public boolean isConsumerSide(URL url) { + return url.getParameter(SIDE_KEY, PROVIDER_SIDE).equals(CONSUMER_SIDE); + } + + public AgentScope buildSpan(Invoker invoker, Invocation invocation) { + AgentSpan span = startDubboSpan(invoker,invocation); + if (span==null){ + return activeScope(); + } + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + private String getVersion(URL url){ + return url.getParameter(VERSION); + } + + private String getHostAddress(Invocation invocation) { + final URL url = invocation.getInvoker().getUrl(); + return HostAndPort.toHostAndPortString(url.getHost(), url.getPort()); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersExtractAdapter.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersExtractAdapter.java new file mode 100644 index 00000000000..f5da784ff3a --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersExtractAdapter.java @@ -0,0 +1,51 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.Map; + +public class DubboHeadersExtractAdapter implements AgentPropagation.ContextVisitor{ + private static final Logger log = LoggerFactory.getLogger(DubboHeadersExtractAdapter.class); + public static final DubboHeadersExtractAdapter GETTER = new DubboHeadersExtractAdapter(); + @Override + public void forEachKey(DubboTraceInfo carrier, AgentPropagation.KeyClassifier classifier) { + Map objectAttachments = getAttachments(carrier); + if (log.isDebugEnabled()) { + log.debug("Dubbo provider Extract size: {}",objectAttachments.entrySet().size()); + } + for (Map.Entry entry : objectAttachments.entrySet()){ + log.debug("Dubbo Extract "+entry.getKey()+":\t\t"+entry.getValue()); + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } + + Map getAttachments(DubboTraceInfo carrier){ + Map objectAttachments = carrier.getContext().getAttachments(); + if (objectAttachments.size()==0) { + // dubbo version < 2.7.15 + try { + Map attachments = (Map) getValue(RpcInvocation.class, carrier.getInvocation(), "attachments"); + return attachments; + } catch (Exception e) { + log.error("Dubbo get context attachments exception", e); + } + } + return objectAttachments; + } + + public static final Object getValue(Class klass, Object instance, String name) throws NoSuchFieldException, IllegalAccessException { + Field field = klass.getDeclaredField(name); + field.setAccessible(true); + return field.get(instance); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersInjectAdapter.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersInjectAdapter.java new file mode 100644 index 00000000000..87ae3a41d76 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboHeadersInjectAdapter.java @@ -0,0 +1,22 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class DubboHeadersInjectAdapter implements AgentPropagation.Setter { + public static final DubboHeadersInjectAdapter SETTER = new DubboHeadersInjectAdapter(); + private static final Logger log = LoggerFactory.getLogger(DubboHeadersInjectAdapter.class); + @Override + public void set(DubboTraceInfo carrier, String key, String value) { + if (log.isDebugEnabled()) { + log.debug("dubbo Inject {} :\t {}" , key , value); + } + carrier.getContext().setAttachment(key, value); + carrier.getInvocation().setAttachment(key, value); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderAdvice.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderAdvice.java new file mode 100644 index 00000000000..f416a85a8d8 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderAdvice.java @@ -0,0 +1,34 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.proxy.AbstractProxyInvoker; + +import static datadog.trace.instrumentation.dubbo_2_7x.DubboDecorator.DECORATE; + +public class DubboProviderAdvice { + + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope beginRequest(@Advice.This AbstractProxyInvoker invoker, + @Advice.Argument(0) final Invocation invocation + ) { + AgentScope scope = DECORATE.buildSpan(invoker, invocation); + return scope; +// return activeScope(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATE.onError(scope.span(), throwable); + DECORATE.beforeFinish(scope.span()); + + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderInstrumentation.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderInstrumentation.java new file mode 100644 index 00000000000..d739ca9e9c5 --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboProviderInstrumentation.java @@ -0,0 +1,58 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class DubboProviderInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public DubboProviderInstrumentation() { + super("apache-dubbo"); + } + + public static final String CLASS_NAME = "org.apache.dubbo.rpc.proxy.AbstractProxyInvoker"; + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(CLASS_NAME)); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("invoke")) + .and(takesArguments(1)) + .and(takesArgument(0, named("org.apache.dubbo.rpc.Invocation"))), + packageName + ".DubboProviderAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".DubboDecorator", + packageName + ".DubboTraceInfo", + packageName + ".HostAndPort", + packageName + ".DubboConstants", + packageName + ".DubboProviderAdvice", + packageName + ".DubboHeadersExtractAdapter", + packageName + ".DubboHeadersInjectAdapter" + }; + } + +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboTraceInfo.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboTraceInfo.java new file mode 100644 index 00000000000..ddcda39f4da --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/DubboTraceInfo.java @@ -0,0 +1,34 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcInvocation; + +public class DubboTraceInfo { + + RpcInvocation invocation; + + RpcContext context; + + public DubboTraceInfo(RpcInvocation invocation, RpcContext context) { + this.invocation = invocation; + this.context = context; + } + + public RpcInvocation getInvocation() { + return invocation; + } + + public void setInvocation(RpcInvocation invocation) { + this.invocation = invocation; + } + + public RpcContext getContext() { + return context; + } + + public void setContext(RpcContext context) { + this.context = context; + } + + +} diff --git a/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/HostAndPort.java b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/HostAndPort.java new file mode 100644 index 00000000000..a9954f1933c --- /dev/null +++ b/dd-java-agent/instrumentation/dubbo-apache-2.7/src/main/java/datadog/trace/instrumentation/dubbo_2_7x/HostAndPort.java @@ -0,0 +1,50 @@ +package datadog.trace.instrumentation.dubbo_2_7x; + +/** + * @Description + * @Author liurui + * @Date 2023/3/29 17:23 + */ +public class HostAndPort { + public static final int NO_PORT = -1; + + public static int getValidPortOrNoPort(int port) { + if (!isValidPort(port)) { + return NO_PORT; + } + return port; + } + + public static int getPortOrNoPort(int port) { + if (port < 0) { + return HostAndPort.NO_PORT; + } + return port; + } + + public static boolean isValidPort(int port) { + return port >= 0 && port <= 65535; + } + + public static String toHostAndPortString(String host, int port) { + return toHostAndPortString(host, port, NO_PORT); + } + + /** + * This API does not verification for input args. + */ + public static String toHostAndPortString(String host, int port, int noPort) { + // don't validation hostName + // don't validation port range + if (noPort == port) { + return host; + } + final int hostLength = host == null ? 0 : host.length(); + final StringBuilder builder = new StringBuilder(hostLength + 6); + builder.append(host); + builder.append(':'); + builder.append(port); + return builder.toString(); + } + +} diff --git a/dd-java-agent/instrumentation/jax-rs-annotations-1/src/main/java/datadog/trace/instrumentation/jaxrs1/JaxRsAnnotationsDecorator.java b/dd-java-agent/instrumentation/jax-rs-annotations-1/src/main/java/datadog/trace/instrumentation/jaxrs1/JaxRsAnnotationsDecorator.java index b5a8cb96c3b..635c9760d2a 100644 --- a/dd-java-agent/instrumentation/jax-rs-annotations-1/src/main/java/datadog/trace/instrumentation/jaxrs1/JaxRsAnnotationsDecorator.java +++ b/dd-java-agent/instrumentation/jax-rs-annotations-1/src/main/java/datadog/trace/instrumentation/jaxrs1/JaxRsAnnotationsDecorator.java @@ -60,7 +60,11 @@ public void onJaxRsSpan( < ResourceNamePriorities.HTTP_FRAMEWORK_ROUTE) { HTTP_RESOURCE_DECORATOR.withRoute( parent.getLocalRootSpan(), httpMethodAndRoute.getLeft(), httpMethodAndRoute.getRight()); - parent.getLocalRootSpan().setTag(Tags.COMPONENT, "jax-rs"); + if (!(parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("alibaba-dubbo") + || parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("dubbo") + || parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("apache-dubbo"))){ + parent.getLocalRootSpan().setTag(Tags.COMPONENT, "jax-rs"); + } } span.setResourceName(DECORATE.spanNameForMethod(target, method)); diff --git a/dd-java-agent/instrumentation/jax-rs-annotations-2/src/main/java/datadog/trace/instrumentation/jaxrs2/JaxRsAnnotationsDecorator.java b/dd-java-agent/instrumentation/jax-rs-annotations-2/src/main/java/datadog/trace/instrumentation/jaxrs2/JaxRsAnnotationsDecorator.java index e224b7d7403..eb42a3c585d 100644 --- a/dd-java-agent/instrumentation/jax-rs-annotations-2/src/main/java/datadog/trace/instrumentation/jaxrs2/JaxRsAnnotationsDecorator.java +++ b/dd-java-agent/instrumentation/jax-rs-annotations-2/src/main/java/datadog/trace/instrumentation/jaxrs2/JaxRsAnnotationsDecorator.java @@ -69,6 +69,11 @@ public void onJaxRsSpan( if (parent.getLocalRootSpan().getResourceNamePriority() < ResourceNamePriorities.HTTP_FRAMEWORK_ROUTE) { + if (parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("alibaba-dubbo") + || parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("dubbo") + || parent.getLocalRootSpan().getTag(Tags.COMPONENT).toString().equals("apache-dubbo")){ + return; + } parent.setTag(Tags.COMPONENT, "jax-rs"); // current handler is a filter diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java index e215cce37ea..ed759f043c9 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java @@ -13,12 +13,14 @@ import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; import datadog.trace.bootstrap.CallDepthThreadLocalMap; +import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.InstrumentationContext; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; import datadog.trace.bootstrap.instrumentation.jdbc.DBQueryInfo; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; @@ -35,8 +37,8 @@ public AbstractPreparedStatementInstrumentation( @Override public String[] helperClassNames() { - return new String[] { - packageName + ".JDBCDecorator", + return new String[]{ + packageName + ".JDBCDecorator", }; } @@ -53,16 +55,58 @@ public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( nameStartsWith("execute").and(takesArguments(0)).and(isPublic()), AbstractPreparedStatementInstrumentation.class.getName() + "$PreparedStatementAdvice"); + transformer.applyAdvice( + nameStartsWith("set").and(takesArguments(2)).and(isPublic()), + AbstractPreparedStatementInstrumentation.class.getName() + "$SetStringAdvice"); + } + + public static class SetStringAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void StartSetString( + // @Advice.Argument(0) final int index, @Advice.Argument(1) final String arg, + @Advice.AllArguments final Object[] args, + @Advice.This final PreparedStatement statement) { + int index = 0; + String arg = ""; + if (args.length != 2) { + return; + } + + if (args[0] instanceof Integer) { + index = (Integer) args[0]; + } + arg = (args[1]).toString(); + + + try { + ContextStore contextStore = InstrumentationContext.get(Statement.class, DBQueryInfo.class); + if (contextStore == null) { + return; + } + + DBQueryInfo queryInfo = contextStore.get(statement); + if (null == queryInfo) { + logMissingQueryInfo(statement); + return; + } + queryInfo.setVal(index, arg); + contextStore.put(statement, queryInfo); + } catch (SQLException e) { + return; + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Thrown final Throwable throwable) { + } } public static class PreparedStatementAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static AgentScope onEnter(@Advice.This final Statement statement) { - int depth = CallDepthThreadLocalMap.incrementCallDepth(Statement.class); - if (depth > 0) { - return null; - } + try { Connection connection = statement.getConnection(); DBQueryInfo queryInfo = diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java index 4a1bb04594d..0f557169254 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java @@ -68,6 +68,12 @@ public DBMCompatibleConnectionInstrumentation() { "org.mariadb.jdbc.Connection", // aws-mysql-jdbc "software.aws.rds.jdbc.mysql.shading.com.mysql.cj.jdbc.ConnectionImpl", + // aws-mysql-jdbc + "software.aws.rds.jdbc.mysql.shading.com.mysql.cj.jdbc.ConnectionImpl", + // IBM Informix + "com.informix.jdbc.IfmxConnection", + // 达梦 DB + "dm.jdbc.driver.DmdbConnection" }; @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DefaultConnectionInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DefaultConnectionInstrumentation.java index 16ff9c654b4..3798540f27f 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DefaultConnectionInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DefaultConnectionInstrumentation.java @@ -42,7 +42,10 @@ public class DefaultConnectionInstrumentation extends AbstractConnectionInstrume // IBM Informix "com.informix.jdbc.IfmxConnection", // for testing purposes - "test.TestConnection" + "test.TestConnection", + // 达梦db + "dm.jdbc.driver.DmdbConnection" + }; @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 1adb8db16cf..b8d4198e2f2 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -1,5 +1,6 @@ package datadog.trace.instrumentation.jdbc; + import static datadog.trace.bootstrap.instrumentation.api.Tags.DB_OPERATION; import datadog.trace.api.Config; @@ -18,8 +19,7 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Statement; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,7 +77,7 @@ public static void logSQLException(SQLException ex) { @Override protected String[] instrumentationNames() { - return new String[] {"jdbc"}; + return new String[]{"jdbc"}; } @Override @@ -206,6 +206,19 @@ private AgentSpan withQueryInfo(AgentSpan span, DBQueryInfo info, CharSequence c if (null != info) { span.setResourceName(info.getSql()); span.setTag(DB_OPERATION, info.getOperation()); + String originSlq = info.getOriginSql().toString(); + if (!originSlq.equals("")) { + Map map = info.getVals(); + StringBuilder sb = new StringBuilder(); + int index = 0; + for (int key : map.keySet()) { + index++; + sb.append(key+"="+map.get(key)+", "); + if (index>=256){break;} + } + span.setTag("sql.params" ,sb.toString()); + span.setTag("db.sql.origin", originSlq); + } } else { span.setResourceName(DB_QUERY); } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java index 38d818b2676..ed5095ca301 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -118,7 +118,11 @@ public final class PreparedStatementInstrumentation extends AbstractPreparedStat // IBM Informix "com.informix.jdbc.IfxPreparedStatement", // for testing purposes - "test.TestPreparedStatement" + "test.TestPreparedStatement", + // 达梦DB + "dm.jdbc.driver.DmdbPreparedStatement", + "dm.jdbc.driver.DmdbCallableStatement", + "dm.jdbc.driver.DmdbStatement" }; @Override diff --git a/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisClientDecorator.java b/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisClientDecorator.java index f85167f6ba2..a3e34865fc0 100644 --- a/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisClientDecorator.java +++ b/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisClientDecorator.java @@ -1,14 +1,20 @@ package datadog.trace.instrumentation.jedis; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; -import redis.clients.jedis.Connection; +import redis.clients.jedis.Protocol; -public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorator { - private static final String REDIS = "redis"; +public class JedisClientDecorator + extends DBTypeProcessingDatabaseClientDecorator { public static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); + public static final CharSequence REDIS_COMMAND = UTF8BytesString.create("redis.command"); + + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); + private static final String REDIS = "redis"; public static final CharSequence OPERATION_NAME = UTF8BytesString.create(SpanNaming.instance().namingSchema().cache().operation(REDIS)); private static final String SERVICE_NAME = @@ -17,12 +23,12 @@ public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorato @Override protected String[] instrumentationNames() { - return new String[] {"jedis", REDIS}; + return new String[] {"jedis", "redis"}; } @Override protected String service() { - return SERVICE_NAME; + return "redis"; } @Override @@ -37,21 +43,28 @@ protected CharSequence spanType() { @Override protected String dbType() { - return REDIS; + return "redis"; } @Override - protected String dbUser(final Connection connection) { + protected String dbUser(final Protocol.Command session) { return null; } @Override - protected String dbInstance(final Connection connection) { + protected String dbInstance(final Protocol.Command session) { return null; } @Override - protected String dbHostname(Connection connection) { - return connection.getHost(); + protected String dbHostname(Protocol.Command command) { + return null; + } + + public AgentSpan setRaw(AgentSpan span, String raw) { + if (RedisCommandRaw){ + span.setTag("redis.command.args",raw); + } + return span; } } diff --git a/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java b/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java index fbc6a4d28ed..be652a6859b 100644 --- a/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java +++ b/dd-java-agent/instrumentation/jedis-1.4/src/main/java/datadog/trace/instrumentation/jedis/JedisInstrumentation.java @@ -5,25 +5,25 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.instrumentation.jedis.JedisClientDecorator.DECORATE; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static datadog.trace.instrumentation.jedis.JedisClientDecorator.REDIS_COMMAND; +import static net.bytebuddy.matcher.ElementMatchers.*; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatcher; -import redis.clients.jedis.Connection; import redis.clients.jedis.Protocol.Command; @AutoService(InstrumenterModule.class) public final class JedisInstrumentation extends InstrumenterModule.Tracing implements Instrumenter.ForSingleType { + private static final String SERVICE_NAME = "redis"; + private static final String COMPONENT_NAME = SERVICE_NAME + "-command"; + public JedisInstrumentation() { super("jedis", "redis"); } @@ -36,13 +36,13 @@ public ElementMatcher classLoaderMatcher() { @Override public String instrumentedType() { - return "redis.clients.jedis.Connection"; + return "redis.clients.jedis.Protocol"; } @Override public String[] helperClassNames() { return new String[] { - packageName + ".JedisClientDecorator", + packageName + ".JedisClientDecorator", }; } @@ -50,8 +50,9 @@ public String[] helperClassNames() { public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( isMethod() + .and(isPublic()) .and(named("sendCommand")) - .and(takesArgument(0, named("redis.clients.jedis.Protocol$Command"))), + .and(takesArgument(1, named("redis.clients.jedis.Protocol$Command"))), JedisInstrumentation.class.getName() + "$JedisAdvice"); // FIXME: This instrumentation only incorporates sending the command, not processing the result. } @@ -59,25 +60,22 @@ public void methodAdvice(MethodTransformer transformer) { public static class JedisAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope onEnter( - @Advice.Argument(0) final Command command, @Advice.This final Connection thiz) { - if (CallDepthThreadLocalMap.incrementCallDepth(Connection.class) > 0) { - return null; - } - final AgentSpan span = startSpan(JedisClientDecorator.OPERATION_NAME); + public static AgentScope onEnter(@Advice.Argument(1) final Command command, @Advice.Argument(2)final byte[][] args) { + final AgentSpan span = startSpan(REDIS_COMMAND); DECORATE.afterStart(span); - DECORATE.onConnection(span, thiz); DECORATE.onStatement(span, command.name()); + StringBuilder args1 = new StringBuilder(); + for(int i = 0; i < args.length; i++) { + args1.append(new String(args[i])); + args1.append(" "); + } + DECORATE.setRaw(span,args1.toString()); return activateSpan(span); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { - if (scope == null) { - return; - } - CallDepthThreadLocalMap.reset(Connection.class); DECORATE.onError(scope.span(), throwable); DECORATE.beforeFinish(scope.span()); scope.close(); diff --git a/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java b/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java index edaaa9278b3..e3227eff401 100644 --- a/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java +++ b/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisClientDecorator.java @@ -1,12 +1,15 @@ package datadog.trace.instrumentation.jedis30; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; -import redis.clients.jedis.Connection; +import redis.clients.jedis.commands.ProtocolCommand; -public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorator { +public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorator { + public static final CharSequence REDIS_COMMAND = UTF8BytesString.create("redis.command"); public static final JedisClientDecorator DECORATE = new JedisClientDecorator(); private static final String REDIS = "redis"; @@ -15,10 +18,11 @@ public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorato private static final String SERVICE_NAME = SpanNaming.instance().namingSchema().cache().service(REDIS); private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); @Override protected String[] instrumentationNames() { - return new String[] {"jedis", REDIS}; + return new String[] {"jedis", "redis"}; } @Override @@ -38,21 +42,27 @@ protected CharSequence spanType() { @Override protected String dbType() { - return REDIS; + return "redis"; } @Override - protected String dbUser(final Connection connection) { + protected String dbUser(final ProtocolCommand session) { return null; } @Override - protected String dbInstance(final Connection connection) { + protected String dbInstance(final ProtocolCommand session) { return null; } @Override - protected String dbHostname(final Connection connection) { - return connection.getHost(); + protected String dbHostname(ProtocolCommand protocolCommand) { + return null; + } + public AgentSpan setRaw(AgentSpan span, String raw) { + if (RedisCommandRaw){ + span.setTag("redis.command.args",raw); + } + return span; } } diff --git a/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java b/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java index 4488fea1c09..c5f968e133d 100644 --- a/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java +++ b/dd-java-agent/instrumentation/jedis-3.0/src/main/java/datadog/trace/instrumentation/jedis30/JedisInstrumentation.java @@ -4,17 +4,15 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.instrumentation.jedis30.JedisClientDecorator.DECORATE; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static datadog.trace.instrumentation.jedis30.JedisClientDecorator.REDIS_COMMAND; +import static net.bytebuddy.matcher.ElementMatchers.*; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; -import redis.clients.jedis.Connection; import redis.clients.jedis.Protocol; import redis.clients.jedis.commands.ProtocolCommand; @@ -27,23 +25,24 @@ public JedisInstrumentation() { } @Override - public String[] helperClassNames() { - return new String[] { - packageName + ".JedisClientDecorator", - }; + public String instrumentedType() { + return "redis.clients.jedis.Protocol"; } @Override - public String instrumentedType() { - return "redis.clients.jedis.Connection"; + public String[] helperClassNames() { + return new String[] { + packageName + ".JedisClientDecorator", + }; } @Override public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( isMethod() + .and(isPublic()) .and(named("sendCommand")) - .and(takesArgument(0, named("redis.clients.jedis.commands.ProtocolCommand"))), + .and(takesArgument(1, named("redis.clients.jedis.commands.ProtocolCommand"))), JedisInstrumentation.class.getName() + "$JedisAdvice"); // FIXME: This instrumentation only incorporates sending the command, not processing the result. } @@ -51,15 +50,10 @@ public void methodAdvice(MethodTransformer transformer) { public static class JedisAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope onEnter( - @Advice.Argument(0) final ProtocolCommand command, @Advice.This final Connection thiz) { - if (CallDepthThreadLocalMap.incrementCallDepth(Connection.class) > 0) { - return null; - } - final AgentSpan span = startSpan(JedisClientDecorator.OPERATION_NAME); + public static AgentScope onEnter(@Advice.Argument(1) final ProtocolCommand command, + @Advice.Argument(2)final byte[][] args ) { + final AgentSpan span = startSpan(REDIS_COMMAND); DECORATE.afterStart(span); - DECORATE.onConnection(span, thiz); - if (command instanceof Protocol.Command) { DECORATE.onStatement(span, ((Protocol.Command) command).name()); } else { @@ -67,16 +61,19 @@ public static AgentScope onEnter( // us if that changes DECORATE.onStatement(span, new String(command.getRaw())); } + StringBuilder args1 = new StringBuilder(); + for(int i = 0; i < args.length; i++) { + args1.append(new String(args[i])); + args1.append(" "); + } + + DECORATE.setRaw(span,args1.toString()); return activateSpan(span); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { - if (scope == null) { - return; - } - CallDepthThreadLocalMap.reset(Connection.class); DECORATE.onError(scope.span(), throwable); DECORATE.beforeFinish(scope.span()); scope.close(); diff --git a/dd-java-agent/instrumentation/jedis-4.0/src/main/java/datadog/trace/instrumentation/jedis40/JedisInstrumentation.java b/dd-java-agent/instrumentation/jedis-4.0/src/main/java/datadog/trace/instrumentation/jedis40/JedisInstrumentation.java index 6f50947d7e7..e9d42611232 100644 --- a/dd-java-agent/instrumentation/jedis-4.0/src/main/java/datadog/trace/instrumentation/jedis40/JedisInstrumentation.java +++ b/dd-java-agent/instrumentation/jedis-4.0/src/main/java/datadog/trace/instrumentation/jedis40/JedisInstrumentation.java @@ -14,10 +14,12 @@ import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import net.bytebuddy.asm.Advice; +import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.JedisClientDecorator; import redis.clients.jedis.Protocol; +import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; @AutoService(InstrumenterModule.class) @@ -61,7 +63,12 @@ public static AgentScope onEnter( DECORATE.onConnection(span, thiz); final ProtocolCommand command = commandObject.getArguments().getCommand(); - + final CommandArguments args = commandObject.getArguments(); + StringBuilder args1 = new StringBuilder(); + for (Rawable arg : args) { + args1.append(new String(arg.getRaw())); + } + DECORATE.setRaw(span, args1.toString()); if (command instanceof Protocol.Command) { DECORATE.onStatement(span, ((Protocol.Command) command).name()); } else { diff --git a/dd-java-agent/instrumentation/jedis-4.0/src/main/java/redis/clients/jedis/JedisClientDecorator.java b/dd-java-agent/instrumentation/jedis-4.0/src/main/java/redis/clients/jedis/JedisClientDecorator.java index ccbfc3d3418..2cd24a08552 100644 --- a/dd-java-agent/instrumentation/jedis-4.0/src/main/java/redis/clients/jedis/JedisClientDecorator.java +++ b/dd-java-agent/instrumentation/jedis-4.0/src/main/java/redis/clients/jedis/JedisClientDecorator.java @@ -1,6 +1,8 @@ package redis.clients.jedis; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; @@ -14,6 +16,7 @@ public class JedisClientDecorator extends DBTypeProcessingDatabaseClientDecorato private static final String SERVICE_NAME = SpanNaming.instance().namingSchema().cache().service(REDIS); private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); @Override protected String[] instrumentationNames() { @@ -55,4 +58,11 @@ protected String dbHostname(Connection connection) { // getHostAndPort is protected hence the decorator sits in the same package return connection.getHostAndPort().getHost(); } + + public AgentSpan setRaw(AgentSpan span, String raw) { + if (RedisCommandRaw){ + span.setTag("redis.command.raw",raw); + } + return span; + } } diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle b/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle index 7a7a20b6783..8880753e3a0 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle @@ -36,7 +36,7 @@ dependencies { // Include latest version of kafka itself along with latest version of client libs. // This seems to help with jar compatibility hell. latestDepTestImplementation group: 'org.apache.kafka', name: 'kafka_2.13', version: '2.+' - latestDepTestImplementation group: 'org.apache.kafka', name: 'kafka-clients', version: '2.+' + // latestDepTestImplementation group: 'org.apache.kafka', name: 'kafka-clients', version: '2.+' latestDepTestImplementation group: 'org.springframework.kafka', name: 'spring-kafka', version: '2.+' latestDepTestImplementation group: 'org.springframework.kafka', name: 'spring-kafka-test', version: '2.+' latestDepTestImplementation group: 'org.assertj', name: 'assertj-core', version: '3.19.+' diff --git a/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/InstrumentationPoints.java b/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/InstrumentationPoints.java index c1b4df423b2..3789af8c3f8 100644 --- a/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/InstrumentationPoints.java +++ b/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/InstrumentationPoints.java @@ -17,8 +17,11 @@ import com.lambdaworks.redis.protocol.CommandType; import com.lambdaworks.redis.protocol.ProtocolKeyword; import com.lambdaworks.redis.protocol.RedisCommand; +import datadog.trace.api.Config; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; + +import javax.swing.plaf.synth.SynthOptionPaneUI; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.CancellationException; @@ -38,6 +41,10 @@ public static AgentScope beforeCommand( DECORATE.afterStart(span); DECORATE.onConnection(span, redisURI); DECORATE.onCommand(span, command); + if (command.getArgs()!=null){ + String args = command.getArgs().toString(); + DECORATE.setArgs(span,args); + } return activateSpan(span); } diff --git a/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/LettuceClientDecorator.java b/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/LettuceClientDecorator.java index 31b8c2e67e2..2b8d8c7b8fc 100644 --- a/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/LettuceClientDecorator.java +++ b/dd-java-agent/instrumentation/lettuce-4/src/main/java/datadog/trace/instrumentation/lettuce4/LettuceClientDecorator.java @@ -4,6 +4,7 @@ import com.lambdaworks.redis.RedisURI; import com.lambdaworks.redis.protocol.RedisCommand; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; @@ -13,6 +14,7 @@ public class LettuceClientDecorator extends DBTypeProcessingDatabaseClientDecorator { public static final CharSequence REDIS_CLIENT = UTF8BytesString.create("redis-client"); + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); public static final CharSequence OPERATION_NAME = UTF8BytesString.create(SpanNaming.instance().namingSchema().cache().operation("redis")); @@ -83,4 +85,11 @@ public String resourceNameForConnection(final RedisURI redisURI) { + "/" + redisURI.getDatabase(); } + + public AgentSpan setArgs(final AgentSpan span,String raw){ + if (RedisCommandRaw){ + span.setTag("redis.command.args",raw); + } + return span; + } } diff --git a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceAsyncCommandsAdvice.java b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceAsyncCommandsAdvice.java index 2f18d9df582..96828de6e5f 100644 --- a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceAsyncCommandsAdvice.java +++ b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceAsyncCommandsAdvice.java @@ -1,10 +1,6 @@ package datadog.trace.instrumentation.lettuce5; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.instrumentation.lettuce5.LettuceClientDecorator.DECORATE; -import static datadog.trace.instrumentation.lettuce5.LettuceInstrumentationUtil.expectsResponse; - +import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.InstrumentationContext; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -15,19 +11,19 @@ import io.lettuce.core.protocol.RedisCommand; import net.bytebuddy.asm.Advice; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.lettuce5.LettuceClientDecorator.DECORATE; +import static datadog.trace.instrumentation.lettuce5.LettuceInstrumentationUtil.expectsResponse; + public class LettuceAsyncCommandsAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static AgentScope onEnter( @Advice.Argument(0) final RedisCommand command, @Advice.This final AbstractRedisAsyncCommands thiz) { - final AgentSpan span = startSpan(LettuceClientDecorator.OPERATION_NAME); DECORATE.afterStart(span); - DECORATE.onConnection( - span, - InstrumentationContext.get(StatefulConnection.class, RedisURI.class) - .get(thiz.getConnection())); DECORATE.onCommand(span, command); return activateSpan(span); @@ -36,11 +32,19 @@ public static AgentScope onEnter( @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Argument(0) final RedisCommand command, + @Advice.This final AbstractRedisAsyncCommands thiz, @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable, @Advice.Return AsyncCommand asyncCommand) { - final AgentSpan span = scope.span(); + ContextStore store = InstrumentationContext.get(StatefulConnection.class, RedisURI.class); + RedisURI info = store.get(thiz.getConnection()); + if (info != null) { + DECORATE.onConnection( + span, + info); + } + if (throwable != null) { DECORATE.onError(span, throwable); DECORATE.beforeFinish(span); diff --git a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceClientDecorator.java b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceClientDecorator.java index a8442a1f320..9000814372b 100644 --- a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceClientDecorator.java +++ b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceClientDecorator.java @@ -1,11 +1,13 @@ package datadog.trace.instrumentation.lettuce5; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; import io.lettuce.core.RedisURI; +import io.lettuce.core.protocol.CommandArgs; import io.lettuce.core.protocol.RedisCommand; public class LettuceClientDecorator extends DBTypeProcessingDatabaseClientDecorator { @@ -16,6 +18,7 @@ public class LettuceClientDecorator extends DBTypeProcessingDatabaseClientDecora private static final String SERVICE_NAME = SpanNaming.instance().namingSchema().cache().service("redis"); + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); @Override protected String[] instrumentationNames() { return new String[] {"lettuce"}; @@ -67,6 +70,10 @@ public AgentSpan onConnection(final AgentSpan span, final RedisURI connection) { } public AgentSpan onCommand(final AgentSpan span, final RedisCommand command) { + if (command.getArgs()!=null && RedisCommandRaw){ + CommandArgs args = command.getArgs(); + span.setTag("redis.command.args",args.toCommandString()); + } final String commandName = LettuceInstrumentationUtil.getCommandName(command); span.setResourceName(LettuceInstrumentationUtil.getCommandResourceName(commandName)); return span; diff --git a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointAdvice.java b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointAdvice.java new file mode 100644 index 00000000000..5ecfdee543c --- /dev/null +++ b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointAdvice.java @@ -0,0 +1,36 @@ +package datadog.trace.instrumentation.lettuce5; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.Tags; +import io.netty.channel.Channel; +import net.bytebuddy.asm.Advice; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; + +public class LettuceDefaultEndpointAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentSpan onEnter( + @Advice.This Object obj,@Advice.FieldValue("channel") Channel channel + ) { + if (channel == null) { + return activeSpan(); + } + AgentSpan span = activeSpan(); + + SocketAddress socketAddress = channel.remoteAddress(); + if (socketAddress instanceof InetSocketAddress) { + InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; +// String peer = inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); + if (span!=null){ +// span.setTag("peer",peer); + span.setTag(Tags.PEER_HOSTNAME,inetSocketAddress.getHostName()); + span.setTag(Tags.PEER_PORT,inetSocketAddress.getPort()); + } + } + return activeSpan(); + } +} diff --git a/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointInstrumentation.java b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointInstrumentation.java new file mode 100644 index 00000000000..79b2f7c3d8e --- /dev/null +++ b/dd-java-agent/instrumentation/lettuce-5/src/main/java/datadog/trace/instrumentation/lettuce5/LettuceDefaultEndpointInstrumentation.java @@ -0,0 +1,30 @@ +package datadog.trace.instrumentation.lettuce5; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +@AutoService(Instrumenter.class) +public class LettuceDefaultEndpointInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public LettuceDefaultEndpointInstrumentation() { + super("lettuce"); + } + + @Override + public String instrumentedType() { + return "io.lettuce.core.protocol.DefaultEndpoint"; + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod() + .and(named("write")), + packageName + ".LettuceDefaultEndpointAdvice"); + + } +} diff --git a/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutBuildAdvice.java b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutBuildAdvice.java new file mode 100644 index 00000000000..1e06c173025 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutBuildAdvice.java @@ -0,0 +1,38 @@ +package datadog.trace.instrumentation.log4j27; + +import datadog.trace.api.Config; +import net.bytebuddy.asm.Advice; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; + +/** + * @Description + * @Author liurui + * @Date 2022/12/8 10:16 + */ +public class PatternLayoutBuildAdvice { + + public static final Logger logger = LoggerFactory.getLogger(PatternLayoutBuildAdvice.class); + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Object obj,@Advice.FieldValue("pattern") String eventPattern) { + String pattern = Config.get().getLogPattern(); + logger.debug("origin pattern:{},new pattern:{}",eventPattern,pattern); + try { + Field field = PatternLayout.Builder.class.getDeclaredField("pattern"); + field.setAccessible(true); + field.set(obj, pattern); + logger.debug("update pattern:{}",pattern); + }catch (Exception e){ + logger.error("pattern update exception:",e); + } + + } + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.FieldValue("pattern") String pattern) { + logger.debug("exit pattern:{}",pattern); + } +} diff --git a/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutInstrumentation.java b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutInstrumentation.java new file mode 100644 index 00000000000..5d6b2314cb0 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/PatternLayoutInstrumentation.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package datadog.trace.instrumentation.log4j27; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.Config; + +@AutoService(Instrumenter.class) +public class PatternLayoutInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public PatternLayoutInstrumentation() { + super("log4j", "log4j-2"); + } + + @Override + protected boolean defaultEnabled() { + return Config.get().isLogPatternReplace(); + } + + @Override + public String instrumentedType() { + return "org.apache.logging.log4j.core.layout.PatternLayout$Builder"; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(named("build")) + .and(takesArguments(0)) + , + packageName + ".PatternLayoutBuildAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".PatternLayoutBuildAdvice" + }; + } +} diff --git a/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/SpanDecoratingContextDataInjector.java b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/SpanDecoratingContextDataInjector.java index b5d685a3690..4093d70dd78 100644 --- a/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/SpanDecoratingContextDataInjector.java +++ b/dd-java-agent/instrumentation/log4j-2.7/src/main/java/datadog/trace/instrumentation/log4j27/SpanDecoratingContextDataInjector.java @@ -4,23 +4,20 @@ */ package datadog.trace.instrumentation.log4j27; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig; - -import datadog.trace.api.Config; -import datadog.trace.api.CorrelationIdentifier; -import datadog.trace.api.DDSpanId; -import datadog.trace.api.DDTraceId; -import datadog.trace.api.InstrumenterConfig; +import datadog.trace.api.*; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.Tags; -import java.util.List; import org.apache.logging.log4j.core.ContextDataInjector; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.SortedArrayStringMap; import org.apache.logging.log4j.util.StringMap; +import java.util.List; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig; + public final class SpanDecoratingContextDataInjector implements ContextDataInjector { private final ContextDataInjector delegate; @@ -31,7 +28,6 @@ public SpanDecoratingContextDataInjector(ContextDataInjector delegate) { @Override public StringMap injectContextData(List list, StringMap reusable) { StringMap contextData = delegate.injectContextData(list, reusable); - AgentSpan span = activeSpan(); if (!traceConfig(span).isLogsInjectionEnabled()) { @@ -53,7 +49,6 @@ public StringMap injectContextData(List list, StringMap reusable) { if (null != version && !version.isEmpty()) { newContextData.putValue(Tags.DD_VERSION, version); } - if (span != null) { DDTraceId traceId = span.context().getTraceId(); String traceIdValue = diff --git a/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutBuildAdvice.java b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutBuildAdvice.java new file mode 100644 index 00000000000..0c398b9dbe7 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutBuildAdvice.java @@ -0,0 +1,39 @@ +package datadog.trace.instrumentation.log4j2; + +import datadog.trace.api.Config; +import net.bytebuddy.asm.Advice; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; + +/** + * @Description + * @Author liurui + * @Date 2022/12/9 12:16 + */ +public class PatternLayoutBuildAdvice { + + public static final Logger logger = LoggerFactory.getLogger(PatternLayoutBuildAdvice.class); + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Object obj,@Advice.FieldValue("pattern") String eventPattern) { + String pattern = Config.get().getLogPattern(); + logger.debug("origin pattern:{},new pattern:{}",eventPattern,pattern); + try { + + Field field = PatternLayout.Builder.class.getDeclaredField("pattern"); + field.setAccessible(true); + field.set(obj, pattern); + logger.debug("update pattern:{}",pattern); + }catch (Exception e){ + logger.error("pattern update exception:",e); + } + + } + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.FieldValue("pattern") String pattern) { + logger.debug("exit pattern:{}",pattern); + } +} diff --git a/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutInstrumentation.java b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutInstrumentation.java new file mode 100644 index 00000000000..3d02733fc57 --- /dev/null +++ b/dd-java-agent/instrumentation/log4j2/src/main/java/datadog/trace/instrumentation/log4j2/PatternLayoutInstrumentation.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package datadog.trace.instrumentation.log4j2; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.Config; + +@AutoService(Instrumenter.class) +public class PatternLayoutInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public PatternLayoutInstrumentation() { + super("log4j", "log4j-2"); + } + + @Override + protected boolean defaultEnabled() { + return Config.get().isLogPatternReplace(); + } + + @Override + public String instrumentedType() { + return "org.apache.logging.log4j.core.layout.PatternLayout$Builder"; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(named("build")) + .and(takesArguments(0)) + , + packageName + ".PatternLayoutBuildAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".PatternLayoutBuildAdvice" + }; + } +} diff --git a/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java b/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java index 71468d0f05e..c4d1f623ad0 100644 --- a/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java +++ b/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java @@ -16,6 +16,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import com.google.auto.service.AutoService; +import datadog.trace.api.TraceConfig; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; import datadog.trace.bootstrap.InstrumentationContext; @@ -57,10 +58,13 @@ public static class CallAppendersAdvice { @Advice.OnMethodEnter public static void onEnter(@Advice.Argument(0) ILoggingEvent event) { AgentSpan span = activeSpan(); + if (span != null) { + TraceConfig traceConfig = span.traceConfig(); - if (span != null && traceConfig(span).isLogsInjectionEnabled()) { - InstrumentationContext.get(ILoggingEvent.class, AgentSpan.Context.class) - .put(event, span.context()); + if (traceConfig != null && traceConfig.isLogsInjectionEnabled()) { + InstrumentationContext.get(ILoggingEvent.class, AgentSpan.Context.class) + .put(event, span.context()); + } } } } diff --git a/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java b/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java index 6de90eb28d3..9414164d742 100644 --- a/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java +++ b/dd-java-agent/instrumentation/logback-1/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java @@ -2,6 +2,7 @@ import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; import static java.util.Collections.singletonMap; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; @@ -96,6 +97,14 @@ public static void onExit( correlationValues.put(CorrelationIdentifier.getTraceIdKey(), traceIdValue); correlationValues.put( CorrelationIdentifier.getSpanIdKey(), DDSpanId.toString(context.getSpanId())); + }else{ + AgentSpan span = activeSpan(); + if (span!=null){ + correlationValues.put( + CorrelationIdentifier.getTraceIdKey(), span.getTraceId().toString()); + correlationValues.put( + CorrelationIdentifier.getSpanIdKey(), DDSpanId.toString(span.getSpanId())); + } } String serviceName = Config.get().getServiceName(); diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber31.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber31.java index c6b5d623df0..484fb1af19d 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber31.java +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber31.java @@ -1,6 +1,8 @@ package datadog.trace.instrumentation.mongo; import java.util.Map; + +import datadog.trace.api.Config; import org.bson.BsonArray; import org.bson.BsonBinary; import org.bson.BsonDbPointer; @@ -27,6 +29,8 @@ protected Context initialValue() { private final Context context; private boolean obfuscate = true; + public boolean mongoObfuscation = Config.get().getMongoObfuscation(); + public BsonScrubber31() { this.context = CONTEXT.get(); } @@ -41,6 +45,10 @@ public String getResourceName() { } private void applyObfuscationPolicy(String name) { + if (mongoObfuscation){ + obfuscate = false; + return; + } if (null != name && !context.ignoreSubTree()) { switch (name) { case "documents": @@ -133,7 +141,11 @@ public void writeDBPointer(String name, BsonDbPointer value) { @Override public void writeDouble(double value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(""+value); + }else { + writeObfuscated(); + } } @Override @@ -155,7 +167,9 @@ public void writeEndDocument() { @Override public void writeInt32(int value) { - if (obfuscate) { + if(mongoObfuscation){ + context.write(value); + }else if (obfuscate) { writeObfuscated(); } else { context.write(value); @@ -170,7 +184,11 @@ public void writeInt32(String name, int value) { @Override public void writeInt64(long value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(value); + }else { + writeObfuscated(); + } } @Override @@ -323,7 +341,11 @@ public void writeSymbol(String name, String value) { @Override public void writeTimestamp(BsonTimestamp value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(value.toString()); + }else { + writeObfuscated(); + } } @Override diff --git a/dd-java-agent/instrumentation/mongo/driver-3.4/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber34.java b/dd-java-agent/instrumentation/mongo/driver-3.4/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber34.java index 26afed96a47..9ac4082df49 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.4/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber34.java +++ b/dd-java-agent/instrumentation/mongo/driver-3.4/src/main/java/datadog/trace/instrumentation/mongo/BsonScrubber34.java @@ -1,6 +1,8 @@ package datadog.trace.instrumentation.mongo; import java.util.Map; + +import datadog.trace.api.Config; import org.bson.BsonArray; import org.bson.BsonBinary; import org.bson.BsonDbPointer; @@ -28,6 +30,8 @@ protected Context initialValue() { private final Context context = CONTEXT.get(); private boolean obfuscate = true; + public boolean mongoObfuscation = Config.get().getMongoObfuscation(); + @Override public void close() { context.clear(); @@ -38,6 +42,10 @@ public String getResourceName() { } private void applyObfuscationPolicy(String name) { + if (mongoObfuscation){ + obfuscate = false; + return; + } if (null != name && !context.ignoreSubTree()) { switch (name) { case "documents": @@ -108,7 +116,11 @@ public void writeBoolean(String name, boolean value) { @Override public void writeDateTime(long value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(value); + }else { + writeObfuscated(); + } } @Override @@ -130,7 +142,11 @@ public void writeDBPointer(String name, BsonDbPointer value) { @Override public void writeDouble(double value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(""+value); + }else { + writeObfuscated(); + } } @Override @@ -153,7 +169,9 @@ public void writeEndDocument() { @Override public void writeInt32(int value) { - if (obfuscate) { + if (mongoObfuscation){ + context.write(value); + } else if (obfuscate) { writeObfuscated(); } else { context.write(value); @@ -168,7 +186,13 @@ public void writeInt32(String name, int value) { @Override public void writeInt64(long value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(value); + }else if (obfuscate) { + writeObfuscated(); + }else { + context.write(value); + } } @Override @@ -332,7 +356,11 @@ public void writeSymbol(String name, String value) { @Override public void writeTimestamp(BsonTimestamp value) { - writeObfuscated(); + if (mongoObfuscation){ + context.write(value.toString()); + }else { + writeObfuscated(); + } } @Override diff --git a/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerRequestTracingHandler.java b/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerRequestTracingHandler.java index 2b769b55a82..ccc48978eb3 100644 --- a/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerRequestTracingHandler.java +++ b/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerRequestTracingHandler.java @@ -7,6 +7,8 @@ import static datadog.trace.instrumentation.netty41.AttributeKeys.SPAN_ATTRIBUTE_KEY; import static datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator.DECORATE; +import datadog.trace.api.Config; +import datadog.trace.api.GlobalTracer; import datadog.trace.api.gateway.Flow; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -18,6 +20,10 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + @ChannelHandler.Sharable public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter { public static HttpServerRequestTracingHandler INSTANCE = new HttpServerRequestTracingHandler(); @@ -43,6 +49,8 @@ public void channelRead(final ChannelHandlerContext ctx, final Object msg) { final Context.Extracted extractedContext = DECORATE.extract(headers); final AgentSpan span = DECORATE.startSpan(headers, extractedContext); + addTag(span,headers); + try (final AgentScope scope = activateSpan(span)) { DECORATE.afterStart(span); DECORATE.onRequest(span, channel, request, extractedContext); @@ -80,4 +88,25 @@ public void channelRead(final ChannelHandlerContext ctx, final Object msg) { } } } + + private void addTag(AgentSpan span,HttpHeaders headers){ + StringBuffer requestHeader = new StringBuffer(""); + boolean tracerHeader = Config.get().isTracerHeaderEnabled(); + if (tracerHeader) { + int count = 0; + for (Map.Entry entry : headers.entries()) { + if (count==0){ + requestHeader.append("{"); + }else{ + requestHeader.append(","); + } + requestHeader.append("\n\"").append(entry.getKey()).append("\":").append("\"").append(entry.getValue().replace("\"","")).append("\""); + count ++; + } + if (count>0){ + requestHeader.append("}"); + } + } + span.setTag("request_header",requestHeader.toString()); + } } diff --git a/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerResponseTracingHandler.java b/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerResponseTracingHandler.java index 04035114f52..2d2bf80f166 100644 --- a/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerResponseTracingHandler.java +++ b/dd-java-agent/instrumentation/netty-4.1/src/main/java/datadog/trace/instrumentation/netty41/server/HttpServerResponseTracingHandler.java @@ -4,6 +4,7 @@ import static datadog.trace.instrumentation.netty41.AttributeKeys.SPAN_ATTRIBUTE_KEY; import static datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator.DECORATE; +import datadog.trace.api.Config; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import io.netty.channel.ChannelHandler; @@ -11,9 +12,12 @@ import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; +import java.util.Map; + @ChannelHandler.Sharable public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdapter { public static HttpServerResponseTracingHandler INSTANCE = new HttpServerResponseTracingHandler(); @@ -29,7 +33,8 @@ public void write(final ChannelHandlerContext ctx, final Object msg, final Chann try (final AgentScope scope = activateSpan(span)) { final HttpResponse response = (HttpResponse) msg; - + span.setTag("guance_trace_id",span.getTraceId().toString()); + addTag(span,response.headers()); try { ctx.write(msg, prm); } catch (final Throwable throwable) { @@ -47,4 +52,25 @@ public void write(final ChannelHandlerContext ctx, final Object msg, final Chann } } } + + private void addTag(AgentSpan span, HttpHeaders headers){ + StringBuffer responseHeader = new StringBuffer(""); + boolean tracerHeader = Config.get().isTracerHeaderEnabled(); + if (tracerHeader) { + int count = 0; + for (Map.Entry entry : headers.entries()) { + if (count==0){ + responseHeader.append("{"); + }else{ + responseHeader.append(",\n"); + } + responseHeader.append("\"").append(entry.getKey()).append("\":").append("\"").append(entry.getValue().replace("\"","")).append("\""); + count ++; + } + if (count>0){ + responseHeader.append("}"); + } + } + span.setTag("response_header",responseHeader.toString()); + } } diff --git a/dd-java-agent/instrumentation/ons-client/build.gradle b/dd-java-agent/instrumentation/ons-client/build.gradle new file mode 100644 index 00000000000..f8857ff7e4d --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/build.gradle @@ -0,0 +1,18 @@ +muzzle { + pass { + group = "com.aliyun.openservices" + module = "ons-client" + versions = "[1.8.0.Final,1.8.8.7.Final)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +dependencies { + compileOnly group: 'com.aliyun.openservices', name: 'ons-client', version: '1.8.0.Final' +} + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ExtractAdapter.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ExtractAdapter.java new file mode 100644 index 00000000000..e9e7bd251d8 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ExtractAdapter.java @@ -0,0 +1,23 @@ +package datadog.trace.instrumentation.ons_client; + +import com.aliyun.openservices.ons.api.Message; +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; + +import java.util.Map; +import java.util.Properties; + +public class ExtractAdapter implements AgentPropagation.ContextVisitor{ + public static final ExtractAdapter GETTER = new ExtractAdapter(); + @Override + public void forEachKey(Message carrier, AgentPropagation.KeyClassifier classifier) { + Properties properties = carrier.getUserProperties(); + for (Map.Entry entry : properties.entrySet()){ + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey().toString(), entry.getValue().toString())) { + return; + } + } + } + + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/InjectAdapter.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/InjectAdapter.java new file mode 100644 index 00000000000..3f54876a063 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/InjectAdapter.java @@ -0,0 +1,14 @@ +package datadog.trace.instrumentation.ons_client; + +import com.aliyun.openservices.ons.api.Message; +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; + +public class InjectAdapter implements AgentPropagation.Setter{ + + public static final InjectAdapter SETTER = new InjectAdapter(); + + @Override + public void set(Message carrier, String key, String value) { + carrier.putUserProperties(key,value); + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqBatchInstrumentation.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqBatchInstrumentation.java new file mode 100644 index 00000000000..b778b990d4d --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqBatchInstrumentation.java @@ -0,0 +1,65 @@ +package datadog.trace.instrumentation.ons_client; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.instrumentation.ons_client.MqDecorator.DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.aliyun.openservices.ons.api.Message; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import java.util.List; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class MqBatchInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + public static final String CLASS_NAME = "com.aliyun.openservices.ons.api.batch.BatchMessageListener"; + + public MqBatchInstrumentation() {super("rocketmq", "ons-client");} + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".MqDecorator", + packageName + ".ExtractAdapter", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod(). + and(named("consume")). + and(takesArguments(2)), + MqBatchInstrumentation.class.getName() + "$AdviceStart"); + } + + public static class AdviceStart { + @Advice.OnMethodEnter + public static AgentScope onEnter(@Advice.Argument(0) List messages) { + return DECORATOR.OnStart(messages); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter final AgentScope scope){ + if (scope == null){ + return; + } + DECORATOR.OnEnd(scope); + } + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqDecorator.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqDecorator.java new file mode 100644 index 00000000000..6959c3fd877 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqDecorator.java @@ -0,0 +1,94 @@ +package datadog.trace.instrumentation.ons_client; + +import com.aliyun.openservices.ons.api.Message; +import datadog.trace.api.Config; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; + +import java.util.List; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.ons_client.ExtractAdapter.GETTER; + +public class MqDecorator extends BaseDecorator { + public static final CharSequence ROCKETMQ_NAME = UTF8BytesString.create("rocketmq"); + private static final String MESSAGE_ID = "message.id"; + public static final CharSequence MESSAGE_LISTENER = UTF8BytesString.create("messageListener"); + private static final String LOCAL_SERVICE_NAME = Config.get().getServiceName(); + private static final String MESSAGING_ROCKETMQ_BROKER_ADDRESS = "messaging.broker_address"; + private static final String MESSAGE_TAG = "message.tag"; + private static final String MESSAGE_LEN = "message_len"; + public static final MqDecorator DECORATOR= new MqDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{"ons-client","rocketmq"}; + } + + @Override + protected CharSequence spanType() { + return ROCKETMQ_NAME; + } + + @Override + protected CharSequence component() { + return MESSAGE_LISTENER; + } + + public AgentScope OnStart(Message message) { + AgentSpan.Context parentContext = propagate().extract(message,GETTER); + String topic = message.getTopic(); + UTF8BytesString spanName = UTF8BytesString.create("producer send"); + AgentSpan span; + if (parentContext == null) { + span = startSpan(spanName); + }else { + span = startSpan(spanName,parentContext); + } + + span.setTag("topic",topic); + span.setServiceName("ons-client"); + span.setResourceName("consumer"); + if (message.getTag() != null){ + span.setTag(MESSAGE_TAG,message.getTag()); + } + span.setTag(MESSAGE_ID,message.getMsgID()); + String key = message.getKey(); + if (key != null){ + span.setTag("key",key); + } + String brokerAddr =message.getBornHost(); + if (brokerAddr != null) { + span.setTag(MESSAGING_ROCKETMQ_BROKER_ADDRESS, brokerAddr); + } + afterStart(span); + return activateSpan(span); + } + + public AgentScope OnStart(List messages){ + // 与 OnStart(Message) 不同。这里添加一个tag:message_len + // 从message[0] 中获取parentContext + Message message = messages.get(0); + if (message == null){ + return null; + } + AgentScope scope = OnStart(message); + scope.span().setTag(MESSAGE_LEN,messages.size()); + return scope; + } + + public void OnEnd( AgentScope scope) { + beforeFinish(scope); + scope.close(); + scope.span().finish(); + } + + public void OnEnd( AgentScope scope,String status) { + scope.span().setTag("message.status",status); + beforeFinish(scope); + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqNormalInstrumentation.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqNormalInstrumentation.java new file mode 100644 index 00000000000..384f78d6cf5 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqNormalInstrumentation.java @@ -0,0 +1,70 @@ +package datadog.trace.instrumentation.ons_client; + + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.ons_client.MqDecorator.DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.aliyun.openservices.ons.api.Message; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class MqNormalInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + // 普通消息 + public static final String CLASS_NAME = "com.aliyun.openservices.ons.api.MessageListener"; + + public MqNormalInstrumentation() { + super("rocketmq", "ons-client"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".MqDecorator", + packageName + ".ExtractAdapter", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod(). + and(named("consume")). + and(takesArguments(2)), + MqNormalInstrumentation.class.getName() + "$AdviceStart"); + } + + public static class AdviceStart { + @Advice.OnMethodEnter + public static AgentScope onEnter(@Advice.Argument(0) Message message) { + return DECORATOR.OnStart(message); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter final AgentScope scope){ + if (scope == null){ + return; + } + DECORATOR.OnEnd(scope); + } + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqOrderInstrumentation.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqOrderInstrumentation.java new file mode 100644 index 00000000000..9279dc26f37 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/MqOrderInstrumentation.java @@ -0,0 +1,79 @@ +package datadog.trace.instrumentation.ons_client; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.instrumentation.ons_client.MqDecorator.DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.order.OrderAction; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class MqOrderInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + + public static final String CLASS_NAME = "com.aliyun.openservices.ons.api.order.MessageOrderListener"; + + public MqOrderInstrumentation() { + super("rocketmq", "ons-client"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".MqDecorator", + packageName + ".ExtractAdapter", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod(). + and(named("consume")). + and(takesArguments(2)), + MqOrderInstrumentation.class.getName() + "$AdviceStart"); + } + + public static class AdviceStart { + @Advice.OnMethodEnter + public static AgentScope onEnter(@Advice.Argument(0) Message message) { + return DECORATOR.OnStart(message); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter final AgentScope scope,@Advice.Return OrderAction action){ + if(scope == null){ + return; + } + String status; + switch (action){ + case Success: + status = "success"; + break; + case Suspend: + status = "suspend"; + break; + default: + status = "unknown"; + } + DECORATOR.OnEnd(scope,status); + } + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerDecorator.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerDecorator.java new file mode 100644 index 00000000000..e3c5e0db48a --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerDecorator.java @@ -0,0 +1,66 @@ +package datadog.trace.instrumentation.ons_client; + +import com.aliyun.openservices.ons.api.Message; +import datadog.trace.api.Config; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.ons_client.InjectAdapter.SETTER; + +public class ProducerDecorator extends BaseDecorator { + public static final CharSequence ROCKETMQ_NAME = UTF8BytesString.create("rocketmq"); + private static final String MESSAGE_TAG = "message.tag"; + + private static final String LOCAL_SERVICE_NAME = Config.get().getServiceName(); + public static final CharSequence PRODUCER = UTF8BytesString.create("producer send"); + private static final String MESSAGING_ROCKETMQ_BROKER_ADDRESS = "messaging.broker_address"; + + public static final ProducerDecorator PRODUCER_DECORATOR = new ProducerDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{"ons-client","rocketmq"}; + } + + @Override + protected CharSequence spanType() { + return ROCKETMQ_NAME; + } + + @Override + protected CharSequence component() { + return PRODUCER; + } + + public AgentScope OnStart(Message message){ + String topic = message.getTopic(); + AgentSpan span = startSpan(topic+" send"); + span.setResourceName(topic + " send"); + span.setServiceName("ons-client"); + if (message.getTag() != null){ + span.setTag(MESSAGE_TAG,message.getTag()); + } + String brokerAddr =message.getBornHost(); + if (brokerAddr != null) { + span.setTag(MESSAGING_ROCKETMQ_BROKER_ADDRESS, brokerAddr); + } + span.setTag("topic",topic); + //span.setTag(MESSAGE_ID,message.getMsgID()); + propagate().inject(span,message,SETTER); // 传递链路信息 + afterStart(span); + return activateSpan(span); + } + + public void OnEnd(AgentScope scope, Throwable throwable){ + if (scope == null) { + return; + } + onError(scope.span(),throwable); + beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerSendInstrumentation.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerSendInstrumentation.java new file mode 100644 index 00000000000..54e97610e6e --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/ProducerSendInstrumentation.java @@ -0,0 +1,114 @@ +package datadog.trace.instrumentation.ons_client; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.ons_client.ProducerDecorator.PRODUCER_DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.SendCallback; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class ProducerSendInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + + public static final String CLASS_NAME = "com.aliyun.openservices.ons.api.Producer"; + + public ProducerSendInstrumentation() { + super("rocketmq", "ons-client"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ExtractAdapter", + packageName + ".InjectAdapter", + packageName + ".WrappingSendCallback", + packageName + ".ProducerDecorator", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod(). + and(named("send")). + and(takesArguments(1)), + ProducerSendInstrumentation.class.getName() + "$SendAdvice"); + transformation.applyAdvice( + isMethod(). + and(named("sendOneway")). // 没有返回值 + and(takesArguments(1)), + ProducerSendInstrumentation.class.getName() + "$SendOnewayAdvice"); + transformation.applyAdvice( + isMethod(). + and(named("sendAsync")). // 没有返回值 + and(takesArguments(2)), + ProducerSendInstrumentation.class.getName() + "$sendAsyncAdvice"); + } + + public static class SendAdvice { + @Advice.OnMethodEnter + public static AgentScope onEnter(@Advice.Argument(0) Message message) { + // 有返回值 + return PRODUCER_DECORATOR.OnStart(message); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Enter final AgentScope scope, + @Advice.Thrown final Throwable throwable){ + if (scope == null){ + return; + } + PRODUCER_DECORATOR.OnEnd(scope,throwable); + } + } + + public static class SendOnewayAdvice { + @Advice.OnMethodEnter + public static AgentScope onEnter( + @Advice.Argument(0) Message message) { + return PRODUCER_DECORATOR.OnStart(message); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Enter final AgentScope scope, + @Advice.Thrown final Throwable throwable){ + if (scope == null){ + return; + } + PRODUCER_DECORATOR.OnEnd(scope,throwable); + } + } + + public static class sendAsyncAdvice { + @Advice.OnMethodEnter + public static void onEnter( + @Advice.Argument(0) Message message, + @Advice.Argument(value = 1, readOnly = false) SendCallback callback) { + // callback + AgentScope scope = PRODUCER_DECORATOR.OnStart(message); + callback = new WrappingSendCallback(callback,scope); + + } + } +} diff --git a/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/WrappingSendCallback.java b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/WrappingSendCallback.java new file mode 100644 index 00000000000..45788223b77 --- /dev/null +++ b/dd-java-agent/instrumentation/ons-client/src/main/java/datadog/trace/instrumentation/ons_client/WrappingSendCallback.java @@ -0,0 +1,33 @@ +package datadog.trace.instrumentation.ons_client; + +import com.aliyun.openservices.ons.api.OnExceptionContext; +import com.aliyun.openservices.ons.api.SendCallback; +import com.aliyun.openservices.ons.api.SendResult; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; + +public class WrappingSendCallback implements SendCallback { + private final SendCallback callback; + private final AgentScope scope; + + public WrappingSendCallback(SendCallback callback, AgentScope scope) { + this.callback = callback; + this.scope = scope; + } + + @Override + public void onSuccess(SendResult sendResult) { + scope.span().setTag("sendAsync_status","success"); + scope.close(); + scope.span().finish(); + callback.onSuccess(sendResult); + } + + @Override + public void onException(OnExceptionContext context) { + scope.span().setTag("sendAsync_status","exception"); + scope.span().addThrowable(context.getException()); + scope.close(); + scope.span().finish(); + callback.onException(context); + } +} diff --git a/dd-java-agent/instrumentation/powerjob/build.gradle b/dd-java-agent/instrumentation/powerjob/build.gradle new file mode 100644 index 00000000000..fa8e3e94437 --- /dev/null +++ b/dd-java-agent/instrumentation/powerjob/build.gradle @@ -0,0 +1,18 @@ + + +muzzle { + pass { + group = "tech.powerjob" + module = "powerjob-worker" + versions = "[4.0.0,)" +// assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + + +dependencies { + compileOnly(group: 'tech.powerjob', name: 'powerjob-worker', version: '4.0.0') +} + diff --git a/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/BasicProcessorInstrumentation.java b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/BasicProcessorInstrumentation.java new file mode 100644 index 00000000000..79deae27bbe --- /dev/null +++ b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/BasicProcessorInstrumentation.java @@ -0,0 +1,97 @@ +package datadog.trace.instrumentation.powerjob; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.powerjob.PowJobDecorator.DECORATOR; +import static datadog.trace.instrumentation.powerjob.PowerjobConstants.ProcessType.*; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import tech.powerjob.worker.core.processor.TaskContext; +import tech.powerjob.worker.core.processor.sdk.BroadcastProcessor; +import tech.powerjob.worker.core.processor.sdk.MapProcessor; +import tech.powerjob.worker.core.processor.sdk.MapReduceProcessor; + +@AutoService(Instrumenter.class) +public class BasicProcessorInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + public BasicProcessorInstrumentation() { + super("powerjob"); + } + + @Override + public String hierarchyMarkerType() { + return PowerjobConstants.Processer.BASIC_PROCESSOR; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())) +// .and(not(implementsInterface(named(PowerjobConstants.Processer.BROADCAST_PROCESSOR)))) +// .and(not(implementsInterface(named(PowerjobConstants.Processer.MAP_PROCESSOR)))) +// .and(not(implementsInterface(named(PowerjobConstants.Processer.MAP_REDUCE_PROCESSOR)))) +// .and(not(named(PowerjobConstants.Processer.MAP_PROCESSOR))) +// .and(not(named(PowerjobConstants.Processer.MAP_REDUCE_PROCESSOR))) + ; + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".PowerjobConstants", + packageName + ".PowerjobConstants$Tags", + packageName + ".PowerjobConstants$ProcessType", + packageName + ".PowerjobConstants$Processer", + packageName + ".PowJobDecorator" + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( isMethod() + .and(isPublic()) + .and(nameStartsWith("process")) + .and(takesArgument(0,named("tech.powerjob.worker.core.processor.TaskContext"))), + this.getClass().getName() + "$BasicProcessorAdvice"); + } + + public static class BasicProcessorAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope execute(@Advice.This Object processor,@Advice.Argument(0) TaskContext context){ + String processType; + if (processor instanceof BroadcastProcessor){ + processType = BROADCAST; + }else if (processor instanceof MapProcessor){ + processType = MAP; + }else if (processor instanceof MapReduceProcessor){ + processType = MAP_REDUCE; + }else{ + processType = BASE; + } + AgentSpan span = DECORATOR.createSpan(processor.getClass().getName(),context, processType); + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATOR.onError(scope, throwable); + DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + } + } +} diff --git a/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowJobDecorator.java b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowJobDecorator.java new file mode 100644 index 00000000000..b52ff67e69e --- /dev/null +++ b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowJobDecorator.java @@ -0,0 +1,40 @@ +package datadog.trace.instrumentation.powerjob; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import tech.powerjob.worker.core.processor.TaskContext; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; + +public class PowJobDecorator extends BaseDecorator { + + public static final PowJobDecorator DECORATOR = new PowJobDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{PowerjobConstants.INSTRUMENTATION_NAME}; + } + + @Override + protected CharSequence spanType() { + return "powerjob"; + } + + @Override + protected CharSequence component() { + return "powerjob"; + } + + public AgentSpan createSpan(String operationName, TaskContext context,String processType) { + AgentSpan span = startSpan(operationName); + + span.setTag(PowerjobConstants.Tags.JOB_ID,context.getJobId()); + span.setTag(PowerjobConstants.Tags.INSTANCE_ID,context.getInstanceId()); + span.setTag(PowerjobConstants.Tags.JOB_PARAM,context.getJobParams()); + span.setTag(PowerjobConstants.Tags.TASK_NAME,context.getTaskName()); + span.setTag(PowerjobConstants.Tags.TASK_ID,context.getTaskId()); + span.setTag(PowerjobConstants.Tags.PROCESS_TYPE,processType); + afterStart(span); + return span; + } +} diff --git a/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowerjobConstants.java b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowerjobConstants.java new file mode 100644 index 00000000000..44825526e5e --- /dev/null +++ b/dd-java-agent/instrumentation/powerjob/src/main/java/datadog/trace/instrumentation/powerjob/PowerjobConstants.java @@ -0,0 +1,31 @@ +package datadog.trace.instrumentation.powerjob; + +public class PowerjobConstants { + + public static final String INSTRUMENTATION_NAME = "powerjob"; + + interface Processer{ + + String BASIC_PROCESSOR = "tech.powerjob.worker.core.processor.sdk.BasicProcessor"; + String BROADCAST_PROCESSOR = "tech.powerjob.worker.core.processor.sdk.BroadcastProcessor"; + String MAP_PROCESSOR = "tech.powerjob.worker.core.processor.sdk.MapProcessor"; + String MAP_REDUCE_PROCESSOR = "tech.powerjob.worker.core.processor.sdk.MapReduceProcessor"; + } + + interface Tags{ + String JOB_ID ="job_id"; + String INSTANCE_ID ="instance_id"; + String TASK_ID ="task_id"; + String TASK_NAME ="task_name"; + String JOB_PARAM ="job_param"; + String PROCESS_TYPE = "process_type"; + + } + + interface ProcessType{ + String BASE = "base"; + String MAP = "map"; + String MAP_REDUCE = "map_reduce"; + String BROADCAST = "broadcast"; + } +} diff --git a/dd-java-agent/instrumentation/pulsar/build.gradle b/dd-java-agent/instrumentation/pulsar/build.gradle new file mode 100644 index 00000000000..7a3df75fe74 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/build.gradle @@ -0,0 +1,56 @@ +muzzle { + pass { + group = "org.apache.pulsar" + module = "pulsar-client" + versions = "[2.8.0,)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +//addTestSuiteForDir('latestDepTest','test') + +dependencies { + compileOnly group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.11.0' + //library("org.apache.pulsar:pulsar-client:2.8.0") + +} + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} +/* + +dependencies { + + testImplementation("javax.annotation:javax.annotation-api:1.3.2") + testImplementation("org.testcontainers:pulsar") + testImplementation("org.apache.pulsar:pulsar-client-admin:2.8.0") +} +*/ + + +/* + +muzzle { + pass { + group = "org.apache.rocketmq" + module = "rocketmq-client" + versions = "[4.8.0,)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest','test') + +dependencies { + compileOnly group: 'org.apache.rocketmq', name: 'rocketmq-client', version: '4.8.0' +} + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} +*/ diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/BasePulsarRequest.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/BasePulsarRequest.java new file mode 100644 index 00000000000..812ece84c07 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/BasePulsarRequest.java @@ -0,0 +1,20 @@ +package datadog.trace.instrumentation.pulsar; + + +public class BasePulsarRequest { + private final String destination; + private final UrlData urlData; + + protected BasePulsarRequest(String destination, UrlData urlData) { + this.destination = destination; + this.urlData = urlData; + } + + public String getDestination() { + return destination; + } + + public UrlData getUrlData() { + return urlData; + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerDecorator.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerDecorator.java new file mode 100644 index 00000000000..5a70699de44 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerDecorator.java @@ -0,0 +1,141 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.pulsar.MessageTextMapGetter.GETTER; +import static datadog.trace.instrumentation.pulsar.PulsarRequest.*; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import java.util.concurrent.CompletableFuture; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Messages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConsumerDecorator extends BaseDecorator { + private static final Logger log = LoggerFactory.getLogger(ConsumerDecorator.class); + + public static final CharSequence PULSAR_NAME = UTF8BytesString.create("queue"); + private static final String TOPIC = "topic"; + private static final String LOCAL_SERVICE_NAME = "pulsar"; + private static final String MESSAGING_ID = "messaging.id"; + private static final String MESSAGING_SYSTEM = "messaging.system"; + + ConsumerDecorator() {} + + @Override + protected String[] instrumentationNames() { + return new String[] {"pulsar", "pulsar-client"}; + } + + @Override + protected CharSequence spanType() { + return PULSAR_NAME; + } + + @Override + protected CharSequence component() { + return null; + } + + public static void startAndEnd(PulsarRequest pr, Throwable throwable, String brokerUrl) { + if (log.isDebugEnabled()) { + log.debug("into startAndEnd"); + } + AgentScope extractScope = MessageStore.extract(pr.getMessage()); + if (extractScope != null) { + return; + } + AgentSpan.Context parentContext = propagate().extract(pr, GETTER); + String topic = pr.getMessage().getTopicName(); + UTF8BytesString spanName = UTF8BytesString.create(topic + " receive"); + final AgentSpan span = startSpan(spanName, parentContext); + span.setResourceName(spanName); + span.setTag(TOPIC, pr.getMessage().getTopicName()); + span.setTag("destination", pr.getDestination()); + span.setTag("broker_url", brokerUrl); + span.setTag(MESSAGING_SYSTEM, LOCAL_SERVICE_NAME); + span.setSpanType(PULSAR_NAME); + span.setTag(MESSAGING_ID, pr.getMessage().getMessageId()); + span.setServiceName(LOCAL_SERVICE_NAME); + if (throwable != null) { + span.setError(true); + span.setErrorMessage(throwable.getMessage()); + } + + AgentScope scope = activateSpan(span); + + // beforeFinish(scope); + scope.span().finish(); + scope.close(); + if (log.isDebugEnabled()) { + log.debug("out startAndEnd"); + } + MessageStore.Inject(pr.getMessage(), scope); + } + + public static CompletableFuture> wrap( + CompletableFuture> future, String brokerUrl) { + if (log.isDebugEnabled()) { + log.debug("into wrap"); + } + CompletableFuture> result = new CompletableFuture<>(); + future.whenComplete( + (message, throwable) -> { + // consumer 用来获取 url + if (message == null) { + return; + } + startAndEnd(create(message), throwable, brokerUrl); + runWithContext( + () -> { + if (throwable != null) { + result.completeExceptionally(throwable); + } else { + result.complete(message); + } + }); + }); + if (log.isDebugEnabled()) { + log.debug("out wrap"); + } + return result; + } + + public static CompletableFuture> wrapBatch( + CompletableFuture> future, String brokerUrl) { + CompletableFuture> result = new CompletableFuture<>(); + future.whenComplete( + (messages, throwable) -> { + if (messages == null) { + return; + } + + for (Message m : messages) { + if (m != null) { + startAndEnd(create(m), throwable, brokerUrl); + } + } + + runWithContext( + () -> { + if (throwable != null) { + result.completeExceptionally(throwable); + } else { + result.complete(messages); + } + }); + }); + + return result; + } + + private static void runWithContext(Runnable runnable) { + runnable.run(); + if (log.isDebugEnabled()) { + log.debug("out runWithContext"); + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerImplInstrumentation.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerImplInstrumentation.java new file mode 100644 index 00000000000..65a281ca696 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ConsumerImplInstrumentation.java @@ -0,0 +1,181 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.pulsar.ConsumerDecorator.startAndEnd; +import static datadog.trace.instrumentation.pulsar.ConsumerDecorator.wrap; +import static datadog.trace.instrumentation.pulsar.ConsumerDecorator.wrapBatch; +import static datadog.trace.instrumentation.pulsar.PulsarRequest.*; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isProtected; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import net.bytebuddy.asm.Advice; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Messages; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.impl.PulsarClientImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@AutoService(Instrumenter.class) +public final class ConsumerImplInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForKnownTypes { + + private static final Logger log = LoggerFactory.getLogger(ConsumerImplInstrumentation.class); + + public ConsumerImplInstrumentation() { + super("pulsar"); + } + + @Override + public String[] knownMatchingTypes() { + return new String[] { + "org.apache.pulsar.client.impl.ConsumerImpl", + "org.apache.pulsar.client.impl.MultiTopicsConsumerImpl" + }; + } + + @Override + public Map contextStore() { + Map store = new HashMap<>(1); + store.put("org.apache.pulsar.client.api.Consumer", String.class.getName()); + return store; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".ConsumerDecorator", + packageName + ".UrlParser", + packageName + ".UrlData", + packageName + ".ProducerData", + packageName + ".BasePulsarRequest", + packageName + ".MessageTextMapGetter", + packageName + ".MessageTextMapSetter", + packageName + ".PulsarBatchRequest", + packageName + ".PulsarRequest", + packageName + ".MessageStore", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + String className = ConsumerImplInstrumentation.class.getName(); + transformation.applyAdvice(isConstructor(), className + "$ConsumerConstructorAdvice"); + + transformation.applyAdvice( + isMethod() + .and(isProtected()) + .and(named("internalReceive")) + .and(takesArguments(2)) + .and(takesArgument(1, named("java.util.concurrent.TimeUnit"))), + className + "$ConsumerInternalReceiveAdvice"); + + // internalReceive will apply to Consumer#receive() + transformation.applyAdvice( + isMethod().and(isProtected()).and(named("internalReceive")).and(takesArguments(0)), + className + "$ConsumerSyncReceiveAdvice"); + + // internalReceiveAsync will apply to Consumer#receiveAsync() + transformation.applyAdvice( + isMethod().and(isProtected()).and(named("internalReceiveAsync")).and(takesArguments(0)), + className + "$ConsumerAsyncReceiveAdvice"); + + // internalBatchReceiveAsync will apply to Consumer#batchReceive() and + // Consumer#batchReceiveAsync() + transformation.applyAdvice( + isMethod() + .and(isProtected()) + .and(named("internalBatchReceiveAsync")) + .and(takesArguments(0)), + className + "$ConsumerBatchAsyncReceiveAdvice"); + } + + public static class ConsumerConstructorAdvice { // 初始化 + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.This Consumer consumer, @Advice.Argument(value = 0) PulsarClient client) { + PulsarClientImpl pulsarClient = (PulsarClientImpl) client; + String url = pulsarClient.getLookup().getServiceUrl(); + // VirtualFieldStore.inject(consumer, url); + ContextStore contextStore = + InstrumentationContext.get(Consumer.class, String.class); + contextStore.put(consumer, url); + } + } + + public static class ConsumerInternalReceiveAdvice { + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.This Consumer consumer, + @Advice.Return Message message, + @Advice.Thrown Throwable throwable) { + ContextStore contextStore = + InstrumentationContext.get(Consumer.class, String.class); + String brokerUrl = contextStore.get(consumer); + if (message == null) { + return; + } + + startAndEnd(create(message), throwable, brokerUrl); + } + } + + @SuppressWarnings("unused") + public static class ConsumerSyncReceiveAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.This Consumer consumer, + @Advice.Return Message message, + @Advice.Thrown Throwable throwable) { + if (message == null) { + return; + } + ContextStore contextStore = + InstrumentationContext.get(Consumer.class, String.class); + String brokerUrl = contextStore.get(consumer); + + startAndEnd(create(message), throwable, brokerUrl); + } + } + + public static class ConsumerAsyncReceiveAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.This Consumer consumer, + @Advice.Return(readOnly = false) CompletableFuture> future) { + ContextStore contextStore = + InstrumentationContext.get(Consumer.class, String.class); + String brokerUrl = contextStore.get(consumer); + + future = wrap(future, brokerUrl); + } + } + + @SuppressWarnings("unused") + public static class ConsumerBatchAsyncReceiveAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.This Consumer consumer, + @Advice.Return(readOnly = false) CompletableFuture> future) { + ContextStore contextStore = + InstrumentationContext.get(Consumer.class, String.class); + String brokerUrl = contextStore.get(consumer); + future = wrapBatch(future, brokerUrl); + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerInstrumentation.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerInstrumentation.java new file mode 100644 index 00000000000..fa5c4211ce1 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerInstrumentation.java @@ -0,0 +1,86 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.pulsar.client.api.MessageListener; +import org.apache.pulsar.client.impl.conf.ConsumerConfigurationData; + +@AutoService(Instrumenter.class) +public class MessageListenerInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public MessageListenerInstrumentation() { + super("pulsar"); + } + + /* @Override + public String[] knownMatchingTypes() { + return new String[] { + "org.apache.pulsar.client.impl.conf.ConsumerConfigurationData", + }; + }*/ + + public static final String CLASS_NAME = + "org.apache.pulsar.client.impl.conf.ConsumerConfigurationData"; + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".ConsumerDecorator", + packageName + ".UrlParser", + packageName + ".UrlData", + packageName + ".ProducerData", + packageName + ".BasePulsarRequest", + packageName + ".MessageTextMapGetter", + packageName + ".MessageTextMapSetter", + packageName + ".PulsarBatchRequest", + packageName + ".PulsarRequest", + packageName + ".MessageStore", + packageName + ".MessageListenerWrapper", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + String className = MessageListenerInstrumentation.class.getName(); + + transformation.applyAdvice( + isMethod().and(isPublic()).and(named("getMessageListener")), + className + "$ConsumerConfigurationDataMethodAdvice"); + } + + public static class ConsumerConfigurationDataMethodAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void after( + @Advice.This ConsumerConfigurationData data, + @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) + MessageListener listener) { + + if (listener == null) { + return; + } + + listener = new MessageListenerWrapper<>(listener); + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerWrapper.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerWrapper.java new file mode 100644 index 00000000000..7fe06f944f0 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageListenerWrapper.java @@ -0,0 +1,72 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.MessageListener; + +public class MessageListenerWrapper implements MessageListener { + private static final long serialVersionUID = 1L; + + private final MessageListener delegate; + + public MessageListenerWrapper(MessageListener messageListener) { + this.delegate = messageListener; + } + + @Override + public void received(Consumer consumer, Message message) { + // process message + AgentScope extractScope = MessageStore.extract(message); + if (extractScope == null) { + this.delegate.received(consumer, message); + return; + } + + try{ + AgentScope scope = start(extractScope, message); + this.delegate.received(consumer, message); + end(scope,null); + }catch (Throwable t){ + + } + } + + public AgentScope start(AgentScope extractScope, Message message) { + String topicName = message.getTopicName(); + UTF8BytesString spanName = UTF8BytesString.create(topicName + " process"); + AgentSpan span = startSpan("datadog",spanName, extractScope.span().context()); + span.setResourceName(spanName); + span.setTag("topic", message.getTopicName()); + span.setTag("destination", message.getTopicName()); + + span.setSpanType("queue"); + span.setTag("message_id", message.getMessageId()); + span.setServiceName("pulsar"); + + AgentScope scope = activateSpan(span); + + return scope; + } + + public void end(AgentScope scope,Throwable throwable) { + // todo error + if (throwable != null) { + scope.span().setError(true); + scope.span().setErrorMessage(throwable.getMessage()); + } + + scope.span().finish(); + scope.close(); + } + + @Override + public void reachedEndOfTopic(Consumer consumer) { + this.delegate.reachedEndOfTopic(consumer); + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageStore.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageStore.java new file mode 100644 index 00000000000..48b0ab9ffab --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageStore.java @@ -0,0 +1,35 @@ +package datadog.trace.instrumentation.pulsar; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import java.util.HashMap; +import java.util.Map; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.impl.TopicMessageImpl; + +public class MessageStore { + private static final Map, AgentScope> MSG_FIELD = + new HashMap,AgentScope>(); + + public static void Inject(Message instance, AgentScope scope){ + if (instance instanceof TopicMessageImpl) { + TopicMessageImpl topicMessage = (TopicMessageImpl) instance; + instance = topicMessage.getMessage(); + } + if (instance != null) { + MSG_FIELD.put(instance, scope); + } + } + + public static AgentScope extract(Message instance) { + if (instance instanceof TopicMessageImpl) { + TopicMessageImpl topicMessage = (TopicMessageImpl) instance; + instance = topicMessage.getMessage(); + } + if (instance == null) { + return null; + } + AgentScope as = MSG_FIELD.get(instance); + MSG_FIELD.remove(instance); + return as; + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapGetter.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapGetter.java new file mode 100644 index 00000000000..be2814fb54a --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapGetter.java @@ -0,0 +1,20 @@ +package datadog.trace.instrumentation.pulsar; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import java.util.Map; + +public class MessageTextMapGetter implements AgentPropagation.ContextVisitor{ + + public static final MessageTextMapGetter GETTER = new MessageTextMapGetter(); + @Override + public void forEachKey(PulsarRequest carrier, AgentPropagation.KeyClassifier classifier) { + Map properties = carrier.getMessage().getProperties(); + for (Map.Entry entry : properties.entrySet()){ + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapSetter.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapSetter.java new file mode 100644 index 00000000000..a87eedec687 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/MessageTextMapSetter.java @@ -0,0 +1,18 @@ +package datadog.trace.instrumentation.pulsar; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.pulsar.client.impl.MessageImpl; + +public class MessageTextMapSetter implements AgentPropagation.Setter{ + public static final MessageTextMapSetter SETTER = new MessageTextMapSetter(); + @Override + public void set(PulsarRequest carrier, String key, String value) { + if (carrier == null) { + return; + } + if (carrier.getMessage() instanceof MessageImpl) { + MessageImpl message = (MessageImpl) carrier.getMessage(); + message.getMessageBuilder().addProperty().setKey(key).setValue(value); + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerData.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerData.java new file mode 100644 index 00000000000..a5deab43f2b --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerData.java @@ -0,0 +1,15 @@ +package datadog.trace.instrumentation.pulsar; + +public final class ProducerData { + public final String url; + public final String topic; + + private ProducerData(String url, String topic) { + this.url = url; + this.topic = topic; + } + + public static ProducerData create(String url, String topic) { + return new ProducerData(url, topic); + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerDecorator.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerDecorator.java new file mode 100644 index 00000000000..83001a8733c --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerDecorator.java @@ -0,0 +1,59 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.pulsar.MessageTextMapSetter.SETTER; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; + +public class ProducerDecorator extends BaseDecorator { + public static final CharSequence ROCKETMQ_NAME = UTF8BytesString.create("pulsar"); + + private static final String MESSAGING_SYSTEM = "messaging.system"; + private static final String MESSAGING_PAYLOAD = "messaging.payload_size_bytes"; + public ProducerDecorator(){} + @Override + protected String[] instrumentationNames() { + return new String[]{"pulsar"}; + } + + @Override + protected CharSequence spanType() { + return ROCKETMQ_NAME; + } + + @Override + protected CharSequence component() { + return null; + } + + public static AgentScope start(PulsarRequest request){ + UTF8BytesString spanName = UTF8BytesString.create(request.getDestination()+" send"); + final AgentSpan span = startSpan(spanName); + span.setServiceName("pulsar"); + span.setResourceName(spanName); + span.setTag("topic",request.getDestination()); + span.setTag("broker_url",request.getUrlData().getHost()); + span.setTag("broker_port",request.getUrlData().getPort()); + span.setTag(MESSAGING_PAYLOAD,request.getMessage().getData().length); + span.setTag("messaging.id",request.getMessage().getMessageId()); + // afterStart(span); + span.setSpanType("queue"); + propagate().inject(span,request, SETTER); + return activateSpan(span); + } + + public void end(AgentScope scope, PulsarRequest request, Exception e) { + if (e != null){ + scope.span().setError(true); + scope.span().setErrorMessage(e.getMessage()); + } + beforeFinish(scope); + scope.span().finish(); + scope.close(); + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerImplInstrumentation.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerImplInstrumentation.java new file mode 100644 index 00000000000..f21213960f3 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/ProducerImplInstrumentation.java @@ -0,0 +1,121 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import java.util.HashMap; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.impl.ProducerImpl; +import org.apache.pulsar.client.impl.PulsarClientImpl; +import org.apache.pulsar.client.impl.SendCallback; + +@AutoService(Instrumenter.class) +public final class ProducerImplInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public static final String CLASS_NAME = "org.apache.pulsar.client.impl.ProducerImpl"; + + public ProducerImplInstrumentation() { + super("pulsar"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public Map contextStore() { + Map store = new HashMap<>(1); + store.put("org.apache.pulsar.client.impl.ProducerImpl", packageName+".ProducerData"); + return store; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".ProducerDecorator", + packageName + ".ProducerData", + packageName + ".UrlParser", + packageName + ".SendCallbackWrapper", + packageName + ".UrlData", + packageName + ".BasePulsarRequest", + packageName + ".MessageTextMapGetter", + packageName + ".MessageTextMapSetter", + packageName + ".PulsarBatchRequest", + packageName + ".PulsarRequest", + + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isConstructor() + .and(isPublic()) + .and( + takesArgument(0, hasSuperType(named("org.apache.pulsar.client.api.PulsarClient")))), + ProducerImplInstrumentation.class.getName() + "$ProducerImplConstructorAdvice"); + + transformation.applyAdvice( + isMethod() + .and(named("sendAsync")) + .and(takesArgument(1, named("org.apache.pulsar.client.impl.SendCallback"))), + ProducerImplInstrumentation.class.getName() + "$ProducerSendAsyncMethodAdvice"); + } + + public static class ProducerImplConstructorAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.This ProducerImpl producer, @Advice.Argument(0) PulsarClient client) { + PulsarClientImpl pulsarClient = (PulsarClientImpl) client; + String brokerUrl = pulsarClient.getLookup().getServiceUrl(); + String topic = producer.getTopic(); + // VirtualFieldStore.inject(producer, brokerUrl, topic); todo 存储 + ContextStore contextStore = + InstrumentationContext.get(ProducerImpl.class, ProducerData.class); + contextStore.put(producer, ProducerData.create(brokerUrl, topic)); + } + } + + // ------------------------------------- 发送消息 -------------------------- + public static class ProducerSendAsyncMethodAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.This ProducerImpl producer, + @Advice.Argument(0) Message message, + @Advice.Argument(value = 1, readOnly = false) SendCallback callback) { + + ContextStore contextStore = + InstrumentationContext.get(ProducerImpl.class, ProducerData.class); + ProducerData producerData = contextStore.get(producer); + + PulsarRequest request = + PulsarRequest.create(message, ProducerData.create(producerData.url, producerData.topic)); + + AgentScope scope = ProducerDecorator.start(request); + + callback = new SendCallbackWrapper(scope, request, callback); + } + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarBatchRequest.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarBatchRequest.java new file mode 100644 index 00000000000..7b475070a70 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarBatchRequest.java @@ -0,0 +1,37 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.instrumentation.pulsar.UrlParser.parseUrl; + +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Messages; + +public class PulsarBatchRequest extends BasePulsarRequest { + private final Messages messages; + + private PulsarBatchRequest(Messages messages, String destination, UrlData urlData) { + super(destination, urlData); + this.messages = messages; + } + + public static PulsarBatchRequest create(Messages messages, String url) { + return new PulsarBatchRequest(messages, getTopicName(messages), parseUrl(url)); + } + + private static String getTopicName(Messages messages) { + String topicName = null; + for (Message message : messages) { + String name = message.getTopicName(); + if (topicName == null) { + topicName = name; + } else if (!topicName.equals(name)) { + return null; + } + } + return topicName; + } + + public Messages getMessages() { + return messages; + } + +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarRequest.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarRequest.java new file mode 100644 index 00000000000..b7d9f635b88 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/PulsarRequest.java @@ -0,0 +1,35 @@ +package datadog.trace.instrumentation.pulsar; + +import static datadog.trace.instrumentation.pulsar.UrlParser.parseUrl; + +import org.apache.pulsar.client.api.Message; + +public final class PulsarRequest extends BasePulsarRequest { + private final Message message; + + private PulsarRequest(Message message, String destination, UrlData urlData) { + super(destination, urlData); + this.message = message; + } + + + public static PulsarRequest create(Message message) { + return new PulsarRequest(message, message.getTopicName(), null); + } + + public static PulsarRequest create(Message message, String url) { + return new PulsarRequest(message, message.getTopicName(), parseUrl(url)); + } + + public static PulsarRequest create(Message message, UrlData urlData) { + return new PulsarRequest(message, message.getTopicName(), urlData); + } + + public static PulsarRequest create(Message message, ProducerData producerData) { + return new PulsarRequest(message, producerData.topic, parseUrl(producerData.url)); + } + + public Message getMessage() { + return message; + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/SendCallbackWrapper.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/SendCallbackWrapper.java new file mode 100644 index 00000000000..36539547d0f --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/SendCallbackWrapper.java @@ -0,0 +1,54 @@ +package datadog.trace.instrumentation.pulsar; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import java.util.concurrent.CompletableFuture; +import org.apache.pulsar.client.api.MessageId; +import org.apache.pulsar.client.impl.MessageImpl; +import org.apache.pulsar.client.impl.SendCallback; + +public class SendCallbackWrapper implements SendCallback { + + private final AgentScope scope; + private final PulsarRequest request; + private final SendCallback delegate; + + public SendCallbackWrapper(AgentScope scope, PulsarRequest request, SendCallback callback) { + this.scope = scope; + this.request = request; + this.delegate = callback; + } + + @Override + public void sendComplete(Exception e) { + if (scope == null) { + this.delegate.sendComplete(e); + return; + } + + try { + this.delegate.sendComplete(e); + } finally { + new ProducerDecorator().end(scope, request, e); + } + } + + @Override + public void addCallback(MessageImpl msg, SendCallback scb) { + this.delegate.addCallback(msg, scb); + } + + @Override + public SendCallback getNextSendCallback() { + return this.delegate.getNextSendCallback(); + } + + @Override + public MessageImpl getNextMessage() { + return this.delegate.getNextMessage(); + } + + @Override + public CompletableFuture getFuture() { + return this.delegate.getFuture(); + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlData.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlData.java new file mode 100644 index 00000000000..577daaabdf9 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlData.java @@ -0,0 +1,19 @@ +package datadog.trace.instrumentation.pulsar; + +public class UrlData { + private final String host; + private final Integer port; + + UrlData(String host, Integer port) { + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } +} diff --git a/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlParser.java b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlParser.java new file mode 100644 index 00000000000..cd3da833100 --- /dev/null +++ b/dd-java-agent/instrumentation/pulsar/src/main/java/datadog/trace/instrumentation/pulsar/UrlParser.java @@ -0,0 +1,40 @@ +package datadog.trace.instrumentation.pulsar; + +public class UrlParser { + private UrlParser() {} + + public static UrlData parseUrl(String url) { + // if there are multiple addresses then they are separated with , or ; + if (url == null || url.indexOf(',') != -1 || url.indexOf(';') != -1) { + return null; + } + + int protocolEnd = url.indexOf("://"); + if (protocolEnd == -1) { + return null; + } + int authorityStart = protocolEnd + 3; + int authorityEnd = url.indexOf('/', authorityStart); + if (authorityEnd == -1) { + authorityEnd = url.length(); + } + String authority = url.substring(authorityStart, authorityEnd); + int portStart = authority.indexOf(':'); + + String host; + Integer port; + if (portStart == -1) { + host = authority; + port = null; + } else { + host = authority.substring(0, portStart); + try { + port = Integer.parseInt(authority.substring(portStart + 1)); + } catch (NumberFormatException exception) { + port = null; + } + } + + return new UrlData(host, port); + } +} diff --git a/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonClientDecorator.java b/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonClientDecorator.java index 481fd745e3b..6b7bab1a0fb 100644 --- a/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonClientDecorator.java +++ b/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonClientDecorator.java @@ -1,11 +1,16 @@ package datadog.trace.instrumentation.redisson; +import datadog.trace.api.Config; import datadog.trace.api.naming.SpanNaming; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; +import io.netty.buffer.ByteBuf; import org.redisson.client.protocol.CommandData; +import java.nio.charset.StandardCharsets; + public class RedissonClientDecorator extends DBTypeProcessingDatabaseClientDecorator> { public static final RedissonClientDecorator DECORATE = new RedissonClientDecorator(); @@ -15,6 +20,8 @@ public class RedissonClientDecorator private static final String SERVICE_NAME = SpanNaming.instance().namingSchema().cache().service("redis"); + public boolean RedisCommandRaw = Config.get().getRedisCommandArgs(); + private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("redis-command"); @Override @@ -56,4 +63,36 @@ protected String dbInstance(CommandData commandData) { protected CharSequence dbHostname(CommandData commandData) { return null; } + + public AgentSpan onArgs(final AgentSpan span, Object[] args) { + if (RedisCommandRaw&& args.length>0){ + StringBuilder sb = new StringBuilder(); + for (Object val : args) { + if (val instanceof ByteBuf) { + ByteBuf buf = (ByteBuf) val; + if (buf.hasArray()) { + String data = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes(), StandardCharsets.UTF_8); + sb.append(data).append(" "); + } else { + byte[] bytes = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), bytes); + try{ + byte[] result = new byte[bytes.length - 1]; + System.arraycopy(bytes, 1, result, 0, bytes.length - 2); + result[result.length - 1] = (byte) (bytes[bytes.length - 1] & 0x7F); + String data = new String(result, StandardCharsets.UTF_8); + sb.append(data).append(" "); + }catch (Exception e){ + System.out.println(e); + } + } + } else { + sb.append(val.toString()).append(" "); + } + } + span.setTag("redis.command.args",sb.toString()); + } + + return span; + } } diff --git a/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonInstrumentation.java b/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonInstrumentation.java index 00a4da74d81..16445ca7007 100644 --- a/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonInstrumentation.java +++ b/dd-java-agent/instrumentation/redisson-2.0.0/src/main/java/datadog/trace/instrumentation/redisson/RedissonInstrumentation.java @@ -66,6 +66,8 @@ public static AgentScope onEnter( final AgentSpan span = startSpan(RedissonClientDecorator.OPERATION_NAME); DECORATE.afterStart(span); DECORATE.onPeerConnection(span, thiz.getRedisClient().getAddr()); + + DECORATE.onArgs(span,command.getParams()); DECORATE.onStatement(span, command.getCommand().getName()); return activateSpan(span); } diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/build.gradle b/dd-java-agent/instrumentation/rocketmq-5.0/build.gradle new file mode 100644 index 00000000000..7d3931db12b --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/build.gradle @@ -0,0 +1,20 @@ +muzzle { + pass { + group = "org.apache.rocketmq" + module = "rocketmq-client-java" + versions = "[5.0.0,)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + + +dependencies { + compileOnly group: 'org.apache.rocketmq', name: 'rocketmq-client-java', version: '5.0.0' +} + + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumeServiceInstrumentation.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumeServiceInstrumentation.java new file mode 100644 index 00000000000..af2704442d6 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumeServiceInstrumentation.java @@ -0,0 +1,60 @@ +package datadog.trace.instrumentation.rocketmq5; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.apis.consumer.MessageListener; + +@AutoService(Instrumenter.class) +public class ConsumeServiceInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + public ConsumeServiceInstrumentation() { + super("rocketmq-5.0"); + } + + @Override + public String hierarchyMarkerType() { + return ("org.apache.rocketmq.client.java.impl.consumer.ConsumeService"); + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ReceiveSpanFinishingCallback", + packageName + ".MessageListenerWrapper", + packageName + ".MessageMapSetter", + packageName + ".MessageViewGetter", + packageName + ".SendSpanFinishingCallback", + packageName + ".Timer", + }; + } + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isConstructor() + .and(isPublic() + .and(takesArgument(1, named("org.apache.rocketmq.client.apis.consumer.MessageListener")))), + ConsumeServiceInstrumentation.class.getName() + "$ConstructorAdvice"); + } + + public static class ConstructorAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.Argument(value = 1, readOnly = false) MessageListener messageListener) { + // Replace messageListener by wrapper. + if (!(messageListener instanceof MessageListenerWrapper)) { + messageListener = new MessageListenerWrapper(messageListener); + } + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumerImplInstrumentation.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumerImplInstrumentation.java new file mode 100644 index 00000000000..790dc28f705 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ConsumerImplInstrumentation.java @@ -0,0 +1,82 @@ +package datadog.trace.instrumentation.rocketmq5; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import apache.rocketmq.v2.ReceiveMessageRequest; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.java.impl.consumer.ReceiveMessageResult; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.Futures; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.ListenableFuture; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.MoreExecutors; + +@AutoService(Instrumenter.class) +public class ConsumerImplInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + + public static final String CLASS_NAME = "org.apache.rocketmq.client.java.impl.consumer.ConsumerImpl"; + + public ConsumerImplInstrumentation() { + super("rocketmq5", "rocketmq-client-java"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ReceiveSpanFinishingCallback", + packageName + ".MessageListenerWrapper", + packageName + ".MessageMapSetter", + packageName + ".MessageViewGetter", + packageName + ".SendSpanFinishingCallback", + packageName + ".Timer", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(named("receiveMessage")) + .and(takesArguments(3)) + .and(takesArgument(0, named("apache.rocketmq.v2.ReceiveMessageRequest"))) + .and(takesArgument(1, named("org.apache.rocketmq.client.java.route.MessageQueueImpl"))) + .and(takesArgument(2, named("java.time.Duration"))), + ConsumerImplInstrumentation.class.getName() + "$ReceiveMessageAdvice"); + } + + + public static class ReceiveMessageAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Timer onStart() { + // todo span start + return Timer.start(); + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) ReceiveMessageRequest request, + @Advice.Enter Timer timer, + @Advice.Return ListenableFuture future) { + ReceiveSpanFinishingCallback spanFinishingCallback = + new ReceiveSpanFinishingCallback(request, timer); + Futures.addCallback(future, spanFinishingCallback, MoreExecutors.directExecutor()); + } + } +} + diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageImplInstrumentation.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageImplInstrumentation.java new file mode 100644 index 00000000000..dd4aea2ed51 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageImplInstrumentation.java @@ -0,0 +1,82 @@ +package datadog.trace.instrumentation.rocketmq5; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.rocketmq5.MessageMapSetter.SETTER; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.apis.message.Message; +import org.apache.rocketmq.client.java.message.MessageBuilderImpl; + +@AutoService(Instrumenter.class) +public class MessageImplInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy{ + public static String CLASS_NAME = ""; + + public MessageImplInstrumentation() { + super("rocketmq-5.0"); + } + + @Override + public String hierarchyMarkerType() { + return "org.apache.rocketmq.client.java.message.MessageBuilderImpl"; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ReceiveSpanFinishingCallback", + packageName + ".MessageListenerWrapper", + packageName + ".MessageMapSetter", + packageName + ".MessageViewGetter", + packageName + ".SendSpanFinishingCallback", + }; + } + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(named("build")) + .and(takesArguments(0)), + MessageImplInstrumentation.class.getName() + "$BuildAdvice" + ); + } + + public static class BuildAdvice{ + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This MessageBuilderImpl impl){ + AgentSpan span = startSpan("message build send"); + span.setSpanType("rocketmq"); + AgentScope scope = activateSpan(span); + propagate().inject(span,impl,SETTER); + return scope; + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.Enter AgentScope scope, + @Advice.Return Message message) { + scope.span().setServiceName("rocketmq-producer"); + scope.span().setTag("topic",message.getTopic()); + scope.span().setTag("tag",message.getTag()); + scope.span().setTag("messageGroup",message.getMessageGroup()); + scope.span().setTag("keys",message.getKeys()); + scope.close(); + scope.span().finish(); + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageListenerWrapper.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageListenerWrapper.java new file mode 100644 index 00000000000..bff49698373 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageListenerWrapper.java @@ -0,0 +1,45 @@ +package datadog.trace.instrumentation.rocketmq5; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.rocketmq.client.apis.consumer.ConsumeResult; +import org.apache.rocketmq.client.apis.consumer.MessageListener; +import org.apache.rocketmq.client.apis.message.MessageView; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.rocketmq5.MessageViewGetter.GetterView; + +public class MessageListenerWrapper implements MessageListener { + private final MessageListener delegator; + + public MessageListenerWrapper(MessageListener delegator) { + this.delegator = delegator; + } + + @Override + public ConsumeResult consume(MessageView messageView) { + ConsumeResult consumeResult = null; + // todo start span and end + AgentSpan.Context parentContext =propagate().extract(messageView, GetterView); + + AgentSpan span ; + if (null != parentContext){ + span = startSpan("messageListener",parentContext); + }else { + span = startSpan("messageListener"); + } + span.setSpanType("rocketmq"); + span.setTag("messageID",messageView.getMessageId()); + span.setServiceName("rocketmq-consume"); + span.setTag("topic",messageView.getTopic()); + span.setTag("tag",messageView.getTag()); + span.setTag("keys",messageView.getKeys()); + span.setTag("message_group",messageView.getMessageGroup()); + AgentScope scope = activateSpan(span); + consumeResult = delegator.consume(messageView); + scope.close(); + scope.span().finish(); + + return consumeResult; + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageMapSetter.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageMapSetter.java new file mode 100644 index 00000000000..ed11857c0a0 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageMapSetter.java @@ -0,0 +1,18 @@ +package datadog.trace.instrumentation.rocketmq5; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.rocketmq.client.java.message.MessageBuilderImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageMapSetter implements AgentPropagation.Setter{ + public static final MessageMapSetter SETTER = new MessageMapSetter(); + private static final Logger log = LoggerFactory.getLogger(MessageMapSetter.class); + @Override + public void set(MessageBuilderImpl carrier, String key, String value) { + if (log.isDebugEnabled()) { + log.debug("rocketmq Inject {} :\t {}" , key , value); + } + carrier.addProperty(key,value); + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageViewGetter.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageViewGetter.java new file mode 100644 index 00000000000..4e736c146b3 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/MessageViewGetter.java @@ -0,0 +1,29 @@ +package datadog.trace.instrumentation.rocketmq5; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.rocketmq.client.apis.message.MessageView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class MessageViewGetter implements AgentPropagation.ContextVisitor{ + + private static final Logger log = LoggerFactory.getLogger(MessageViewGetter.class); + public static final MessageViewGetter GetterView = new MessageViewGetter(); + + @Override + public void forEachKey(MessageView carrier, AgentPropagation.KeyClassifier classifier) { + Map properties = carrier.getProperties(); + if (log.isDebugEnabled()) { + log.debug("Extract size: {}",properties.entrySet().size()); + } + for (Map.Entry entry : properties.entrySet()){ + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ReceiveSpanFinishingCallback.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ReceiveSpanFinishingCallback.java new file mode 100644 index 00000000000..5832241617a --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/ReceiveSpanFinishingCallback.java @@ -0,0 +1,60 @@ +package datadog.trace.instrumentation.rocketmq5; + +import apache.rocketmq.v2.ReceiveMessageRequest; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.rocketmq.client.java.impl.consumer.ReceiveMessageResult; +import org.apache.rocketmq.client.java.message.MessageViewImpl; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.FutureCallback; + +import java.util.List; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.rocketmq5.MessageViewGetter.GetterView; + +public class ReceiveSpanFinishingCallback implements FutureCallback { + + private final ReceiveMessageRequest request; + private final Timer timer; + + public ReceiveSpanFinishingCallback(ReceiveMessageRequest request, Timer timer) { + this.request = request; + this.timer = timer; + } + + @Override + public void onSuccess(ReceiveMessageResult receiveMessageResult) { + List messageViews = receiveMessageResult.getMessageViewImpls(); + // Don't create spans when no messages were received. + if (messageViews.isEmpty()) { + return; + } + String consumerGroup = request.getGroup().getName(); + String topic = request.getMessageQueue().getTopic().getName(); + + for (MessageViewImpl messageView : messageViews) { + // propagate().inject(span.context(),messageView,setterView); + AgentSpan.Context parentContext = propagate().extract(messageView,GetterView); + AgentSpan childSpan ; + if (null != parentContext){ + childSpan = startSpan("receive_message",parentContext); + }else { + childSpan = startSpan("receive_message"); + } + childSpan.setServiceName("rocketmq-consume"); + childSpan.setTag("messageID",messageView.getMessageId()); + AgentScope scopeC = activateSpan(childSpan); + scopeC.span().setSpanType("rocketmq"); + scopeC.span().setTag("groupID",consumerGroup); + scopeC.span().setTag("topic",topic); + scopeC.span().setTag("status","success"); + + scopeC.close(); + scopeC.span().finish(); + } + } + + @Override + public void onFailure(Throwable throwable) { + } +} diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/SendSpanFinishingCallback.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/SendSpanFinishingCallback.java new file mode 100644 index 00000000000..6b64ed7454f --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/SendSpanFinishingCallback.java @@ -0,0 +1,38 @@ +package datadog.trace.instrumentation.rocketmq5; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.FutureCallback; + +public class SendSpanFinishingCallback implements FutureCallback { + + private final AgentSpan scope; + + private final PublishingMessageImpl message; + + public SendSpanFinishingCallback(AgentSpan scope, PublishingMessageImpl message){ + this.message = message; + this.scope = scope; + } + @Override + public void onSuccess(SendReceiptImpl result) { + scope.setTag("MessageID",result.getMessageId()); + scope.setSpanType("rocketmq"); + scope.finish(); + } + + @Override + public void onFailure(Throwable t) { + scope.setTag("Message_Type",message.getMessageType()); + scope.setErrorMessage(t.getMessage()); + scope.setError(true); + scope.finish(); + } +} + +// Tried to close datadog.trace.agent.core.scopemanager.ContinuableScope@4373e60c-> +// DDSpan [ t_id=9178204451511121555, s_id=8568375034643761603, p_id=3362837340041904303 ] trace=order-center/dubbo/com.k1k.ordercenter.order.rpc.api.OrderRpcCmdServiceI.placeOrder(PlaceOrderCmd) tags={_sample_rate=1, component=apache-dubbo, dubbo_method=placeOrder, dubbo_short_url=registry://hw-test-nacos.aidyd.com:8848/com.k1k.ordercenter.order.rpc.api.OrderRpcCmdServiceI.placeOrder(PlaceOrderCmd), dubbo_side=provider, dubbo_url=registry://hw-test-nacos.aidyd.com:8848/org.apache.dubbo.registry.RegistryService?application=order&dubbo=2.0.2&namespace=rpc-dev&pid=9®istry=nacos&release=3.0.5×tamp=1690192157364, dubbo_version=3.0.5, env=test, language=jvm, process_id=9, runtime-id=e9750af1-b4a0-47d7-a5b3-37f28220ac15, thread.id=4817, thread.name=DubboServerHandler-172.20.202.60:20895-thread-200},duration_ns=0 +// scope when not on top. +// Current top: datadog.trace.agent.core.scopemanager.ContinuableScope@65c8e2ec-> +// DDSpan [ t_id=9178204451511121555, s_id=4239552935852319480, p_id=8568375034643761603 ] trace=order-center/ORDER_AUTO_CLOSE_TOPIC send/order-center tags={bornAddr=10.20.0.10:10101, bornHost=172.20.202.60, brokerName=ORDER_AUTO_CLOSE_TOPIC, env=test, messaging.rocketmq.broker_address=10.20.0.10:10101, messaging.rocketmq.tags=ORDER_AUTO_CLOSE_TAG, thread.id=4817, thread.name=DubboServerHandler-172.20.202.60:20895-thread-200}, duration_ns=0 diff --git a/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/Timer.java b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/Timer.java new file mode 100644 index 00000000000..f80d2f6b285 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq-5.0/src/main/java/datadog/trace/instrumentation/rocketmq5/Timer.java @@ -0,0 +1,25 @@ +package datadog.trace.instrumentation.rocketmq5; +import java.time.Instant; + +public class Timer { + public static Timer start() { + return new Timer(Instant.now(), System.nanoTime()); + } + + private final Instant startTime; + private final long startNanoTime; + + private Timer(Instant startTime, long startNanoTime) { + this.startTime = startTime; + this.startNanoTime = startNanoTime; + } + + public Instant startTime() { + return startTime; + } + + public Instant now() { + long durationNanos = System.nanoTime() - startNanoTime; + return startTime().plusNanos(durationNanos); + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/build.gradle b/dd-java-agent/instrumentation/rocketmq/build.gradle new file mode 100644 index 00000000000..15b503e0656 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/build.gradle @@ -0,0 +1,20 @@ +muzzle { + pass { + group = "org.apache.rocketmq" + module = "rocketmq-client" + versions = "[4.8.0,)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest','test') + +dependencies { + compileOnly group: 'org.apache.rocketmq', name: 'rocketmq-client', version: '4.8.0' +} + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqDecorator.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqDecorator.java new file mode 100644 index 00000000000..c55f7fe033f --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqDecorator.java @@ -0,0 +1,161 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import org.apache.rocketmq.client.hook.ConsumeMessageContext; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageExt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.net.SocketAddress; + + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.rocketmq.TextMapExtractAdapter.GETTER; +import static datadog.trace.instrumentation.rocketmq.TextMapInjectAdapter.SETTER; + +public class RocketMqDecorator extends BaseDecorator { + private static final Logger log = LoggerFactory.getLogger(RocketMqDecorator.class); + public static final CharSequence ROCKETMQ_NAME = UTF8BytesString.create("rocketmq"); + private static final String BROKER_HOST = "bornHost"; + private static final String BROKER_ADDR = "bornAddr"; + private static final String BROKER_NAME = "brokerName"; + private static final String TOPIC = "topic"; + private static final String MESSAGING_ROCKETMQ_TAGS = "messaging.rocketmq.tags"; + private static final String MESSAGING_ROCKETMQ_BROKER_ADDRESS = "messaging.rocketmq.broker_address"; + private static final String MESSAGING_ROCKETMQ_SEND_RESULT = "messaging.rocketmq.send_result"; + private static final String MESSAGING_ROCKETMQ_QUEUE_ID = "messaging.rocketmq.queue_id"; + private static final String MESSAGING_ID = "messaging.id"; + private static final String MESSAGING_ROCKETMQ_QUEUE_OFFSET = "messaging.rocketmq.queue_offset"; + + RocketMqDecorator() { + } + + @Override + protected String[] instrumentationNames() { + return new String[]{"rocketmq", "rocketmq-client"}; + } + + @Override + protected CharSequence spanType() { + return ROCKETMQ_NAME; + } + + @Override + protected CharSequence component() { + return null; + } + + private static final String LOCAL_SERVICE_NAME = "rocketmq"; + + + public AgentScope start(ConsumeMessageContext context) { + MessageExt ext = context.getMsgList().get(0); + AgentSpan.Context parentContext = propagate().extract(ext, GETTER); + UTF8BytesString name = UTF8BytesString.create(ext.getTopic() + " receive"); + final AgentSpan span = startSpan(name, parentContext); + span.setResourceName(name); + + span.setServiceName(LOCAL_SERVICE_NAME); + + span.setTag(BROKER_NAME, ext.getBrokerName()); + String tags = ext.getTags(); + if (tags != null) { + span.setTag(MESSAGING_ROCKETMQ_TAGS, tags); + } + span.setTag(TOPIC, ext.getTopic()); + span.setTag(MESSAGING_ROCKETMQ_QUEUE_ID, ext.getQueueId()); + span.setTag(MESSAGING_ROCKETMQ_QUEUE_OFFSET, ext.getQueueOffset()); + span.setTag(MESSAGING_ID, ext.getMsgId()); + SocketAddress storeHost = ext.getStoreHost(); + if (storeHost != null) { + span.setTag(MESSAGING_ROCKETMQ_BROKER_ADDRESS, getBrokerHost(storeHost)); + } + afterStart(span); + AgentScope scope = activateSpan(span); + if (log.isDebugEnabled()){ + log.debug("consumer span start topic:{}",ext.getTopic()); + } + return scope; + } + + private static String getBrokerHost(SocketAddress storeHost) { + return storeHost.toString().replace("/", ""); + } + + public void end(ConsumeMessageContext context) { + String status = context.getStatus(); + AgentScope scope = activeScope(); + scope.span().setTag("status", status); + beforeFinish(scope); + scope.span().finish(); + scope.close(); + if (log.isDebugEnabled()){ + log.debug("consumer span end"); + } + } + + public AgentScope start(SendMessageContext context) { + String topic = context.getMessage().getTopic(); + UTF8BytesString spanName = UTF8BytesString.create(topic + " send"); + final AgentSpan span = startSpan(spanName); + span.setResourceName(spanName); + + span.setTag(BROKER_HOST, context.getBornHost()); + span.setTag(BROKER_ADDR, context.getBrokerAddr()); + span.setServiceName(LOCAL_SERVICE_NAME); + if (context.getMessage() != null) { + String tags = context.getMessage().getTags(); + if (tags != null) { + span.setTag(MESSAGING_ROCKETMQ_TAGS, tags); + } + } + + Message message = context.getMessage(); + if (null != message) { + span.setTag(TOPIC, message.getTopic()); + } + SendResult sendResult = context.getSendResult(); + if (null != sendResult) { + span.setTag(MESSAGING_ID, sendResult.getMsgId()); + } + String brokerAddr = context.getBrokerAddr(); + if (brokerAddr != null) { + span.setTag(MESSAGING_ROCKETMQ_BROKER_ADDRESS, brokerAddr); + } + + propagate().inject(span, context, SETTER); + AgentScope scope = activateSpan(span); + afterStart(span); + if (log.isDebugEnabled()){ + log.debug("consumer span start topic:{}",topic); + } + return scope; + } + + public void end(SendMessageContext context) { + Exception exception = context.getException(); + AgentScope scope = activeScope(); + if (scope == null){ + return; + } + if (null != exception) { + onError(scope, exception); + } + if (context.getSendResult() != null&&context.getSendResult().getSendStatus() != null){ + scope.span().setTag(MESSAGING_ROCKETMQ_SEND_RESULT, context.getSendResult().getSendStatus().name()); + } + + beforeFinish(scope); + scope.span().finish(); + scope.close(); + if (log.isDebugEnabled()){ + log.debug("consumer span end"); + } + } +} + diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqHook.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqHook.java new file mode 100644 index 00000000000..bedf1d4bbe3 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqHook.java @@ -0,0 +1,17 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import org.apache.rocketmq.client.hook.ConsumeMessageContext; +import org.apache.rocketmq.client.hook.ConsumeMessageHook; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.hook.SendMessageHook; + +public final class RocketMqHook { + public static ConsumeMessageHook buildConsumerHook(){ + return new TracingConsumeMessageHookImpl(); + } + public static SendMessageHook buildSendHook(){ + return new TracingSendMessageHookImpl(); + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqInstrumentation.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqInstrumentation.java new file mode 100644 index 00000000000..3d2e9ead4c2 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqInstrumentation.java @@ -0,0 +1,77 @@ +package datadog.trace.instrumentation.rocketmq; + +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.HashMap; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; + +@AutoService(Instrumenter.class) +public class RocketMqInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public static final String CLASS_NAME = "org.apache.rocketmq.client.consumer.DefaultMQPushConsumer"; + + public RocketMqInstrumentation() { + super("rocketmq", "rocketmq-client"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".RocketMqHook", + packageName + ".TracingConsumeMessageHookImpl", + packageName + ".TracingSendMessageHookImpl", + packageName + ".RocketMqDecorator", + packageName + ".TextMapExtractAdapter", + packageName + ".TextMapInjectAdapter", + }; + } + + @Override + public Map contextStore() { + Map map = new HashMap<>(1); + map.put("org.apache.rocketmq.client.hook.ConsumeMessageContext", "datadog.trace.bootstrap.instrumentation.api.AgentScope"); + return map; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + + transformation.applyAdvice( + isMethod(). + and(named("start")). + and(takesArguments(0)), + RocketMqInstrumentation.class.getName() + "$AdviceStart"); + } + + + public static class AdviceStart { + @Advice.OnMethodEnter + public static void onEnter( + @Advice.FieldValue( + value = "defaultMQPushConsumerImpl", declaringType = DefaultMQPushConsumer.class) + DefaultMQPushConsumerImpl defaultMqPushConsumerImpl) { + + defaultMqPushConsumerImpl.registerConsumeMessageHook(RocketMqHook.buildConsumerHook()); + + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqSendInstrumentation.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqSendInstrumentation.java new file mode 100644 index 00000000000..f96496b58ff --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/RocketMqSendInstrumentation.java @@ -0,0 +1,64 @@ +package datadog.trace.instrumentation.rocketmq; + +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.rocketmq.client.producer.DefaultMQProducer; + +@AutoService(Instrumenter.class) +public class RocketMqSendInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public static final String CLASS_NAME = "org.apache.rocketmq.client.producer.DefaultMQProducer"; + + public RocketMqSendInstrumentation() { + super("rocketmq", "rocketmq-client"); + } + + @Override + public String hierarchyMarkerType() { + return CLASS_NAME; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".RocketMqHook", + packageName + ".TracingSendMessageHookImpl", + packageName + ".TracingConsumeMessageHookImpl", + packageName + ".RocketMqDecorator", + packageName + ".TextMapExtractAdapter", + packageName + ".TextMapInjectAdapter", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod(). + and(named("start")). + and(takesArguments(0)), + RocketMqSendInstrumentation.class.getName() + "$AdviceStart"); + } + + public static class AdviceStart { + @Advice.OnMethodEnter + public static void onEnter( + @Advice.FieldValue(value = "defaultMQProducerImpl", declaringType = DefaultMQProducer.class) + DefaultMQProducerImpl defaultMqProducerImpl) { + + defaultMqProducerImpl.registerSendMessageHook(RocketMqHook.buildSendHook()); + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapExtractAdapter.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapExtractAdapter.java new file mode 100644 index 00000000000..d8b083228f7 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapExtractAdapter.java @@ -0,0 +1,24 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.common.message.MessageExt; + +import java.util.Map; + +public class TextMapExtractAdapter implements AgentPropagation.ContextVisitor{ + + public static final TextMapExtractAdapter GETTER = new TextMapExtractAdapter(); + + @Override + public void forEachKey(MessageExt carrier, AgentPropagation.KeyClassifier classifier) { + Map objectAttachments = carrier.getProperties(); + for (Map.Entry entry : objectAttachments.entrySet()){ + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapInjectAdapter.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapInjectAdapter.java new file mode 100644 index 00000000000..39f26bfacce --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TextMapInjectAdapter.java @@ -0,0 +1,16 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.apache.rocketmq.client.hook.SendMessageContext; + +import java.util.Map; + +public class TextMapInjectAdapter implements AgentPropagation.Setter{ + + public static final TextMapInjectAdapter SETTER = new TextMapInjectAdapter(); + + @Override + public void set(final SendMessageContext carrier, final String key, final String value) { + carrier.getMessage().getProperties().put(key,value); + } +} diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java new file mode 100644 index 00000000000..1c868bbf303 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java @@ -0,0 +1,38 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import org.apache.rocketmq.client.hook.ConsumeMessageContext; +import org.apache.rocketmq.client.hook.ConsumeMessageHook; + +public final class TracingConsumeMessageHookImpl implements ConsumeMessageHook { + private final RocketMqDecorator rocketMqDecorator; + //private final ContextStore scopeAccessor; + + TracingConsumeMessageHookImpl() { + this.rocketMqDecorator = new RocketMqDecorator(); + // this.scopeAccessor = scopeAccessor; + } + + @Override + public String hookName() { + return "ConsumeMessageTraceHook"; + } + + @Override + public void consumeMessageBefore(ConsumeMessageContext context) { + if (context == null || context.getMsgList() == null || context.getMsgList().isEmpty()) { + return; + } + rocketMqDecorator.start(context); + } + + @Override + public void consumeMessageAfter(ConsumeMessageContext context) { + if (context == null || context.getMsgList() == null || context.getMsgList().isEmpty()) { + return; + } + rocketMqDecorator.end(context); + } +} + diff --git a/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingSendMessageHookImpl.java b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingSendMessageHookImpl.java new file mode 100644 index 00000000000..78c7b9d3432 --- /dev/null +++ b/dd-java-agent/instrumentation/rocketmq/src/main/java/datadog/trace/instrumentation/rocketmq/TracingSendMessageHookImpl.java @@ -0,0 +1,40 @@ +package datadog.trace.instrumentation.rocketmq; + +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.hook.SendMessageHook; + +public final class TracingSendMessageHookImpl implements SendMessageHook { + + private final RocketMqDecorator rocketMqDecorator; + // private final ContextStore scopeAccessor; + + private AgentScope scope; + + TracingSendMessageHookImpl() { + this.rocketMqDecorator = new RocketMqDecorator(); + // this.scopeAccessor = scopeAccessor; + } + + @Override + public String hookName() { + return "SendMessageTraceHook"; + } + + @Override + public void sendMessageBefore(SendMessageContext context) { + if (context == null) { + return; + } + rocketMqDecorator.start(context); + } + + @Override + public void sendMessageAfter(SendMessageContext context) { + if (context == null) { + return; + } + rocketMqDecorator.end(context); + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Advice.java b/dd-java-agent/instrumentation/servlet/request-2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Advice.java index ac31baaa709..b923787be45 100644 --- a/dd-java-agent/instrumentation/servlet/request-2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Advice.java +++ b/dd-java-agent/instrumentation/servlet/request-2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Advice.java @@ -14,6 +14,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.instrumentation.servlet.ServletBlockingHelper; import java.security.Principal; +import java.util.Enumeration; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @@ -34,18 +35,44 @@ public static boolean onEnter( if (invalidRequest) { return false; } - final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse)response; + httpServletResponse.setHeader("guance_trace_id", GlobalTracer.get().getTraceId()); Object spanAttr = request.getAttribute(DD_SPAN_ATTRIBUTE); + + StringBuffer requestHeader = new StringBuffer(""); + + boolean tracerHeader = Config.get().isTracerHeaderEnabled(); + if (tracerHeader) { + Enumeration headerNames = httpServletRequest.getHeaderNames(); + int count = 0; + while (headerNames.hasMoreElements()) { + if (count==0){ + requestHeader.append("{"); + }else{ + requestHeader.append(","); + } + String headerName = headerNames.nextElement(); + requestHeader.append("\"").append(headerName).append("\":").append("\"").append(httpServletRequest.getHeader(headerName).replace("\"","")).append("\"\n"); + count ++; + } + if (count>0){ + requestHeader.append("}"); + } + } + final boolean hasServletTrace = spanAttr instanceof AgentSpan; if (hasServletTrace) { // Tracing might already be applied by the FilterChain or a parent request (forward/include). + AgentSpan span = (AgentSpan)spanAttr; + span.setTag("request_header",requestHeader.toString()); return false; } if (response instanceof HttpServletResponse) { // Default value for checking for uncaught error later InstrumentationContext.get(ServletResponse.class, Integer.class).put(response, 200); + } final AgentSpan.Context.Extracted extractedContext = DECORATE.extract(httpServletRequest); @@ -54,7 +81,7 @@ public static boolean onEnter( scope.setAsyncPropagation(true); DECORATE.afterStart(span); DECORATE.onRequest(span, httpServletRequest, httpServletRequest, extractedContext); - + span.setTag("request_header",requestHeader.toString()); httpServletRequest.setAttribute(DD_SPAN_ATTRIBUTE, span); httpServletRequest.setAttribute( CorrelationIdentifier.getTraceIdKey(), GlobalTracer.get().getTraceId()); diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/BodyReaderHttpServletRequestWrapper.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/BodyReaderHttpServletRequestWrapper.java new file mode 100644 index 00000000000..7bf0442db1f --- /dev/null +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/BodyReaderHttpServletRequestWrapper.java @@ -0,0 +1,94 @@ +package datadog.trace.instrumentation.servlet3; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.IOException; +import java.io.*; +import java.nio.charset.Charset; + +public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { + + + private String bodyStr; + + private final byte[] body; + + public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + this.bodyStr = getBodyString(request); + body = bodyStr.getBytes(); + } + + public String getBodyStr() { + return bodyStr; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } + + + public String getBodyString(HttpServletRequest request) throws IOException { + StringBuilder sb = new StringBuilder(); + InputStream inputStream = null; + BufferedReader reader = null; + try { + inputStream = request.getInputStream(); + reader = new BufferedReader( + new InputStreamReader(inputStream, Charset.forName("UTF-8"))); + + char[] bodyCharBuffer = new char[1024]; + int len = 0; + while ((len = reader.read(bodyCharBuffer)) != -1) { + sb.append(new String(bodyCharBuffer, 0, len)); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return sb.toString(); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } +} diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java index b61545ed29f..6f2e67b64c1 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Advice.java @@ -15,6 +15,8 @@ import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.instrumentation.servlet.ServletBlockingHelper; + +import java.io.IOException; import java.security.Principal; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.ServletRequest; @@ -23,8 +25,12 @@ import javax.servlet.http.HttpServletResponse; import net.bytebuddy.asm.Advice; +import java.util.Enumeration; + public class Servlet3Advice { + static final String APPLICATION_JSON_VALUE = "application/json"; + static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8"; @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) public static boolean onEnter( @Advice.Argument(value = 0, readOnly = false) ServletRequest request, @@ -37,10 +43,8 @@ public static boolean onEnter( if (invalidRequest) { return false; } - - final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletRequest httpServletRequest = (HttpServletRequest) request; final HttpServletResponse httpServletResponse = (HttpServletResponse) response; - Object dispatchSpan = request.getAttribute(DD_DISPATCH_SPAN_ATTRIBUTE); if (dispatchSpan instanceof AgentSpan) { request.removeAttribute(DD_DISPATCH_SPAN_ATTRIBUTE); @@ -54,12 +58,75 @@ public static boolean onEnter( scope = activateSpan(castDispatchSpan); return false; } + String methodType = httpServletRequest.getMethod(); + BodyReaderHttpServletRequestWrapper requestWrapper = null; + Object requestBody = request.getAttribute("datadog_request_body"); + final boolean body = requestBody instanceof Boolean; + String bodyStr = null; + boolean bodyFlag = false; + boolean tracerRequestBodyEnabled = Config.get().isTracerRequestBodyEnabled(); + if (tracerRequestBodyEnabled) { + String contextType = request.getContentType(); + if (!body && "POST".equalsIgnoreCase(methodType) && (contextType.equals(APPLICATION_JSON_VALUE) || contextType.equals(APPLICATION_JSON_UTF8_VALUE))) { + try { + request.setAttribute("datadog_request_body", true); + requestWrapper = new BodyReaderHttpServletRequestWrapper( + (HttpServletRequest) request); + bodyStr = requestWrapper.getBodyStr(); + bodyFlag = true; + request = requestWrapper; + } catch (IOException e) { + e.printStackTrace(); + } + } + } + httpServletResponse.setHeader("guance_trace_id", GlobalTracer.get().getTraceId()); + StringBuffer requestHeader = new StringBuffer(""); + StringBuffer responseHeader = new StringBuffer(""); + boolean tracerHeader = Config.get().isTracerHeaderEnabled(); + if (tracerHeader) { + Enumeration headerNames = httpServletRequest.getHeaderNames(); + int count = 0; + while (headerNames.hasMoreElements()) { + if (count==0){ + requestHeader.append("{"); + }else{ + requestHeader.append(",\n"); + } + String headerName = headerNames.nextElement(); + requestHeader.append("\"").append(headerName).append("\":").append("\"").append(httpServletRequest.getHeader(headerName).replace("\"","")).append("\""); + count ++; + } + if (count>0){ + requestHeader.append("}"); + } + count = 0; + for (String headerName : httpServletResponse.getHeaderNames()) { + if (count==0){ + responseHeader.append("{"); + }else{ + responseHeader.append(",\n"); + } + responseHeader.append("\"").append(headerName).append("\":").append("\"").append(httpServletResponse.getHeader(headerName)).append("\""); + count ++; + } + + if (count>0){ + responseHeader.append("}"); + } + } finishSpan = true; Object spanAttrValue = request.getAttribute(DD_SPAN_ATTRIBUTE); final boolean hasServletTrace = spanAttrValue instanceof AgentSpan; if (hasServletTrace) { + AgentSpan span = (AgentSpan)spanAttrValue; + span.setTag("request_header",requestHeader.toString()); + span.setTag("response_header",responseHeader.toString()); + if (bodyFlag) { + span.setTag("request_body", bodyStr); + } // Tracing might already be applied by other instrumentation, // the FilterChain or a parent request (forward/include). return false; @@ -73,6 +140,11 @@ public static boolean onEnter( DECORATE.afterStart(span); DECORATE.onRequest(span, httpServletRequest, httpServletRequest, extractedContext); + span.setTag("request_header",requestHeader.toString()); + span.setTag("response_header",responseHeader.toString()); + if (bodyFlag) { + span.setTag("request_body", bodyStr); + } httpServletRequest.setAttribute(DD_SPAN_ATTRIBUTE, span); httpServletRequest.setAttribute( CorrelationIdentifier.getTraceIdKey(), GlobalTracer.get().getTraceId()); @@ -115,7 +187,6 @@ public static void stopSpan( if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { final HttpServletResponse resp = (HttpServletResponse) response; - final AgentSpan span = scope.span(); if (request.isAsyncStarted()) { diff --git a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java index 1ef6c582c77..0369c48fd95 100644 --- a/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Instrumentation.java @@ -52,6 +52,8 @@ public String[] helperClassNames() { packageName + ".Servlet3Decorator", packageName + ".ServletRequestURIAdapter", packageName + ".FinishAsyncDispatchListener", + packageName+".BodyReaderHttpServletRequestWrapper", + packageName+".BodyReaderHttpServletRequestWrapper$1", "datadog.trace.instrumentation.servlet.ServletBlockingHelper", }; } diff --git a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DataDogHttpServletResponseWrapper.java b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DataDogHttpServletResponseWrapper.java new file mode 100644 index 00000000000..dc2fca0df46 --- /dev/null +++ b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DataDogHttpServletResponseWrapper.java @@ -0,0 +1,81 @@ +package datadog.trace.instrumentation.springweb; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class DataDogHttpServletResponseWrapper extends HttpServletResponseWrapper { + + private ByteArrayOutputStream buffer; + + private ServletOutputStream out; + + public DataDogHttpServletResponseWrapper(HttpServletResponse httpServletResponse) + { + super(httpServletResponse); + buffer = new ByteArrayOutputStream(); + out = new WrapperOutputStream(buffer); + } + + @Override + public ServletOutputStream getOutputStream() + throws IOException + { + return out; + } + + @Override + public void flushBuffer() + throws IOException + { + if (out != null) + { + out.flush(); + } + } + + public byte[] getContent() + throws IOException + { + flushBuffer(); + return buffer.toByteArray(); + } + + class WrapperOutputStream extends ServletOutputStream + { + private ByteArrayOutputStream bos; + + public WrapperOutputStream(ByteArrayOutputStream bos) + { + this.bos = bos; + } + + @Override + public void write(int b) + throws IOException + { + bos.write(b); + } + + @Override + public boolean isReady() + { + + // TODO Auto-generated method stub + return false; + + } + + @Override + public void setWriteListener(WriteListener arg0) + { + + // TODO Auto-generated method stub + + } + } + + } diff --git a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java index 2a28e42709e..4d21d6e41f3 100644 --- a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/DispatcherServletInstrumentation.java @@ -45,6 +45,8 @@ public String[] helperClassNames() { packageName + ".ServletRequestURIAdapter", packageName + ".HandlerMappingResourceNameFilter", packageName + ".PathMatchingHttpServletRequestWrapper", + packageName + ".DataDogHttpServletResponseWrapper", + packageName + ".DataDogHttpServletResponseWrapper$WrapperOutputStream" }; } diff --git a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerMappingResourceNameFilter.java b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerMappingResourceNameFilter.java index 40e2441af6b..d0ae054f373 100644 --- a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerMappingResourceNameFilter.java +++ b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/HandlerMappingResourceNameFilter.java @@ -3,6 +3,7 @@ import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_SPAN_ATTRIBUTE; import static datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator.DECORATE; +import datadog.trace.api.Config; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import java.io.IOException; import java.util.List; @@ -28,7 +29,7 @@ public class HandlerMappingResourceNameFilter extends OncePerRequestFilter imple @Override protected void doFilterInternal( final HttpServletRequest request, - final HttpServletResponse response, + HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { @@ -46,8 +47,27 @@ protected void doFilterInternal( // mapping.getHandler() threw exception. Ignore } } - - filterChain.doFilter(request, response); + if (!Config.get().isTracerResponseBodyEnabled()){ + filterChain.doFilter(request, response); + return; + } + DataDogHttpServletResponseWrapper wrapper = new DataDogHttpServletResponseWrapper(response); + filterChain.doFilter(request, wrapper); + String contentType = response.getContentType(); + if(contentType==null){ + return; + } + if (contentType.contains("application/json")) { + try { + String responseStr = new String(wrapper.getContent(), "UTF-8"); + ((AgentSpan) parentSpan).setTag("response_body", responseStr); + wrapper.setContentType("application/json;charset="+Config.get().getTracerResponseBodyEncoding()); + response = wrapper; + response.getWriter().write(responseStr); + } catch (Exception e) { + e.printStackTrace(); + } + } } /** diff --git a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/WebApplicationContextInstrumentation.java b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/WebApplicationContextInstrumentation.java index 7b3bb9e373f..27da2990e18 100644 --- a/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/WebApplicationContextInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/WebApplicationContextInstrumentation.java @@ -45,6 +45,8 @@ public String[] helperClassNames() { packageName + ".HandlerMappingResourceNameFilter", packageName + ".HandlerMappingResourceNameFilter$BeanDefinition", packageName + ".PathMatchingHttpServletRequestWrapper", + packageName + ".DataDogHttpServletResponseWrapper", + packageName + ".DataDogHttpServletResponseWrapper$WrapperOutputStream" }; } diff --git a/dd-java-agent/instrumentation/taobao-hsf/build.gradle b/dd-java-agent/instrumentation/taobao-hsf/build.gradle new file mode 100644 index 00000000000..12c67ee53b6 --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/build.gradle @@ -0,0 +1,14 @@ + +apply from: "$rootDir/gradle/java.gradle" + +muzzle { + pass { + group = "com.alibaba.middleware" + module = "hsf-sdk" + versions = "[2.2.8.2--2019-06-stable,)" + } +} + +dependencies { + compileOnly(group: 'com.alibaba.middleware', name: 'hsf-sdk', version: '2.2.8.2--2019-06-stable') +} diff --git a/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFClientInstrumentation.java b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFClientInstrumentation.java new file mode 100644 index 00000000000..353ae3b8436 --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFClientInstrumentation.java @@ -0,0 +1,80 @@ +package datadog.trace.instrumentation.hsf; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.hsf.HSFDecorator.DECORATE; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import com.taobao.hsf.invocation.Invocation; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; + +/** + * @Description + * @Author liurui + * @Date 2022/12/26 9:11 + */ +@AutoService(Instrumenter.class) +public class HSFClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public HSFClientInstrumentation() { + super("hsf-client"); + } + + @Override + public String instrumentedType() { +// CommonClientFilter + return "com.taobao.hsf.common.filter.CommonClientFilter"; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { +// RPCFilter + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(named("invoke")) + .and(takesArgument(1, named("com.taobao.hsf.invocation.Invocation"))), + this.getClass().getName() + "$ClientInvokeAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".HSFDecorator", + packageName + ".HSFExtractAdapter", + packageName + ".HSFInjectAdapter", + }; + } + + // RPCFilter + public static class ClientInvokeAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope before(@Advice.This Object filter, + @Advice.Argument(1) final Invocation invocation + ) { + AgentSpan span = DECORATE.buildClientSpan(invocation); + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATE.onError(scope.span(), throwable); + DECORATE.beforeFinish(scope.span()); + + scope.close(); + scope.span().finish(); + } + } + + +} diff --git a/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFDecorator.java b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFDecorator.java new file mode 100644 index 00000000000..5df04556e80 --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFDecorator.java @@ -0,0 +1,70 @@ +package datadog.trace.instrumentation.hsf; + +import com.taobao.hsf.context.RPCContext; +import com.taobao.hsf.invocation.Invocation; +import com.taobao.hsf.util.PojoUtils; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.hsf.HSFExtractAdapter.GETTER; +import static datadog.trace.instrumentation.hsf.HSFInjectAdapter.SETTER; + +/** + * @Description + * @Author liurui + * @Date 2022/12/26 10:18 + */ +public class HSFDecorator extends BaseDecorator { + public static final HSFDecorator DECORATE = new HSFDecorator(); + @Override + protected String[] instrumentationNames() { + return new String[]{"taobao-hsf","HSF"}; + } + + @Override + protected CharSequence spanType() { + return "hsf"; + } + + @Override + protected CharSequence component() { + return "taobao-hsf"; + } + + public AgentSpan buildClientSpan(Invocation invocation){ + Invocation.ClientInvocationContext context = invocation.getClientInvocationContext(); + String methodInterface = context.getMethodModel().getUniqueName(); + String methodName = context.getMethodModel().getMethodName(); + AgentSpan span = startSpan(component()); + span.setResourceName(methodInterface +":"+ methodName); + span.setTag("invoke_type",context.getMethodModel().getInvokeType().toLowerCase()); + afterStart(span); + span.setTag("args",methodArgs(invocation)); + span.setTag("argsV",methodArgsV(invocation)); + propagate().inject(span, RPCContext.getClientContext(), SETTER); + return span; + } + + public AgentSpan buildServerSpan(Invocation invocation){ + AgentSpan.Context parentContext = propagate().extract(RPCContext.getServerContext(), GETTER); + AgentSpan span = startSpan(component(),parentContext); + + span.setResourceName(invocation.getServerInvocationContext().getMetadata().getUniqueName()); + afterStart(span); + return span; + } + + private String methodArgs(Invocation invocation){ + String[] sigs = invocation.getMethodArgSigs(); + return Arrays.stream(sigs).collect(Collectors.joining(",")); + } + private String methodArgsV(Invocation invocation){ + Object [] args = invocation.getMethodArgs(); + return Arrays.stream(args).map(arg->PojoUtils.generalize(arg).toString()).collect(Collectors.joining(",")); + } +} diff --git a/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFExtractAdapter.java b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFExtractAdapter.java new file mode 100644 index 00000000000..ff2d2e48c9f --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFExtractAdapter.java @@ -0,0 +1,29 @@ +package datadog.trace.instrumentation.hsf; + +import com.taobao.hsf.context.RPCContext; +import com.taobao.hsf.invocation.Invocation; +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class HSFExtractAdapter implements AgentPropagation.ContextVisitor{ + private static final Logger log = LoggerFactory.getLogger(HSFExtractAdapter.class); + public static final HSFExtractAdapter GETTER = new HSFExtractAdapter(); + @Override + public void forEachKey(RPCContext carrier, AgentPropagation.KeyClassifier classifier) { + Map objectAttachments = carrier.getAttachments(); + if (log.isDebugEnabled()) { + log.debug("Extract size: {}",objectAttachments.entrySet().size()); + } + for (Map.Entry entry : objectAttachments.entrySet()){ + log.debug("Extract "+entry.getKey()+"\t"+entry.getValue()); + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey().toString(), entry.getValue().toString())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFInjectAdapter.java b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFInjectAdapter.java new file mode 100644 index 00000000000..3982b83a3c2 --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFInjectAdapter.java @@ -0,0 +1,17 @@ +package datadog.trace.instrumentation.hsf; + +import com.taobao.hsf.context.RPCContext; +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HSFInjectAdapter implements AgentPropagation.Setter { + public static final HSFInjectAdapter SETTER = new HSFInjectAdapter(); + private static final Logger log = LoggerFactory.getLogger(HSFInjectAdapter.class); + @Override + public void set(RPCContext carrier, String key, String value) { + log.debug("hsf Inject " + key + ":\t" + value); + carrier.putAttachment(key, value); +// carrier.getAttachments().put(key, value); + } +} diff --git a/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFServerInstrumentation.java b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFServerInstrumentation.java new file mode 100644 index 00000000000..c44553deb10 --- /dev/null +++ b/dd-java-agent/instrumentation/taobao-hsf/src/main/java/datadog/trace/instrumentation/hsf/HSFServerInstrumentation.java @@ -0,0 +1,77 @@ +package datadog.trace.instrumentation.hsf; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.hsf.HSFDecorator.DECORATE; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import com.taobao.hsf.invocation.Invocation; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; + +/** + * @Description + * @Author liurui + * @Date 2022/12/26 9:11 + */ +@AutoService(Instrumenter.class) +public class HSFServerInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public HSFServerInstrumentation() { + super("hsf-server"); + } + + @Override + public String instrumentedType() { + return "com.taobao.hsf.common.filter.CommonServerFilter"; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { +// RPCFilter + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(named("invoke")) + .and(takesArgument(1, named("com.taobao.hsf.invocation.Invocation"))), + this.getClass().getName() + "$ServerInvokeAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".HSFDecorator", + packageName + ".HSFExtractAdapter", + packageName + ".HSFInjectAdapter", + }; + } + + // RPCFilter + public static class ServerInvokeAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope before(@Advice.This Object filter, + @Advice.Argument(1) final Invocation invocation + ) { + AgentSpan span = DECORATE.buildServerSpan(invocation); + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATE.onError(scope.span(), throwable); + DECORATE.beforeFinish(scope.span()); + + scope.close(); + scope.span().finish(); + } + } +} diff --git a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGClassListener.java b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGClassListener.java index 4ec715d1d1b..265e9ca7e59 100644 --- a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGClassListener.java +++ b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGClassListener.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.testng.IMethodInstance; diff --git a/dd-java-agent/instrumentation/thrift/build.gradle b/dd-java-agent/instrumentation/thrift/build.gradle new file mode 100644 index 00000000000..737fb9e3174 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/build.gradle @@ -0,0 +1,26 @@ +ext { + minJavaVersionForTests = JavaVersion.VERSION_1_8 +} + +muzzle { + pass { + group = "org.apache.thrift" + module = "libthrift" + versions = "[0.9.3,)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest','test') + +dependencies { + compileOnly(group: 'org.apache.thrift', name: 'libthrift', version: '0.9.3') + main_java8CompileOnly(group: 'org.apache.thrift', name: 'libthrift', version: '0.9.3') +// testImplementation group: 'org.apache.thrift', name: 'libthrift', version: '0.11.0' +} + +test { + useJUnitPlatform() +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AbstractContext.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AbstractContext.java new file mode 100644 index 00000000000..d4f02823231 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AbstractContext.java @@ -0,0 +1,26 @@ +package datadog.trace.instrumentation.thrift; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public abstract class AbstractContext { + public String methodName; + public long startTime = 0L; + public boolean createdSpan = false; + + public abstract String getArguments(); + + public abstract String getOperatorName(); + + public final void setup(String methodName) { + this.methodName = methodName; + this.startTime = MILLISECONDS.toMicros(System.currentTimeMillis()); + } + + public boolean isCreatedSpan() { + return createdSpan; + } + + public void setCreatedSpan(boolean createdSpan) { + this.createdSpan = createdSpan; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncContext.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncContext.java new file mode 100644 index 00000000000..dff12e555c2 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncContext.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package datadog.trace.instrumentation.thrift; + +import java.util.Map; +import org.apache.thrift.AsyncProcessFunction; + +public class AsyncContext extends AbstractContext { + private final Map processMapView; + + public AsyncContext(Map processMapView) { + this.processMapView = processMapView; + } + + @Override + public String getArguments() { +// for (Map.Entry entry : processMapView.entrySet()){ +// System.out.println("ARGS1:"+entry.getKey()+"\t"+entry.getValue().getClass().getName()); +// +// System.out.println("ARGS2:"+entry.getValue().getEmptyArgsInstance().toString()); +// } + if (processMapView==null){ + return null; + } + AsyncProcessFunction function = processMapView.get(methodName); + if (function==null){ + return null; + } + return function.getEmptyArgsInstance().toString(); + } + + @Override + public String getOperatorName() { + if (processMapView==null){ + return null; + } + AsyncProcessFunction function = processMapView.get(methodName); + if (function==null){ + return null; + } + return function.getClass().getName(); + } + +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallConstructorAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallConstructorAdvice.java new file mode 100644 index 00000000000..1fe515e0154 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallConstructorAdvice.java @@ -0,0 +1,31 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncMethodCall; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope; + +public class AsyncMethodCallConstructorAdvice { + private static final Logger logger = LoggerFactory.getLogger(AsyncMethodCallConstructorAdvice.class); + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void after(@Advice.This TAsyncMethodCall objInst + , @Advice.AllArguments final Object[] args) { + if (args[3] instanceof AsyncMethodCallback) { + final AsyncMethodCallback callback = (AsyncMethodCallback) args[3]; + try { + ThriftConstants.setValue(TAsyncMethodCall.class, objInst, "callback", new DataDogAsyncMethodCallback(callback,null)); + } catch (Exception e) { + logger.error("set value error:", e); + } + } + +// if (allArguments[2] instanceof EnhancedInstance) { +// remotePeer = (String) ((EnhancedInstance) allArguments[2]).getSkyWalkingDynamicField(); +// } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallMethodAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallMethodAdvice.java new file mode 100644 index 00000000000..d0b0ac6dd81 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/AsyncMethodCallMethodAdvice.java @@ -0,0 +1,33 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncMethodCall; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.thrift.ThriftClientDecorator.CLIENT_DECORATOR; + +public class AsyncMethodCallMethodAdvice { + public static final Logger logger = LoggerFactory.getLogger(AsyncMethodCallMethodAdvice.class); + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final TAsyncMethodCall methodCall, + @Advice.AllArguments final Object[] args, + @Advice.FieldValue("callback") final AsyncMethodCallback callback) { + AgentSpan agentSpan = CLIENT_DECORATOR.createSpan(methodCall.getClass().getName(), null); + AgentScope scope = activateSpan(agentSpan); + try { + ThriftConstants.setValue(TAsyncMethodCall.class, methodCall, "callback", new DataDogAsyncMethodCallback(callback, scope)); + } catch (Exception e) { + if (logger.isDebugEnabled()){ + logger.debug("set value callback fail",e); + } + logger.error("set value callback fail",e); + } + return scope; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/CTProtocolFactory.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/CTProtocolFactory.java new file mode 100644 index 00000000000..7c4ccab799f --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/CTProtocolFactory.java @@ -0,0 +1,17 @@ +package datadog.trace.instrumentation.thrift; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TTransport; + +public class CTProtocolFactory implements TProtocolFactory { + TProtocolFactory inputProtocolFactory; + public CTProtocolFactory(TProtocolFactory inputProtocolFactory){ + this.inputProtocolFactory = inputProtocolFactory; + } + @Override + public TProtocol getProtocol(TTransport tTransport) { + ClientOutProtocolWrapper wrapper = new ClientOutProtocolWrapper(inputProtocolFactory.getProtocol(tTransport)); + return wrapper; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ClientOutProtocolWrapper.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ClientOutProtocolWrapper.java new file mode 100644 index 00000000000..9388cb05bd0 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ClientOutProtocolWrapper.java @@ -0,0 +1,67 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; +import static datadog.trace.instrumentation.thrift.InjectAdepter.SETTER; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; + +/** + * Wrapping client output protocol for injecting and propagating the trace header. This is also safe even if the server + * doesn't deal with it. + */ +public class ClientOutProtocolWrapper extends TProtocolDecorator { + + private static final Logger log = LoggerFactory.getLogger(ClientOutProtocolWrapper.class); + + public ClientOutProtocolWrapper(TProtocol protocol) { + super(protocol); + } + + @Override + public final void writeMessageBegin(final TMessage message) throws TException { + CLIENT_INJECT_THREAD.set(false); + super.writeMessageBegin(message); + } + + @Override + public final void writeFieldStop() throws TException { + AgentSpan span = activeSpan(); + boolean injected = CLIENT_INJECT_THREAD.get(); + if (!injected && Optional.ofNullable(span).isPresent()) { + try { + Map map = new HashMap<>(); + propagate().inject(span, map, SETTER); + writeHeader(map); + } catch (Throwable throwable) { + if (log.isDebugEnabled()) { + log.error("inject exception", throwable); + } + } finally { + CLIENT_INJECT_THREAD.set(true); + } + } + super.writeFieldStop(); + } + + private void writeHeader(Map header) throws TException { + super.writeFieldBegin(new TField(DD_MAGIC_FIELD, TType.MAP, DD_MAGIC_FIELD_ID)); + super.writeMapBegin(new TMap(TType.STRING, TType.STRING, header.size())); + + final Set> entries = header.entrySet(); + for (Map.Entry entry : entries) { + super.writeString(entry.getKey()); + super.writeString(entry.getValue()); + } + + super.writeMapEnd(); + super.writeFieldEnd(); + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/Context.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/Context.java new file mode 100644 index 00000000000..a949ba6c6cb --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/Context.java @@ -0,0 +1,38 @@ +package datadog.trace.instrumentation.thrift; + +import org.apache.thrift.ProcessFunction; + +import java.util.Map; + +public class Context extends AbstractContext { + private Map processMapView; + + public Context(Map processMapView) { + this.processMapView = processMapView; + } + + @Override + public String getArguments() { + if (processMapView==null){ + return null; + } + ProcessFunction function = processMapView.get(methodName); + if (function==null){ + return null; + } + return function.getEmptyArgsInstance().toString(); + } + + @Override + public String getOperatorName() { + if (processMapView==null){ + return null; + } + ProcessFunction function = processMapView.get(methodName); + if (function==null){ + return methodName; + } + return function.getClass().getName(); + } + +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/DataDogAsyncMethodCallback.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/DataDogAsyncMethodCallback.java new file mode 100644 index 00000000000..aa325dae332 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/DataDogAsyncMethodCallback.java @@ -0,0 +1,57 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import org.apache.thrift.async.AsyncMethodCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Optional; + +import static datadog.trace.instrumentation.thrift.ThriftClientDecorator.CLIENT_DECORATOR; +import static datadog.trace.instrumentation.thrift.ThriftConstants.CLIENT_INJECT_THREAD; + +public class DataDogAsyncMethodCallback implements AsyncMethodCallback { + public static final Logger logger = LoggerFactory.getLogger(DataDogAsyncMethodCallback.class); + final AsyncMethodCallback callback; + AgentScope scope; + + public DataDogAsyncMethodCallback(AsyncMethodCallback callback, AgentScope scope) { + this.callback = callback; + this.scope = scope; + logger.debug("init DataDogAsyncMethodCallback"); + } + + @Override + public void onComplete(final Object response) { + logger.debug("do onComplete"); + if (!Optional.ofNullable(scope).isPresent()) { + return; + } + try { + logger.debug("onComplete scope is not null,thread:" + Thread.currentThread().getName()); + CLIENT_DECORATOR.onError(scope.span(), null); + CLIENT_DECORATOR.beforeFinish(scope.span()); + CLIENT_INJECT_THREAD.remove(); + scope.close(); + scope.span().finish(); + } finally { + callback.onComplete(response); + } + } + + @Override + public void onError(final Exception exception) { + if (!Optional.ofNullable(scope).isPresent()) { + return; + } + try { + CLIENT_DECORATOR.onError(scope.span(), exception); + CLIENT_DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + CLIENT_INJECT_THREAD.remove(); + } finally { + callback.onError(exception); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ExtractAdepter.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ExtractAdepter.java new file mode 100644 index 00000000000..fc00f658d50 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ExtractAdepter.java @@ -0,0 +1,20 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; + +import java.util.Map; + +public class ExtractAdepter implements AgentPropagation.ContextVisitor> { + public static final ExtractAdepter GETTER = new ExtractAdepter(); + + @Override + public void forEachKey(Map carrier, AgentPropagation.KeyClassifier classifier) { + for (Map.Entry entry : carrier.entrySet()){ + if (null != entry.getValue()) { + if (!classifier.accept(entry.getKey(), entry.getValue())) { + return; + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/InjectAdepter.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/InjectAdepter.java new file mode 100644 index 00000000000..25bebc07929 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/InjectAdepter.java @@ -0,0 +1,15 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; + +import java.util.Map; + +public class InjectAdepter implements AgentPropagation.Setter> { + + public static final InjectAdepter SETTER = new InjectAdepter(); + + @Override + public void set(final Map carrier, final String key, final String value) { + carrier.put(key, value); + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/STProtocolFactory.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/STProtocolFactory.java new file mode 100644 index 00000000000..2bf9b8cf434 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/STProtocolFactory.java @@ -0,0 +1,17 @@ +package datadog.trace.instrumentation.thrift; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TTransport; + +public class STProtocolFactory implements TProtocolFactory { + TProtocolFactory inputProtocolFactory; + public STProtocolFactory(TProtocolFactory inputProtocolFactory){ + this.inputProtocolFactory = inputProtocolFactory; + } + @Override + public TProtocol getProtocol(TTransport tTransport) { + ServerInProtocolWrapper wrapper = new ServerInProtocolWrapper(inputProtocolFactory.getProtocol(tTransport)); + return wrapper; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ServerInProtocolWrapper.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ServerInProtocolWrapper.java new file mode 100644 index 00000000000..6053d30fe62 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ServerInProtocolWrapper.java @@ -0,0 +1,99 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.thrift.ThriftConstants.CONTEXT_THREAD; +import static datadog.trace.instrumentation.thrift.ThriftServerDecorator.SERVER_DECORATOR; + + +public class ServerInProtocolWrapper extends TProtocolDecorator { + public static final Logger logger = LoggerFactory.getLogger(ServerInProtocolWrapper.class); + + public ServerInProtocolWrapper(TProtocol protocol) { + super(protocol); + } + + public void initial(AbstractContext context) { + CONTEXT_THREAD.set(context); + } + + @Override + public TField readFieldBegin() throws TException { + final TField field = super.readFieldBegin(); + if (field.id == ThriftConstants.DD_MAGIC_FIELD_ID && field.type == TType.MAP) { + try { + TMap tMap = super.readMapBegin(); + Map header = new HashMap<>(tMap.size); + + for (int i = 0; i < tMap.size; i++) { + String key = readString(); + String value = readString(); + header.put(key, value); + } + AbstractContext context = CONTEXT_THREAD.get(); + context.setCreatedSpan(true); + AgentSpan span = SERVER_DECORATOR.createSpan(header, context); + CONTEXT_THREAD.set(context); + activateSpan(span); + } catch (Throwable throwable) { + logger.error("readFieldBegin exception", throwable); + throw throwable; + } finally { + super.readMapEnd(); + super.readFieldEnd(); +// readFieldEnd(); + } + return readFieldBegin(); + } + + return field; + } + +// @Override +// public void readFieldEnd() { +// Throwable throwable = null; +// try { +// super.readFieldEnd(); +// } catch (TException e) { +// e.printStackTrace(); +// throwable = new RuntimeException(e); +// } +// logger.info("ServerInProtocolWrapper readFieldEnd time:"+System.currentTimeMillis()); +// if (Optional.ofNullable(CONTEXT_THREAD.get()).isPresent() && CONTEXT_THREAD.get().isCreatedSpan()) { +// AgentScope scope = activeScope(); +// SERVER_DECORATOR.onError(scope.span(), throwable); +// SERVER_DECORATOR.beforeFinish(scope.span()); +// +// scope.close(); +// scope.span().finish(); +// +// CONTEXT_THREAD.remove(); +// logger.info("ServerInProtocolWrapper end span time:"+System.currentTimeMillis()); +// logger.info("ServerInProtocolWrapper remove CONTEXT_THREAD"); +// } +// } + + @Override + public TMessage readMessageBegin() throws TException { + final TMessage message = super.readMessageBegin(); + if (Objects.nonNull(message)) { + AbstractContext context = CONTEXT_THREAD.get(); + if (context == null) { + context = new Context(null); + CONTEXT_THREAD.set(context); + } + context.setup(message.name); + } + return message; + } + +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientConstructorAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientConstructorAdvice.java new file mode 100644 index 00000000000..7b1fd394dce --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientConstructorAdvice.java @@ -0,0 +1,29 @@ +package datadog.trace.instrumentation.thrift; + +import net.bytebuddy.asm.Advice; +import org.apache.thrift.TServiceClient; +import org.apache.thrift.async.TAsyncClient; +import org.apache.thrift.async.TAsyncMethodCall; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.TServer; + +public class TAsyncClientConstructorAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.This TAsyncClient tAsyncClient + , @Advice.FieldValue("___protocolFactory") TProtocolFactory ___protocolFactory + ) throws NoSuchFieldException, IllegalAccessException { +// TProtocolFactory inputProtocolFactory = (TProtocolFactory) ThriftConstants.getValue( +// TAsyncClient.class, +// tAsyncClient, +// "___protocolFactory" +// ); + + ThriftConstants.setValue( + TAsyncClient.class, + tAsyncClient, + "___protocolFactory", + new CTProtocolFactory(___protocolFactory) + ); + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientInstrumentation.java new file mode 100644 index 00000000000..ded1197a703 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncClientInstrumentation.java @@ -0,0 +1,53 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.thrift.ThriftConstants.INSTRUMENTATION_NAME; +import static datadog.trace.instrumentation.thrift.ThriftConstants.TASYNC_CLIENT; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class TAsyncClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public TAsyncClientInstrumentation() { + super(INSTRUMENTATION_NAME); + } + + @Override + public String hierarchyMarkerType() { + return TASYNC_CLIENT; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftClientDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ClientOutProtocolWrapper", + packageName + ".TAsyncClientConstructorAdvice", + packageName + ".InjectAdepter", + packageName + ".CTProtocolFactory", + packageName + ".Context" + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isConstructor() + ,packageName + ".TAsyncClientConstructorAdvice"); + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncMethodCallInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncMethodCallInstrumentation.java new file mode 100644 index 00000000000..2e7832abf29 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TAsyncMethodCallInstrumentation.java @@ -0,0 +1,61 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.thrift.ThriftConstants.INSTRUMENTATION_NAME; +import static datadog.trace.instrumentation.thrift.ThriftConstants.T_ASYNC_METHOD_CALL; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class TAsyncMethodCallInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public TAsyncMethodCallInstrumentation() { + super(INSTRUMENTATION_NAME); + } + + @Override + public String hierarchyMarkerType() { + return T_ASYNC_METHOD_CALL; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftClientDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".AsyncContext", + packageName + ".Context", + packageName + ".ClientOutProtocolWrapper", + packageName + ".AsyncMethodCallConstructorAdvice", + packageName + ".AsyncMethodCallMethodAdvice", + packageName + ".DataDogAsyncMethodCallback", + packageName + ".InjectAdepter", + packageName + ".CTProtocolFactory" + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isConstructor() + ,packageName+ ".AsyncMethodCallConstructorAdvice"); + transformation.applyAdvice(isMethod() + .and(isProtected()) + .and(named("prepareMethodCall")) + ,packageName + ".AsyncMethodCallMethodAdvice"); + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseAsyncProcessorInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseAsyncProcessorInstrumentation.java new file mode 100644 index 00000000000..58e445fae3f --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseAsyncProcessorInstrumentation.java @@ -0,0 +1,91 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static datadog.trace.instrumentation.thrift.ThriftServerDecorator.SERVER_DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.TBaseAsyncProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.server.AbstractNonblockingServer; + +@AutoService(Instrumenter.class) +public class TBaseAsyncProcessorInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public TBaseAsyncProcessorInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_SERVER); + } + + @Override + public String hierarchyMarkerType() { + return T_BASE_ASYNC_PROCESSOR; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(hierarchyMarkerType())); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("process")) + , getClass().getName() + "$AsyncProcessAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ServerInProtocolWrapper", + packageName + ".ExtractAdepter", + packageName + ".CTProtocolFactory", + packageName + ".STProtocolFactory", + packageName + ".ThriftServerDecorator", + packageName + ".AsyncContext", + packageName + ".Context" + }; + } + + public static class AsyncProcessAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final TBaseAsyncProcessor tBaseAsyncProcessor + , @Advice.AllArguments final Object[] args) { + try { + System.out.println("do AsyncProcessAdvice onEnter"); + TProtocol protocol = ((AbstractNonblockingServer.AsyncFrameBuffer) args[0]).getInputProtocol(); + ((ServerInProtocolWrapper) protocol).initial(new AsyncContext(tBaseAsyncProcessor.getProcessMapView())); + }catch (Exception e){ + e.printStackTrace(); + } + return activateSpan(noopSpan()); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void after(@Advice.Thrown final Throwable throwable) { + AgentScope scope = activeScope(); + if (scope != null) { + SERVER_DECORATOR.onError(scope.span(), throwable); + SERVER_DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + CONTEXT_THREAD.remove(); + } + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseProcessorInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseProcessorInstrumentation.java new file mode 100644 index 00000000000..f33592de3ba --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TBaseProcessorInstrumentation.java @@ -0,0 +1,94 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static datadog.trace.instrumentation.thrift.ThriftServerDecorator.SERVER_DECORATOR; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.TBaseProcessor; + +@AutoService(Instrumenter.class) +public class TBaseProcessorInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public TBaseProcessorInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_SERVER); + } + + @Override + public String hierarchyMarkerType() { + return T_BASE_PROCESSOR; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(T_BASE_PROCESSOR)); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("process")) + , getClass().getName() + "$ProcessAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ServerInProtocolWrapper", + packageName + ".ExtractAdepter", + packageName + ".CTProtocolFactory", + packageName + ".STProtocolFactory", + packageName + ".ThriftServerDecorator", + packageName + ".Context" + }; + } + + public static class ProcessAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void before(@Advice.This final Object obj + , @Advice.AllArguments final Object[] args) { + System.out.println("do ProcessAdvice onEnter"); + if (obj instanceof TBaseProcessor) { + try { + Object in = args[0]; + if (in instanceof ServerInProtocolWrapper) { + TBaseProcessor tBaseProcessor = (TBaseProcessor) obj; + ((ServerInProtocolWrapper) in).initial(new Context(tBaseProcessor.getProcessMapView())); + } + }catch (Exception e){ + e.printStackTrace(); + throw e; + } + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void after(@Advice.Thrown final Throwable throwable) { + AgentScope scope = activeScope(); + if (scope!=null) { + System.out.println("finish ProcessAdvice span."); + SERVER_DECORATOR.onError(scope.span(), throwable); + SERVER_DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + CONTEXT_THREAD.remove(); + } + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TClientConstructorAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TClientConstructorAdvice.java new file mode 100644 index 00000000000..6e2d17d3530 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TClientConstructorAdvice.java @@ -0,0 +1,21 @@ +package datadog.trace.instrumentation.thrift; + +import net.bytebuddy.asm.Advice; +import org.apache.thrift.TServiceClient; +import org.apache.thrift.protocol.TProtocol; + +public class TClientConstructorAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.This TServiceClient tServiceClient + , @Advice.FieldValue("oprot_") TProtocol oprot_ + ) throws NoSuchFieldException, IllegalAccessException { + if (!(oprot_ instanceof ClientOutProtocolWrapper)) { + ThriftConstants.setValue( + TServiceClient.class, + tServiceClient, + "oprot_", + new ClientOutProtocolWrapper(oprot_) + ); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TMultiplexedProcessorInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TMultiplexedProcessorInstrumentation.java new file mode 100644 index 00000000000..a10653191b8 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TMultiplexedProcessorInstrumentation.java @@ -0,0 +1,113 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.noopSpan; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import java.util.HashMap; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import org.apache.thrift.ProcessFunction; +import org.apache.thrift.TMultiplexedProcessor; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; + +@AutoService(Instrumenter.class) +public class TMultiplexedProcessorInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + + + public TMultiplexedProcessorInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_SERVER); + } + + @Override + public String instrumentedType() { + return T_MULTIPLEXED_PROCESSOR; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isConstructor() + , getClass().getName() + "$TMultiplexedProcessorConstructorAdvice"); + + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("process")) + , getClass().getName() + "$TMultiplexedProcessorProcessAdvice"); + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("registerProcessor")) + , getClass().getName() + "$TMultiplexedProcessorRegisterProcessAdvice"); + //0.12以上版本 + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("registerDefault")) + , getClass().getName() + "$TMultiplexedProcessorRegisterDefaultAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ServerInProtocolWrapper", + packageName + ".ExtractAdepter", + packageName + ".CTProtocolFactory", + packageName + ".STProtocolFactory", + packageName + ".ThriftServerDecorator", + packageName + ".Context" + }; + } + + public static class TMultiplexedProcessorConstructorAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void after(@Advice.This final TMultiplexedProcessor processor) { + TM_M.put(processor,new HashMap()); + } + } + + public static class TMultiplexedProcessorProcessAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final TMultiplexedProcessor processor,@Advice.AllArguments final Object[] args) { + try { + TProtocol protocol = (TProtocol) args[0]; + ((ServerInProtocolWrapper) protocol).initial(new Context(TM_M.get(processor))); + } catch (Exception e) { + throw e; + } + return activateSpan(noopSpan()); + } + } + + public static class TMultiplexedProcessorRegisterProcessAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final TMultiplexedProcessor obj,@Advice.AllArguments final Object[] allArguments) { + Map processMap = TM_M.get(obj); + String serviceName = (String) allArguments[0]; + TProcessor processor = (TProcessor) allArguments[1]; + processMap.putAll(ThriftConstants.getProcessMap(serviceName, processor)); + TM_M.put(obj,processMap); + return activateSpan(noopSpan()); + } + } + + public static class TMultiplexedProcessorRegisterDefaultAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final TMultiplexedProcessor obj,@Advice.AllArguments final Object[] allArguments) { + Map processMap = TM_M.get(obj); + TProcessor processor = (TProcessor) allArguments[0]; + processMap.putAll(ThriftConstants.getProcessMap(processor)); + TM_M.put(obj,processMap); + return activateSpan(noopSpan()); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorInstrumentation.java new file mode 100644 index 00000000000..d1d616d9065 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorInstrumentation.java @@ -0,0 +1,66 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +/** + * @Description + * @Author lenovo + * @Date 2022/11/24 9:34 + */ +@AutoService(Instrumenter.class) +public class TProcessorInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public TProcessorInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_SERVER); + } + + @Override + public String hierarchyMarkerType() { + return T_PROCESSOR; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(namedOneOf(hierarchyMarkerType(),T_ASYNC_PROCESSOR)) + .and(not(named(T_BASE_ASYNC_PROCESSOR))) + .and(not(named(T_BASE_PROCESSOR))) + .and(not(named(T_MULTIPLEXED_PROCESSOR))); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isMethod() + .and(isPublic()) + .and(named("process")) + , packageName + ".TProcessorProcessAdvice"); + + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ServerInProtocolWrapper", + packageName + ".ExtractAdepter", + packageName + ".CTProtocolFactory", + packageName + ".STProtocolFactory", + packageName + ".ThriftServerDecorator", + packageName + ".TProcessorProcessAdvice", + packageName + ".Context" + }; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorProcessAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorProcessAdvice.java new file mode 100644 index 00000000000..248d2448265 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TProcessorProcessAdvice.java @@ -0,0 +1,44 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.thrift.ThriftConstants.CONTEXT_THREAD; +import static datadog.trace.instrumentation.thrift.ThriftServerDecorator.SERVER_DECORATOR; + +/** + * @Description + * @Author lenovo + * @Date 2022/11/25 11:12 + */ +public class TProcessorProcessAdvice { + public static final Logger logger = LoggerFactory.getLogger(TProcessorProcessAdvice.class); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This final Object obj, @Advice.AllArguments final Object[] args) { + logger.info("TProcessorProcessAdvice : " + obj.getClass().getName()); + try { + Object in = args[0]; + if (in instanceof ServerInProtocolWrapper) { + ((ServerInProtocolWrapper) in).initial(new Context(null)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return activateSpan(noopSpan()); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void after(@Advice.Thrown final Throwable throwable) { + AgentScope scope = activeScope(); + if (scope != null) { + SERVER_DECORATOR.onError(scope.span(), throwable); + SERVER_DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + CONTEXT_THREAD.remove(); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerConstructorAdvice.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerConstructorAdvice.java new file mode 100644 index 00000000000..f15eeee4f79 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerConstructorAdvice.java @@ -0,0 +1,29 @@ +package datadog.trace.instrumentation.thrift; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.TServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TServerConstructorAdvice { + private static final Logger logger = LoggerFactory.getLogger(TServerConstructorAdvice.class); + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This(typing = Assigner.Typing.DYNAMIC) TServer tServer, + @Advice.FieldValue(value = "inputProtocolFactory_", readOnly = false, typing = Assigner.Typing.DYNAMIC) TProtocolFactory inputProtocolFactory_ + ) { + try { + TProtocolFactory trans = new STProtocolFactory(inputProtocolFactory_); + ThriftConstants.setValue( + TServer.class, + tServer, + "inputProtocolFactory_", + trans + ); + } catch (Exception e) { + logger.debug("TServerConstructorAdvice exception:", e); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerInstrumentation.java new file mode 100644 index 00000000000..7e133c80a1f --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServerInstrumentation.java @@ -0,0 +1,54 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class TServerInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + public TServerInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_SERVER); + } + + @Override + public String hierarchyMarkerType() { + return T_SERVER; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(hierarchyMarkerType())); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftClientDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ServerInProtocolWrapper", + packageName + ".ExtractAdepter", + packageName + ".TServerConstructorAdvice", + packageName + ".STProtocolFactory", + packageName + ".ThriftServerDecorator", + packageName + ".Context" + }; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { +// TServer + transformation.applyAdvice(isConstructor() + ,packageName + ".TServerConstructorAdvice"); + } + +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServiceClientInstrumentation.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServiceClientInstrumentation.java new file mode 100644 index 00000000000..cae98aedb4f --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/TServiceClientInstrumentation.java @@ -0,0 +1,119 @@ +package datadog.trace.instrumentation.thrift; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.*; +import static datadog.trace.instrumentation.thrift.ThriftClientDecorator.CLIENT_DECORATOR; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.TBase; +import org.apache.thrift.TServiceClient; + +@AutoService(Instrumenter.class) +public class TServiceClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + public TServiceClientInstrumentation() { + super(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME_CLIENT); + } + + @Override + public String hierarchyMarkerType() { + return TSERVICE_CLIENT; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return extendsClass(named(hierarchyMarkerType())); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice(isConstructor() + .and(takesArgument(1, named("org.apache.thrift.protocol.TProtocol"))) + , packageName + ".TClientConstructorAdvice"); + + transformation.applyAdvice( + isMethod() + .and(isPrivate()) + .and(named("sendBase")) + .and(takesArguments(3)) + .and(takesArgument(0, String.class)) + .and(takesArgument(1, named("org.apache.thrift.TBase"))), + getClass().getName() + "$SendBaseAdvice"); + + transformation.applyAdvice( + isMethod() + .and(isProtected()) +// .and(takesArgument(1,String.class)) + .and(named("receiveBase")), + getClass().getName() + "$ReceiveBaseAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".ThriftConstants", + packageName + ".ThriftBaseDecorator", + packageName + ".ThriftClientDecorator", + packageName + ".ThriftConstants$Tags", + packageName + ".AbstractContext", + packageName + ".ClientOutProtocolWrapper", + packageName + ".ServerInProtocolWrapper", + packageName + ".TClientConstructorAdvice", + packageName + ".InjectAdepter", + packageName + ".Context" + }; + } + + public static class SendBaseAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope onEnter(@Advice.This TServiceClient tServiceClient, + @Advice.Argument(0) String methodName, + @Advice.Argument(1) TBase tb) { + AgentSpan span = CLIENT_DECORATOR.createSpan(methodName, tb); + AgentScope agentScope = activateSpan(span); + return agentScope; + } + +// @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) +// public static void onExit( +// @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { +// if (scope == null) { +// return; +// } +// System.out.println("SendBaseAdvice: onExit close span "); +// CLIENT_DECORATOR.onError(scope.span(), throwable); +// CLIENT_DECORATOR.beforeFinish(scope.span()); +// scope.close(); +// scope.span().finish(); +// CLIENT_INJECT_THREAD.remove(); +// System.out.println("CLIENT_INJECT_THREAD remove"); +// } + } + + public static class ReceiveBaseAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Thrown final Throwable throwable) { + AgentScope scope = activeScope(); + if (scope==null){ + return; + } + CLIENT_DECORATOR.onError(scope.span(), throwable); + CLIENT_DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + CLIENT_INJECT_THREAD.remove(); + } + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftBaseDecorator.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftBaseDecorator.java new file mode 100644 index 00000000000..5eef8c9843a --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftBaseDecorator.java @@ -0,0 +1,41 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator; +import org.apache.thrift.TBase; +import org.apache.thrift.TFieldIdEnum; + +public abstract class ThriftBaseDecorator extends BaseDecorator { + + public void withMethod(final AgentSpan span, final String methodName) { + span.setTag(ThriftConstants.Tags.METHOD,methodName); + } + public void withResource(final AgentSpan span, final String resourceName) { + span.setResourceName(resourceName); + } + + public void withArgs(final AgentSpan span, final String methodName,TBase tb) { + if (tb!=null) { + span.setTag(ThriftConstants.Tags.ARGS, getArguments(methodName, tb)); + } + } + + public String getArguments(String method, TBase base) { + int idx = 0; + StringBuilder buffer = new StringBuilder(method).append("("); + while (true) { + TFieldIdEnum field = base.fieldForId(++idx); + if (field == null) { + idx--; + break; + } + buffer.append(field.getFieldName()).append(", "); + } + if (idx > 0) { + buffer.delete(buffer.length() - 2, buffer.length()); + } + return buffer.append(")").toString(); + } + + public abstract CharSequence spanName(); +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftClientDecorator.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftClientDecorator.java new file mode 100644 index 00000000000..1b913a9227c --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftClientDecorator.java @@ -0,0 +1,44 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.thrift.TBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; + +public class ThriftClientDecorator extends ThriftBaseDecorator { + private static final Logger log = LoggerFactory.getLogger(ThriftClientDecorator.class); + public static final ThriftClientDecorator CLIENT_DECORATOR = new ThriftClientDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{INSTRUMENTATION_NAME}; + } + + @Override + protected CharSequence spanType() { + return THRIFT; + } + + @Override + protected CharSequence component() { + return THRIFT_CLIENT_COMPONENT; + } + + @Override + public CharSequence spanName() { + return component(); + } + + public AgentSpan createSpan(String operationName, TBase tb) { + AgentSpan span = startSpan(spanName()); + withMethod(span, operationName); + withResource(span, operationName); + withArgs(span,operationName,tb); + afterStart(span); + return span; + } + +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftConstants.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftConstants.java new file mode 100644 index 00000000000..ee599b399e2 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftConstants.java @@ -0,0 +1,90 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import org.apache.thrift.*; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ThriftConstants { + + public static final Logger logger = LoggerFactory.getLogger(ThriftConstants.class); + public static final CharSequence THRIFT = UTF8BytesString.create("thrift"); + + public static final String INSTRUMENTATION_NAME = "thrift"; + public static final String INSTRUMENTATION_NAME_CLIENT = "thrift-client"; + public static final String INSTRUMENTATION_NAME_SERVER = "thrift-server"; + public static final String TASYNC_CLIENT = "org.apache.thrift.async.TAsyncClient"; + public static final String T_ASYNC_METHOD_CALL = "org.apache.thrift.async.TAsyncMethodCall"; + public static final String TSERVICE_CLIENT = "org.apache.thrift.TServiceClient"; + public static final String T_BASE_PROCESSOR = "org.apache.thrift.TBaseProcessor"; + public static final String T_PROCESSOR = "org.apache.thrift.TProcessor"; + public static final String T_ASYNC_PROCESSOR = "org.apache.thrift.TAsyncProcessor"; + public static final String T_MULTIPLEXED_PROCESSOR = "org.apache.thrift.TMultiplexedProcessor"; + public static final String T_BASE_ASYNC_PROCESSOR = "org.apache.thrift.TBaseAsyncProcessor"; + public static final String T_SERVER = "org.apache.thrift.server.TServer"; + + public static final String DD_MAGIC_FIELD = "DD_MAGIC_FIELD"; // Field Name + public static final short DD_MAGIC_FIELD_ID = 8888; // Field ID, a magic number + + public static final CharSequence THRIFT_CLIENT_COMPONENT = UTF8BytesString.create(INSTRUMENTATION_NAME_CLIENT); + public static final CharSequence THRIFT_SERVER_COMPONENT = UTF8BytesString.create(INSTRUMENTATION_NAME_SERVER); + + public static ThreadLocal CONTEXT_THREAD = new ThreadLocal<>(); + public static ThreadLocal CLIENT_INJECT_THREAD = new ThreadLocal<>(); + public static ConcurrentHashMap> TM_M = new ConcurrentHashMap<>(); + + interface Tags { + String ARGS = "args"; + String METHOD = "method"; + } + + public static final void setValue(Class klass, Object instance, String name, Object value) throws NoSuchFieldException, IllegalAccessException { + Field field = klass.getDeclaredField(name); + field.setAccessible(true); + field.set(instance, value); + } + + public static final Object getValue(Class klass, Object instance, String name) throws NoSuchFieldException, IllegalAccessException { + Field field = klass.getDeclaredField(name); + field.setAccessible(true); + return field.get(instance); + } + + public static Map getProcessMap(String serviceName, TProcessor processor) { + Map hashMap = new HashMap<>(); + if (processor instanceof TBaseProcessor) { + Map processMapView = ((TBaseProcessor) processor).getProcessMapView(); + processMapView.forEach((k, v) -> hashMap.put(serviceName + TMultiplexedProtocol.SEPARATOR + k, v)); + } else if (processor instanceof TBaseAsyncProcessor) { + Map processMapView = ((TBaseAsyncProcessor) processor).getProcessMapView(); + processMapView.forEach((k, v) -> hashMap.put(serviceName + TMultiplexedProtocol.SEPARATOR + k, v)); + } else { + if (logger.isDebugEnabled()) { + logger.error("Not support this processor:{},serviceName:{}", serviceName, processor.getClass().getName()); + } + } + return hashMap; + } + + public static Map getProcessMap(TProcessor processor) { + Map hashMap = new HashMap<>(); + if (processor instanceof TBaseProcessor) { + Map processMapView = ((TBaseProcessor) processor).getProcessMapView(); + hashMap.putAll(processMapView); + } else if (processor instanceof TBaseAsyncProcessor) { + Map processMapView = ((TBaseProcessor) processor).getProcessMapView(); + hashMap.putAll(processMapView); + } else { + if (logger.isDebugEnabled()) { + logger.error("Not support this processor:{}", processor.getClass().getName()); + } + } + return hashMap; + } +} diff --git a/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftServerDecorator.java b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftServerDecorator.java new file mode 100644 index 00000000000..7d9b022d7d5 --- /dev/null +++ b/dd-java-agent/instrumentation/thrift/src/main/java/datadog/trace/instrumentation/thrift/ThriftServerDecorator.java @@ -0,0 +1,50 @@ +package datadog.trace.instrumentation.thrift; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.commons.codec.binary.StringUtils; + +import java.util.Map; +import java.util.Optional; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.thrift.ExtractAdepter.GETTER; +import static datadog.trace.instrumentation.thrift.ThriftConstants.*; + +public class ThriftServerDecorator extends ThriftBaseDecorator { + public static final ThriftServerDecorator SERVER_DECORATOR = new ThriftServerDecorator(); + + @Override + protected String[] instrumentationNames() { + return new String[]{INSTRUMENTATION_NAME}; + } + + @Override + protected CharSequence spanType() { + return THRIFT; + } + + @Override + protected CharSequence component() { + return THRIFT_SERVER_COMPONENT; + } + + @Override + public CharSequence spanName() { + return component(); + } + + public AgentSpan createSpan(Map header,AbstractContext context) { + AgentSpan.Context parentContext = propagate().extract(header, GETTER); +// AgentSpan span = startSpan(spanName(),parentContext,context.startTime); + AgentSpan span = startSpan(spanName(),parentContext); + withMethod(span, context.methodName); + withResource(span, Optional.ofNullable(context.getOperatorName()).isPresent()?context.getOperatorName():context.methodName); + if (Optional.ofNullable(context.getArguments()).isPresent()) { + span.setTag(ThriftConstants.Tags.ARGS, context.getArguments()); + } + afterStart(span); + return span; + } + +} diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/InactiveAppSecTest.groovy b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/InactiveAppSecTest.groovy new file mode 100644 index 00000000000..03cc32ca614 --- /dev/null +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/InactiveAppSecTest.groovy @@ -0,0 +1,81 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.base.HttpServer +import datadog.trace.agent.test.utils.OkHttpUtils +import datadog.trace.instrumentation.servlet5.TestServlet5 +import okhttp3.HttpUrl +import okhttp3.MultipartBody +import okhttp3.OkHttpClient +import okhttp3.Request +import org.apache.catalina.Context +import org.apache.catalina.Wrapper +import spock.lang.Shared +import spock.lang.Subject + +import java.util.concurrent.TimeUnit + +import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_MULTIPART + +class InactiveAppSecTest extends AgentTestRunner { + @Shared + @Subject + HttpServer server + @Shared + OkHttpClient client = OkHttpUtils.client(15, 15, TimeUnit.SECONDS) + @Shared + URI address + + @Override + protected void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("dd.appsec.enabled", "") + } + + def setupSpec() { + server = new TomcatServer('tomcat-context', false, { Context ctx -> + Wrapper wrapper = ctx.createWrapper() + wrapper.name = UUID.randomUUID() + wrapper.servletClass = TestServlet5.name + wrapper.asyncSupported = true + ctx.addChild(wrapper) + ctx.addServletMappingDecoded(BODY_MULTIPART.path, wrapper.name) + }) + server.start() + address = server.address() + assert address.port > 0 + assert address.path.endsWith("/") + println "$server started at: $address" + } + + void cleanupSpec() { + server.stop() + println "$server stopped at: $address" + } + + void 'multipart requests still work'() { + setup: + def body = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart('a', 'x') + .build() + def url = HttpUrl.get(address.resolve('/tomcat-context' + BODY_MULTIPART.path)) + .newBuilder().build() + def request = new Request.Builder() + .url(url) + .method('POST', body).build() + def response = client.newCall(request).execute() + if (isDataStreamsEnabled()) { + TEST_DATA_STREAMS_WRITER.waitForGroups(1) + } + + expect: + response.body().charStream().text == '[a:[x]]' + + when: + TEST_WRITER.waitForTraces(1) + + then: + TEST_WRITER.get(0).every { + it.getTag('request.body.converted') == null + } + } +} diff --git a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAdvice.java b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAdvice.java index 548fce528bd..332ca1b5009 100644 --- a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAdvice.java +++ b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAdvice.java @@ -1,18 +1,65 @@ package datadog.trace.instrumentation.trace_annotation; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.instrumentation.trace_annotation.TraceDecorator.DECORATE; +import datadog.trace.api.Trace; import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; public class TraceAdvice { - + private static final String DEFAULT_OPERATION_NAME = "trace.annotation"; @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope onEnter(@Advice.Origin final Method method) { - return activateSpan(DECORATE.startMethodSpan(method)); + public static AgentScope onEnter(@Advice.Origin final Method method,@Advice.AllArguments final Object[] args) { + final Trace traceAnnotation = method.getAnnotation(Trace.class); + CharSequence operationName = traceAnnotation == null ? null : traceAnnotation.operationName(); + + if (operationName == null || operationName.length() == 0) { + if (DECORATE.useLegacyOperationName()) { + operationName = DEFAULT_OPERATION_NAME; + } else { + operationName = DECORATE.spanNameForMethod(method); + } + } + + final AgentSpan span = startSpan(operationName); + + Parameter[] parameters = method.getParameters(); + StringBuffer methodName = new StringBuffer(method.getName()); + methodName.append("("); + Integer max_size = 1024; + if (parameters.length>0){ + for (int i = 0; i < parameters.length; i++) { + if (args[i]==null){ + span.setTag(parameters[i].getName(),"null"); + }else { + String value = args[i].toString(); + span.setTag(parameters[i].getName(), args[i].toString().substring(0,value.length()>=max_size?max_size:value.length())); + } + methodName.append(parameters[i].getType().getTypeName()).append(" ").append(parameters[i].getName()); + if (i XxlJobContext.HANDLE_COCE_SUCCESS) { + throwable = new Throwable(XxlJobContext.getXxlJobContext().getHandleMsg()); + } + return super.onError(scope,throwable); + } +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobAdvice.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobAdvice.java new file mode 100644 index 00000000000..5abad277280 --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobAdvice.java @@ -0,0 +1,44 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import com.xxl.job.core.handler.IJobHandler; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import io.netty.util.internal.StringUtil; +import net.bytebuddy.asm.Advice; + +import java.lang.reflect.Method; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.*; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobDecorator.DECORATOR; + +public class MethodJobAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope execute(@Advice.This IJobHandler jobHandler, @Advice.FieldValue("method") final Method method) { + String methodName = method.getDeclaringClass().getName() + "." + method.getName(); + AgentSpan span = DECORATOR.createSpan(methodName); + span.setTag(JOB_METHOD,methodName); + span.setTag(JOB_TYPE,JobType.METHOD_JOB); + String jobParam = com.xxl.job.core.context.XxlJobHelper.getJobParam(); + if (!StringUtil.isNullOrEmpty(jobParam)){ + span.setTag(JOB_PARAM, jobParam); + } + span.setTag(JOB_ID, com.xxl.job.core.context.XxlJobHelper.getJobId()); + AgentScope agentScope = activateSpan(span); + return agentScope; +// return activateSpan(noopSpan()); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATOR.error(scope, throwable); + DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobInstrumentation.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobInstrumentation.java new file mode 100644 index 00000000000..f9d6e204bfe --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/MethodJobInstrumentation.java @@ -0,0 +1,52 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.INSTRUMENTATION_NAME; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class MethodJobInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public MethodJobInstrumentation() { + super(INSTRUMENTATION_NAME); + } + + @Override + public String hierarchyMarkerType() { + return JobConstants.HandleClassName.METHOD_CLASS; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("execute")) + .and(takesArguments(0)), + packageName + ".MethodJobAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[]{ + packageName + ".JobDecorator", + packageName + ".JobConstants", + packageName + ".ScriptJobAdvice", + packageName + ".JobConstants$JobType", + packageName + ".JobConstants$HandleClassName" + }; + } +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobAdvice.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobAdvice.java new file mode 100644 index 00000000000..1e0fbd2d121 --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobAdvice.java @@ -0,0 +1,47 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import com.xxl.job.core.glue.GlueTypeEnum; +import com.xxl.job.core.handler.IJobHandler; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import io.netty.util.internal.StringUtil; +import net.bytebuddy.asm.Advice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.*; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobDecorator.DECORATOR; + +public class ScriptJobAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope execute(@Advice.This IJobHandler jobHandler, + @Advice.FieldValue("glueType") GlueTypeEnum glueType + , @Advice.FieldValue("jobId") int jobId) { + String operationName = glueType.getCmd() + "/id/" + jobId; + AgentSpan span = DECORATOR.createSpan(operationName); + span.setTag(CMD, glueType.getCmd()); + span.setTag(JOB_TYPE, JobConstants.JobType.SCRIPT_JOB); + span.setTag(JOB_ID, jobId); + String jobParam = com.xxl.job.core.context.XxlJobHelper.getJobParam(); + if (!StringUtil.isNullOrEmpty(jobParam)) { + span.setTag(JOB_PARAM, jobParam); + } + AgentScope agentScope = activateSpan(span); + return agentScope; +// return activateSpan(noopSpan()); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATOR.error(scope, throwable); + DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobInstrumentation.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobInstrumentation.java new file mode 100644 index 00000000000..bf9bf98f4e2 --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/ScriptJobInstrumentation.java @@ -0,0 +1,42 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.INSTRUMENTATION_NAME; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class ScriptJobInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public ScriptJobInstrumentation() { + super(INSTRUMENTATION_NAME); + } + + @Override + public String hierarchyMarkerType() { + return JobConstants.HandleClassName.SCRIPT_CLASS; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return named(hierarchyMarkerType()); + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("execute")) + .and(takesArguments(0)), + packageName + ".ScriptJobAdvice"); + } + +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobAdvice.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobAdvice.java new file mode 100644 index 00000000000..f539eb48f6c --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobAdvice.java @@ -0,0 +1,40 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import com.xxl.job.core.handler.IJobHandler; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import io.netty.util.internal.StringUtil; +import net.bytebuddy.asm.Advice; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.*; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobDecorator.DECORATOR; + +public class SimpleJobAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope execute(@Advice.This IJobHandler jobHandler + , @Advice.FieldValue("jobId") int jobId) { + String operationName = JobType.SIMPLE_JOB+"/id/"+jobId; + AgentSpan span = DECORATOR.createSpan(operationName); + span.setTag(JOB_TYPE, JobType.SIMPLE_JOB); + span.setTag(JOB_ID, jobId); + String jobParam = com.xxl.job.core.context.XxlJobHelper.getJobParam(); + if (!StringUtil.isNullOrEmpty(jobParam)){ + span.setTag(JOB_PARAM, jobParam); + } + AgentScope agentScope = activateSpan(span); + return agentScope; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit( + @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { + if (scope == null) { + return; + } + DECORATOR.error(scope, throwable); + DECORATOR.beforeFinish(scope.span()); + scope.close(); + scope.span().finish(); + } +} diff --git a/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobInstrumentation.java b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobInstrumentation.java new file mode 100644 index 00000000000..bbd0a9a44d9 --- /dev/null +++ b/dd-java-agent/instrumentation/xxl-job-2.3/src/main/java/datadog/trace/instrumentation/xxl_job_2_3x/SimpleJobInstrumentation.java @@ -0,0 +1,48 @@ +package datadog.trace.instrumentation.xxl_job_2_3x; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.instrumentation.xxl_job_2_3x.JobConstants.INSTRUMENTATION_NAME; +import static net.bytebuddy.matcher.ElementMatchers.*; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class SimpleJobInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy { + + public SimpleJobInstrumentation() { + super(INSTRUMENTATION_NAME); + } + + + @Override + public String hierarchyMarkerType() { + return JobConstants.HandleClassName.HANDLER_CLASS; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())) + .and(not(named(JobConstants.HandleClassName.METHOD_CLASS))) + .and(not(named(JobConstants.HandleClassName.SCRIPT_CLASS))) + .and(not(named(JobConstants.HandleClassName.GLUE_CLASS))) + ; + } + + @Override + public void methodAdvice(MethodTransformer transformation) { + transformation.applyAdvice( + isMethod() + .and(isPublic()) + .and(nameStartsWith("execute")) + .and(takesArguments(0)), + packageName + ".SimpleJobAdvice"); + } +} diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java index 107ec5f4b56..c2bbfce738b 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java @@ -1,11 +1,8 @@ package datadog.trace.bootstrap; import de.thetaphi.forbiddenapis.SuppressForbidden; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; + +import java.io.*; import java.lang.instrument.Instrumentation; import java.lang.management.ManagementFactory; import java.lang.reflect.Field; @@ -61,8 +58,27 @@ public static void agentmain(final String agentArgs, final Instrumentation inst) } try { + String agentVersion = AgentJar.getAgentVersion(); + System.out.println(agentVersion); + // System.setProperty("dd.version", agentVersion); final URL agentJarURL = installAgentJar(inst); - final Class agentClass = Class.forName("datadog.trace.bootstrap.Agent", true, null); + // System.out.println(agentArgs); + if (agentArgs != null && !agentArgs.equals("")) { + String[] split = agentArgs.split(","); + for (int i = 0; i < split.length; i++) { + String[] strings = split[i].split("="); + if (strings.length != 2) { + continue; + } + String envStr = strings[0].replace('.', '_').replace('-', '_').toUpperCase(); + if (System.getProperty(strings[0]) == null && System.getenv(envStr) == null) { + System.out.println("set " + strings[0] + " = " + strings[1]); + System.setProperty(strings[0], strings[1]); + } + } + } + final Class agentClass = + ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); if (agentClass.getClassLoader() != null) { throw new IllegalStateException("DD Java Agent NOT added to bootstrap classpath."); } @@ -356,8 +372,8 @@ private static boolean checkJarManifestMainClassIsThis(final URL jarUrl) throws final URL manifestUrl = new URL("jar:" + jarUrl + "!/META-INF/MANIFEST.MF"); final String mainClassLine = "Main-Class: " + thisClass.getCanonicalName(); try (final BufferedReader reader = - new BufferedReader( - new InputStreamReader(manifestUrl.openStream(), StandardCharsets.UTF_8))) { + new BufferedReader( + new InputStreamReader(manifestUrl.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.equals(mainClassLine)) { diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index ca96e8d1544..3252556ff9a 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -34,7 +34,7 @@ public final class ConfigDefaults { /* These fields are made public because they're referenced elsewhere internally. They're not intended as public API. */ public static final String DEFAULT_AGENT_HOST = "localhost"; - public static final int DEFAULT_TRACE_AGENT_PORT = 8126; + public static final int DEFAULT_TRACE_AGENT_PORT = 9529; public static final int DEFAULT_DOGSTATSD_PORT = 8125; public static final String DEFAULT_TRACE_AGENT_SOCKET_PATH = "/var/run/datadog/apm.socket"; public static final String DEFAULT_DOGSTATSD_SOCKET_PATH = "/var/run/datadog/dsd.socket"; @@ -230,5 +230,25 @@ public final class ConfigDefaults { static final boolean DEFAULT_JAX_RS_EXCEPTION_AS_ERROR_ENABLED = true; static final boolean DEFAULT_TELEMETRY_DEBUG_REQUESTS_ENABLED = false; + static final boolean DEFAULT_JDBC_SQL_OBFUSCATION = false; + + static final boolean DEFAULT_MONGO_OBFUSCATION = false; + + static final boolean DEFAULT_REDIS_COMMAND_ARGS = false; + + static final String DEFAULT_LOG_PATTERN="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - [%method,%line] %X{dd.service} %X{dd.trace_id} %X{dd.span_id} - %msg%n"; + + static final boolean DEFAULT_LOG_PATTERN_REPLACE=false; + + static final boolean DEFAULT_TRACE_HEADER_ENABLED = false; + + static final boolean DEFAULT_TRACE_REQUEST_BODY_ENABLED = false; + + static final boolean DEFAULT_TRACE_RESPONSE_BODY_ENABLED = false; + + static final String DEFAULT_TRACE_RESPONSE_BODY_ENCODING="utf-8"; + + static final boolean DEFAULT_TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED = false; + private ConfigDefaults() {} } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DD128bTraceId.java b/dd-trace-api/src/main/java/datadog/trace/api/DD128bTraceId.java index 3479d3d4489..5780cb70ef0 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DD128bTraceId.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DD128bTraceId.java @@ -166,12 +166,12 @@ public int hashCode() { @Override public String toString() { - String s = this.str; + // String s = this.str; // This race condition is intentional and benign. // The worst that can happen is that an identical value is produced and written into the field. - if (s == null) { - this.str = s = Long.toUnsignedString(this.lowOrderBits); - } - return s; + // if (s == null) { + // this.str = s = Long.toUnsignedString(this.lowOrderBits); + // } + return toHexString(); } } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index b11329f6ab7..42e8c449b8f 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -60,6 +60,9 @@ public final class TraceInstrumentationConfig { public static final String DB_DBM_PROPAGATION_MODE_MODE = "dbm.propagation.mode"; public static final String JDBC_CONNECTION_CLASS_NAME = "trace.jdbc.connection.class.name"; + public static final String JDBC_SQL_OBFUSCATION = "jdbc.sql.obfuscation"; + public static final String MONGO_OBFUSCATION = "mongo.obfuscation"; + public static final String REDIS_COMMAND_ARGS = "redis.command.args"; public static final String HTTP_URL_CONNECTION_CLASS_NAME = "trace.http.url.connection.class.name"; @@ -74,6 +77,9 @@ public final class TraceInstrumentationConfig { public static final String LOGS_INJECTION_ENABLED = "logs.injection.enabled"; public static final String LOGS_INJECTION = "logs.injection"; + public static final String LOGS_PATTERN = "logs.pattern"; + public static final String LOGS_PATTERN_REPLACE = "logs.pattern.replace"; + public static final String LOGS_MDC_TAGS_INJECTION_ENABLED = "logs.mdc.tags.injection"; public static final String TRACE_128_BIT_TRACEID_LOGGING_ENABLED = "trace.128.bit.traceid.logging.enabled"; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java index cb1e1c1baa1..ac835c2ff09 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java @@ -135,5 +135,16 @@ public final class TracerConfig { public static final String TRACE_POST_PROCESSING_TIMEOUT = "trace.post-processing.timeout"; + public static final String TRACE_HEADER_ENABLED = "trace.headers.enabled"; + + public static final String TRACE_REQUEST_BODY_ENABLED = "trace.request.body.enabled"; + + public static final String TRACE_RESPONSE_BODY_ENABLED = "trace.response.body.enabled"; + + public static final String TRACE_RESPONSE_BODY_ENCODING = "trace.response.body.encoding"; + + public static final String TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED = + "trace.dubbo.provider.propagate.enabled"; + private TracerConfig() {} } diff --git a/dd-trace-core/src/jmh/java/datadog/trace/core/BlackholeWriter.java b/dd-trace-core/src/jmh/java/datadog/trace/core/BlackholeWriter.java index ead942ab0f3..be85aa651e5 100644 --- a/dd-trace-core/src/jmh/java/datadog/trace/core/BlackholeWriter.java +++ b/dd-trace-core/src/jmh/java/datadog/trace/core/BlackholeWriter.java @@ -24,6 +24,7 @@ public void write(List trace) { counters.spans += trace.size(); } + @Override public void start() {} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 2df38ffe753..acf6550dcf6 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -1270,6 +1270,7 @@ private DDSpan buildSpan() { EndpointTracker tracker = tracer.onRootSpanStarted(span); span.setEndpointTracker(tracker); } + span.setTag("trace_128_bit_id",span.getTraceId().toString()); return span; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java index 76ca949e228..8aeb265b545 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java @@ -181,7 +181,6 @@ public boolean accept(String key, String value) { break; default: } - if (classification != IGNORE) { try { if (null != value) { @@ -290,7 +289,7 @@ void parseTraceParentHeader(String tp) { long version = LongStringUtils.parseUnsignedLongHex(tp, 0, 2, true); if (version == 255) { throw new IllegalStateException("Illegal version number " + tp.substring(0, 2)); - } else if (version == 0 && length > TRACE_PARENT_LENGTH) { + } else if (version == 1 && length > TRACE_PARENT_LENGTH) { throw new IllegalStateException("The length of traceparent '" + tp + "' is too long"); } DDTraceId traceId = DD128bTraceId.fromHex(tp, TRACE_PARENT_TID_START, 32, true); diff --git a/dd-trace-ot/build.gradle b/dd-trace-ot/build.gradle index 78e010d7494..f00f3fa57a2 100644 --- a/dd-trace-ot/build.gradle +++ b/dd-trace-ot/build.gradle @@ -36,7 +36,7 @@ dependencies { } api project(':dd-trace-api') - implementation (project(':dd-trace-core')) { + implementation(project(':dd-trace-core')) { // why all communication pulls in remote config is beyond me... exclude(group: 'com.datadoghq', module: 'remote-config') } diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 770c5ad8fc8..034eba19f34 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -79,16 +79,21 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_IAST_TRUNCATION_MAX_VALUE_LENGTH; import static datadog.trace.api.ConfigDefaults.DEFAULT_IAST_WEAK_CIPHER_ALGORITHMS; import static datadog.trace.api.ConfigDefaults.DEFAULT_IAST_WEAK_HASH_ALGORITHMS; +import static datadog.trace.api.ConfigDefaults.DEFAULT_JDBC_SQL_OBFUSCATION; import static datadog.trace.api.ConfigDefaults.DEFAULT_JMX_FETCH_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_LIMIT; import static datadog.trace.api.ConfigDefaults.DEFAULT_LOGS_INJECTION_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_LOG_PATTERN; +import static datadog.trace.api.ConfigDefaults.DEFAULT_LOG_PATTERN_REPLACE; +import static datadog.trace.api.ConfigDefaults.DEFAULT_MONGO_OBFUSCATION; import static datadog.trace.api.ConfigDefaults.DEFAULT_PARTIAL_FLUSH_MIN_SPANS; import static datadog.trace.api.ConfigDefaults.DEFAULT_PERF_METRICS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_PRIORITY_SAMPLING_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_PRIORITY_SAMPLING_FORCE; import static datadog.trace.api.ConfigDefaults.DEFAULT_PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_PROPAGATION_STYLE; +import static datadog.trace.api.ConfigDefaults.DEFAULT_REDIS_COMMAND_ARGS; import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_INTEGRITY_CHECK_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES; @@ -113,6 +118,8 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_PORT; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_V05_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_ANALYTICS_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_HEADER_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_HTTP_RESOURCE_REMOVE_TRAILING_SLASH; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL; @@ -120,7 +127,10 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_PROPAGATION_STYLE; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RATE_LIMIT; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_REPORT_HOSTNAME; +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_REQUEST_BODY_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RESOLVER_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RESPONSE_BODY_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RESPONSE_BODY_ENCODING; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_X_DATADOG_TAGS_MAX_LENGTH; import static datadog.trace.api.ConfigDefaults.DEFAULT_WRITER_BAGGAGE_INJECT; import static datadog.trace.api.DDTags.DJM_ENABLED; @@ -377,6 +387,7 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.IGNITE_CACHE_INCLUDE_KEYS; import static datadog.trace.api.config.TraceInstrumentationConfig.INTEGRATION_SYNAPSE_LEGACY_OPERATION_NAME; import static datadog.trace.api.config.TraceInstrumentationConfig.JAX_RS_EXCEPTION_AS_ERROR_ENABLED; +import static datadog.trace.api.config.TraceInstrumentationConfig.JDBC_SQL_OBFUSCATION; import static datadog.trace.api.config.TraceInstrumentationConfig.JMS_PROPAGATION_DISABLED_QUEUES; import static datadog.trace.api.config.TraceInstrumentationConfig.JMS_PROPAGATION_DISABLED_TOPICS; import static datadog.trace.api.config.TraceInstrumentationConfig.JMS_UNACKNOWLEDGED_MAX_AGE; @@ -384,12 +395,17 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.KAFKA_CLIENT_PROPAGATION_DISABLED_TOPICS; import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_INJECTION; import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_INJECTION_ENABLED; +import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_MDC_TAGS_INJECTION_ENABLED; +import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_PATTERN; +import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_PATTERN_REPLACE; import static datadog.trace.api.config.TraceInstrumentationConfig.MESSAGE_BROKER_SPLIT_BY_DESTINATION; +import static datadog.trace.api.config.TraceInstrumentationConfig.MONGO_OBFUSCATION; import static datadog.trace.api.config.TraceInstrumentationConfig.OBFUSCATION_QUERY_STRING_REGEXP; import static datadog.trace.api.config.TraceInstrumentationConfig.PLAY_REPORT_HTTP_STATUS; import static datadog.trace.api.config.TraceInstrumentationConfig.RABBIT_INCLUDE_ROUTINGKEY_IN_RESOURCE; import static datadog.trace.api.config.TraceInstrumentationConfig.RABBIT_PROPAGATION_DISABLED_EXCHANGES; import static datadog.trace.api.config.TraceInstrumentationConfig.RABBIT_PROPAGATION_DISABLED_QUEUES; +import static datadog.trace.api.config.TraceInstrumentationConfig.REDIS_COMMAND_ARGS; import static datadog.trace.api.config.TraceInstrumentationConfig.SERVLET_ASYNC_TIMEOUT_ERROR; import static datadog.trace.api.config.TraceInstrumentationConfig.SERVLET_PRINCIPAL_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.SERVLET_ROOT_CONTEXT_SERVICE_NAME; @@ -438,7 +454,9 @@ import static datadog.trace.api.config.TracerConfig.TRACE_ANALYTICS_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_CLIENT_IP_HEADER; import static datadog.trace.api.config.TracerConfig.TRACE_CLIENT_IP_RESOLVER_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_GIT_METADATA_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_HEADER_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_CLIENT_PATH_RESOURCE_NAME_MAPPING; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_RESOURCE_REMOVE_TRAILING_SLASH; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_SERVER_PATH_RESOURCE_NAME_MAPPING; @@ -453,7 +471,10 @@ import static datadog.trace.api.config.TracerConfig.TRACE_RATE_LIMIT; import static datadog.trace.api.config.TracerConfig.TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_REPORT_HOSTNAME; +import static datadog.trace.api.config.TracerConfig.TRACE_REQUEST_BODY_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_RESOLVER_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_RESPONSE_BODY_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_RESPONSE_BODY_ENCODING; import static datadog.trace.api.config.TracerConfig.TRACE_SAMPLE_RATE; import static datadog.trace.api.config.TracerConfig.TRACE_SAMPLING_OPERATION_RULES; import static datadog.trace.api.config.TracerConfig.TRACE_SAMPLING_RULES; @@ -675,6 +696,8 @@ static class HostNameHolder { private final int tracerMetricsMaxPending; private final boolean reportHostName; + private final String logPattern; + private final boolean logPatternReplace; private final boolean traceAnalyticsEnabled; private final String traceClientIpHeader; @@ -711,6 +734,8 @@ static class HostNameHolder { private final int profilingExceptionHistogramMaxCollectionSize; private final boolean profilingExcludeAgentThreads; private final boolean profilingUploadSummaryOn413Enabled; + + private final boolean logsMDCTagsInjectionEnabled; private final boolean profilingRecordExceptionMessage; private final boolean crashTrackingAgentless; @@ -927,6 +952,12 @@ static class HostNameHolder { private final String dogStatsDPath; private final List dogStatsDArgs; + private final boolean jdbcSqlObfuscation; + + private final boolean mongoObfuscation; + + private final boolean redisCommandArgs; + private String env; private String version; private final String primaryTag; @@ -943,11 +974,18 @@ static class HostNameHolder { private final boolean sparkTaskHistogramEnabled; private final boolean sparkAppNameAsService; private final boolean jaxRsExceptionAsErrorsEnabled; + private final boolean tracerHeaderEnabled; + private final boolean tracerRequestBodyEnabled; + + private final boolean tracerResponseBodyEnabled; + private final String tracerResponseBodyEncoding; private final boolean axisPromoteResourceName; private final float traceFlushIntervalSeconds; private final long tracePostProcessingTimeout; + private final boolean dubboProviderPropagateEnabled; + private final boolean telemetryDebugRequestsEnabled; // Read order: System Properties -> Env Variables, [-> properties file], [-> default value] @@ -1429,6 +1467,12 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins tracerMetricsMaxAggregates = configProvider.getInteger(TRACER_METRICS_MAX_AGGREGATES, 2048); tracerMetricsMaxPending = configProvider.getInteger(TRACER_METRICS_MAX_PENDING, 2048); + logPattern = configProvider.getString(LOGS_PATTERN, DEFAULT_LOG_PATTERN); + logPatternReplace = + configProvider.getBoolean(LOGS_PATTERN_REPLACE, DEFAULT_LOG_PATTERN_REPLACE); + + logsMDCTagsInjectionEnabled = configProvider.getBoolean(LOGS_MDC_TAGS_INJECTION_ENABLED, true); + reportHostName = configProvider.getBoolean(TRACE_REPORT_HOSTNAME, DEFAULT_TRACE_REPORT_HOSTNAME); @@ -1893,6 +1937,11 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) awsPropagationEnabled = isPropagationEnabled(true, "aws", "aws-sdk"); sqsPropagationEnabled = isPropagationEnabled(true, "sqs"); + jdbcSqlObfuscation = + configProvider.getBoolean(JDBC_SQL_OBFUSCATION, DEFAULT_JDBC_SQL_OBFUSCATION); + mongoObfuscation = configProvider.getBoolean(MONGO_OBFUSCATION, DEFAULT_MONGO_OBFUSCATION); + redisCommandArgs = configProvider.getBoolean(REDIS_COMMAND_ARGS, DEFAULT_REDIS_COMMAND_ARGS); + kafkaClientPropagationEnabled = isPropagationEnabled(true, "kafka", "kafka.client"); kafkaClientPropagationDisabledTopics = tryMakeImmutableSet(configProvider.getList(KAFKA_CLIENT_PROPAGATION_DISABLED_TOPICS)); @@ -1980,6 +2029,21 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) cwsTlsRefresh = configProvider.getInteger(CWS_TLS_REFRESH, DEFAULT_CWS_TLS_REFRESH); dataJobsEnabled = configProvider.getBoolean(DATA_JOBS_ENABLED, DEFAULT_DATA_JOBS_ENABLED); + tracerHeaderEnabled = + configProvider.getBoolean(TRACE_HEADER_ENABLED, DEFAULT_TRACE_HEADER_ENABLED); + tracerRequestBodyEnabled = + configProvider.getBoolean(TRACE_REQUEST_BODY_ENABLED, DEFAULT_TRACE_REQUEST_BODY_ENABLED); + + dubboProviderPropagateEnabled = + configProvider.getBoolean( + TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED, DEFAULT_TRACE_DUBBO_PROVIDER_PROPAGATE_ENABLED); + + tracerResponseBodyEnabled = + configProvider.getBoolean(TRACE_RESPONSE_BODY_ENABLED, DEFAULT_TRACE_RESPONSE_BODY_ENABLED); + + tracerResponseBodyEncoding = + configProvider.getString( + TRACE_RESPONSE_BODY_ENCODING, DEFAULT_TRACE_RESPONSE_BODY_ENCODING); dataStreamsEnabled = configProvider.getBoolean(DATA_STREAMS_ENABLED, DEFAULT_DATA_STREAMS_ENABLED); @@ -2524,6 +2588,18 @@ public boolean isLogsInjectionEnabled() { return logsInjectionEnabled; } + public String getLogPattern() { + return logPattern; + } + + public boolean isLogPatternReplace() { + return logPatternReplace; + } + + public boolean isLogsMDCTagsInjectionEnabled() { + return logsMDCTagsInjectionEnabled; + } + public boolean isReportHostName() { return reportHostName; } @@ -3465,7 +3541,32 @@ public boolean isDataJobsEnabled() { return dataJobsEnabled; } - /** @return A map of tags to be applied only to the local application root span. */ + /** + * @return A map of tags to be applied only to the local application root span. + */ + public boolean isTracerHeaderEnabled() { + return tracerHeaderEnabled; + } + + public boolean isTracerRequestBodyEnabled() { + return tracerRequestBodyEnabled; + } + + public boolean isDubboProviderPropagateEnabled() { + return dubboProviderPropagateEnabled; + } + + public boolean isTracerResponseBodyEnabled() { + return tracerResponseBodyEnabled; + } + + public String getTracerResponseBodyEncoding() { + return tracerResponseBodyEncoding.toLowerCase(); + } + + /** + * @return A map of tags to be applied only to the local application root span. + */ public Map getLocalRootSpanTags() { final Map runtimeTags = getRuntimeTags(); final Map result = new HashMap<>(runtimeTags.size() + 2); @@ -4178,6 +4279,18 @@ public static Config get(final Properties properties) { } } + public boolean getJdbcSqlObfuscation() { + return jdbcSqlObfuscation; + } + + public boolean getMongoObfuscation() { + return mongoObfuscation; + } + + public boolean getRedisCommandArgs() { + return redisCommandArgs; + } + @Override public String toString() { return "Config{" @@ -4268,6 +4381,14 @@ public String toString() { + httpClientSplitByDomain + ", httpResourceRemoveTrailingSlash=" + httpResourceRemoveTrailingSlash + + ", tracerHeaderEnabled=" + + tracerHeaderEnabled + + ", tracerRequestBodyEnabled=" + + tracerRequestBodyEnabled + + ", tracerResponseBodyEnabled=" + + tracerResponseBodyEnabled + + ", tracerResponseBodyEncoding=" + + tracerResponseBodyEncoding + ", dbClientSplitByInstance=" + dbClientSplitByInstance + ", dbClientSplitByInstanceTypeSuffix=" @@ -4341,6 +4462,14 @@ public String toString() { + tracerMetricsMaxAggregates + ", tracerMetricsMaxPending=" + tracerMetricsMaxPending + + ", logsInjectionEnabled=" + + logsInjectionEnabled + + ", logsMDCTagsInjectionEnabled=" + + logsMDCTagsInjectionEnabled + + ", logPattern=" + + logPattern + + ", logPatternReplace=" + + logPatternReplace + ", reportHostName=" + reportHostName + ", traceAnalyticsEnabled=" @@ -4580,6 +4709,12 @@ public String toString() { + appSecScaEnabled + ", dataJobsEnabled=" + dataJobsEnabled + + ", jdbcSqlObfuscation=" + + jdbcSqlObfuscation + + ", mongoObfuscation=" + + mongoObfuscation + + ", dubboProviderPropagateEnabled=" + + dubboProviderPropagateEnabled + '}'; } } diff --git a/settings.gradle b/settings.gradle index f06ac6a9cae..ad019e07157 100644 --- a/settings.gradle +++ b/settings.gradle @@ -168,6 +168,7 @@ include ':dd-java-agent:instrumentation:aws-java-sqs-1.0' include ':dd-java-agent:instrumentation:aws-java-sqs-2.0' include ':dd-java-agent:instrumentation:aws-lambda-handler' include ':dd-java-agent:instrumentation:axis-2' +include ':dd-java-agent:instrumentation:axis-1' include ':dd-java-agent:instrumentation:axway-api' include ':dd-java-agent:instrumentation:caffeine' include ':dd-java-agent:instrumentation:cdi-1.2' @@ -195,6 +196,10 @@ include ':dd-java-agent:instrumentation:datastax-cassandra-3.8' include ':dd-java-agent:instrumentation:datastax-cassandra-4' include ':dd-java-agent:instrumentation:dropwizard' include ':dd-java-agent:instrumentation:dropwizard:dropwizard-views' +include ':dd-java-agent:instrumentation:dubbo-apache-2.7' +include ':dd-java-agent:instrumentation:dubbo-alibaba' +include ':dd-java-agent:instrumentation:rocketmq' +include ':dd-java-agent:instrumentation:rocketmq-5.0' include ':dd-java-agent:instrumentation:elasticsearch' include ':dd-java-agent:instrumentation:elasticsearch:rest-5' include ':dd-java-agent:instrumentation:elasticsearch:rest-6.4' @@ -228,6 +233,7 @@ include ':dd-java-agent:instrumentation:hibernate' include ':dd-java-agent:instrumentation:hibernate:core-3.3' include ':dd-java-agent:instrumentation:hibernate:core-4.0' include ':dd-java-agent:instrumentation:hibernate:core-4.3' +include ':dd-java-agent:instrumentation:taobao-hsf' include ':dd-java-agent:instrumentation:http-url-connection' include ':dd-java-agent:instrumentation:hystrix-1.4' include ':dd-java-agent:instrumentation:iast-instrumenter' @@ -346,6 +352,7 @@ include ':dd-java-agent:instrumentation:opentelemetry:opentelemetry-annotations- include ':dd-java-agent:instrumentation:opentelemetry:opentelemetry-annotations-1.26' // POC - Disabled for now //include ':dd-java-agent:instrumentation:opentelemetry:opentelemetry-instrumentations' +include ':dd-java-agent:instrumentation:ons-client' include ':dd-java-agent:instrumentation:opentracing' include ':dd-java-agent:instrumentation:opentracing:api-0.31' include ':dd-java-agent:instrumentation:opentracing:api-0.32' @@ -361,6 +368,8 @@ include ':dd-java-agent:instrumentation:play-ws-1' include ':dd-java-agent:instrumentation:play-ws-2' include ':dd-java-agent:instrumentation:play-ws-2.1' include ':dd-java-agent:instrumentation:protobuf' +include ':dd-java-agent:instrumentation:powerjob' +include ':dd-java-agent:instrumentation:pulsar' include ':dd-java-agent:instrumentation:quartz-2' include ':dd-java-agent:instrumentation:rabbitmq-amqp-2.7' include ':dd-java-agent:instrumentation:ratpack-1.5' @@ -417,6 +426,7 @@ include ':dd-java-agent:instrumentation:testng' include ':dd-java-agent:instrumentation:testng:testng-6' include ':dd-java-agent:instrumentation:testng:testng-7' include ':dd-java-agent:instrumentation:thymeleaf' +include ':dd-java-agent:instrumentation:thrift' include 'dd-java-agent:instrumentation:tinylog-2' include ':dd-java-agent:instrumentation:tomcat-5.5' include ':dd-java-agent:instrumentation:tomcat-5.5-common' @@ -442,9 +452,11 @@ include ':dd-java-agent:instrumentation:vertx-web-4.0' include ':dd-java-agent:instrumentation:redisson-2.0.0' include ':dd-java-agent:instrumentation:zio' include ':dd-java-agent:instrumentation:zio:zio-2.0' +include ':dd-java-agent:instrumentation:xxl-job-2.3' // benchmark include ':dd-java-agent:benchmark' include ':dd-java-agent:benchmark-integration' include ':dd-java-agent:benchmark-integration:jetty-perftest' include ':dd-java-agent:benchmark-integration:play-perftest' +