From 0eddcc4dda80cd77b8e35b19d4838d6eb9a047e5 Mon Sep 17 00:00:00 2001 From: Justin Lee Date: Tue, 26 Nov 2024 12:28:12 -0500 Subject: [PATCH] add code origin support to kafka client # Conflicts: # dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java --- .../debugger/agent/DebuggerTransformer.java | 2 +- .../debugger/instrumentation/MethodInfo.java | 6 + .../debugger/probe/CodeOriginProbe.java | 18 +- .../src/test/groovy/GrpcCodeOriginTest.groovy | 107 +--------- .../kafka-clients-3.8/build.gradle | 3 + .../MessageListenerInstrumentation.java | 43 ++++ .../groovy/KafkaCodeOriginForkedTest.groovy | 187 ++++++++++++++++++ .../codeorigin/EntrySpanOriginAdvice.java | 1 + dd-java-agent/testing/build.gradle | 2 + .../trace/agent/test/AgentTestRunner.groovy | 51 +++++ .../agent/test/asserts/TagsAssert.groovy | 12 ++ 11 files changed, 318 insertions(+), 114 deletions(-) create mode 100644 dd-java-agent/instrumentation/kafka-clients-3.8/src/main/java/datadog/trace/instrumentation/kafka_clients38/MessageListenerInstrumentation.java create mode 100644 dd-java-agent/instrumentation/kafka-clients-3.8/src/test/groovy/KafkaCodeOriginForkedTest.groovy diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java index 61929009aa7..0a35c845efa 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java @@ -251,7 +251,7 @@ public byte[] transform( private boolean skipInstrumentation(ClassLoader loader, String classFilePath) { if (definitionMatcher.isEmpty()) { - log.warn("No debugger definitions present."); + log.debug("No debugger definitions present."); return true; } if (classFilePath == null) { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java index 3ae5d5568a9..d58ad0251e0 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java @@ -58,4 +58,10 @@ public String getSourceFileName() { public String getTypeName() { return Strings.getClassName(classNode.name); } + + @Override + public String toString() { + return String.format( + "MethodInfo{classNode=%s, methodNode=%s}", classNode.name, methodNode.desc); + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java index f51cc67e1eb..8709f9c9edf 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java @@ -118,20 +118,8 @@ public int hashCode() { @Override public String toString() { - return "CodeOriginProbe{" - + "id='" - + id - + '\'' - + ", version=" - + version - + ", tags=" - + Arrays.toString(tags) - + ", where=" - + where - + ", evaluateAt=" - + evaluateAt - + ", entrySpanProbe=" - + entrySpanProbe - + "} "; + return String.format( + "CodeOriginProbe{probeId=%s, entrySpanProbe=%s, signature=%s, location=%s}", + probeId, entrySpanProbe, signature, location); } } diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcCodeOriginTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcCodeOriginTest.groovy index cde6156d66f..846b17ffc68 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcCodeOriginTest.groovy +++ b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcCodeOriginTest.groovy @@ -1,9 +1,6 @@ -import datadog.trace.bootstrap.debugger.DebuggerContext.CodeOriginRecorder import com.google.common.util.concurrent.MoreExecutors import datadog.trace.agent.test.naming.VersionedNamingTestBase -import datadog.trace.api.DDSpanTypes import datadog.trace.bootstrap.debugger.DebuggerContext -import datadog.trace.bootstrap.instrumentation.api.Tags import example.GreeterGrpc import example.Helloworld import io.grpc.BindableService @@ -13,17 +10,15 @@ import io.grpc.inprocess.InProcessChannelBuilder import io.grpc.inprocess.InProcessServerBuilder import io.grpc.stub.StreamObserver -import java.lang.reflect.Method import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference -import static datadog.trace.api.config.TraceInstrumentationConfig.* +import static datadog.trace.agent.test.asserts.TagsAssert.assertTags -abstract class GrpcCodeOriginTest extends VersionedNamingTestBase { - def codeOriginRecorder +abstract class GrpcCodeOriginTest extends VersionedNamingTestBase { @Override final String service() { return null @@ -56,7 +51,7 @@ abstract class GrpcCodeOriginTest extends VersionedNamingTestBase { codeOriginSetup() } - def "test conversation #name"() { + def "code origin test #name"() { setup: def msgCount = serverMessageCount @@ -155,77 +150,13 @@ abstract class GrpcCodeOriginTest extends VersionedNamingTestBase { } }.flatten().sort() - - assert codeOriginRecorder.invoked - assertTraces(2) { - trace((hasClientMessageSpans() ? clientMessageCount * serverMessageCount : 0) + 1) { - span { - operationName clientOperation() - resourceName "example.Greeter/Conversation" - spanType DDSpanTypes.RPC - parent() - errored false - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.RPC_SERVICE" "example.Greeter" - "status.code" "OK" - "request.type" "example.Helloworld\$Response" - "response.type" "example.Helloworld\$Response" - peerServiceFrom(Tags.RPC_SERVICE) - defaultTags() - } - } - if (hasClientMessageSpans()) { - (1..(clientMessageCount * serverMessageCount)).each { - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "message.type" "example.Helloworld\$Response" - defaultTagsNoPeerService() - } - } - } - } - } - trace(clientMessageCount + 1) { - span { - operationName serverOperation() - resourceName "example.Greeter/Conversation" - spanType DDSpanTypes.RPC - childOf trace(0).get(0) - errored false - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "status.code" "OK" - - defaultTags(true) - } - } - clientRange.each { - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "message.type" "example.Helloworld\$Response" - defaultTags() - } - } - } - } + assert DebuggerContext.codeOriginRecorder != null + def span = TEST_WRITER.flatten().find { + it.operationName.toString() == "grpc.server.request" } + assertTags(span, { + it.codeOriginTags() + }, false) cleanup: channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) @@ -247,26 +178,6 @@ abstract class GrpcCodeOriginTest extends VersionedNamingTestBase { clientRange = 1..clientMessageCount serverRange = 1..serverMessageCount } - - - void codeOriginSetup() { - injectSysConfig(CODE_ORIGIN_FOR_SPANS_ENABLED, "true", true) - codeOriginRecorder = new CodeOriginRecorder() { - def invoked = false - @Override - String captureCodeOrigin(boolean entry) { - invoked = true - return "done" - } - - @Override - String captureCodeOrigin(Method method, boolean entry) { - invoked = true - return "done" - } - } - DebuggerContext.initCodeOrigin(codeOriginRecorder) - } } class GrpcCodeOriginForkedTest extends GrpcCodeOriginTest { diff --git a/dd-java-agent/instrumentation/kafka-clients-3.8/build.gradle b/dd-java-agent/instrumentation/kafka-clients-3.8/build.gradle index fceec499038..ede1e1e2453 100644 --- a/dd-java-agent/instrumentation/kafka-clients-3.8/build.gradle +++ b/dd-java-agent/instrumentation/kafka-clients-3.8/build.gradle @@ -38,6 +38,9 @@ dependencies { implementation project(':dd-java-agent:instrumentation:kafka-common') main_java17Implementation project(':dd-java-agent:instrumentation:kafka-common') + implementation project(':dd-java-agent:instrumentation:span-origin') + main_java17Implementation project(':dd-java-agent:instrumentation:span-origin') + testImplementation group: 'org.apache.kafka', name: 'kafka-clients', version: '3.8.0' testImplementation group: 'org.springframework.kafka', name: 'spring-kafka', version: '3.1.0' testImplementation group: 'org.springframework.kafka', name: 'spring-kafka-test', version: '3.1.0' diff --git a/dd-java-agent/instrumentation/kafka-clients-3.8/src/main/java/datadog/trace/instrumentation/kafka_clients38/MessageListenerInstrumentation.java b/dd-java-agent/instrumentation/kafka-clients-3.8/src/main/java/datadog/trace/instrumentation/kafka_clients38/MessageListenerInstrumentation.java new file mode 100644 index 00000000000..aae08d3a549 --- /dev/null +++ b/dd-java-agent/instrumentation/kafka-clients-3.8/src/main/java/datadog/trace/instrumentation/kafka_clients38/MessageListenerInstrumentation.java @@ -0,0 +1,43 @@ +package datadog.trace.instrumentation.kafka_clients38; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +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(InstrumenterModule.class) +public class MessageListenerInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + + public MessageListenerInstrumentation() { + super("kafka", "kafka-3.8"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassNamed("org.apache.kafka.clients.MetadataRecoveryStrategy"); // since 3.8 + } + + @Override + public String hierarchyMarkerType() { + return "org.springframework.kafka.listener.MessageListener"; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return implementsInterface(named(hierarchyMarkerType())); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(named("onMessage")), + datadog.trace.instrumentation.codeorigin.EntrySpanOriginAdvice.class.getName()); + } +} diff --git a/dd-java-agent/instrumentation/kafka-clients-3.8/src/test/groovy/KafkaCodeOriginForkedTest.groovy b/dd-java-agent/instrumentation/kafka-clients-3.8/src/test/groovy/KafkaCodeOriginForkedTest.groovy new file mode 100644 index 00000000000..f63c7d07b63 --- /dev/null +++ b/dd-java-agent/instrumentation/kafka-clients-3.8/src/test/groovy/KafkaCodeOriginForkedTest.groovy @@ -0,0 +1,187 @@ +import datadog.trace.agent.test.naming.VersionedNamingTestBase +import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags +import datadog.trace.common.writer.ListWriter +import datadog.trace.core.DDSpan +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.apache.kafka.clients.producer.KafkaProducer +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.clients.producer.ProducerRecord +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.listener.KafkaMessageListenerContainer +import org.springframework.kafka.listener.MessageListener +import org.springframework.kafka.test.utils.ContainerTestUtils +import org.springframework.kafka.test.utils.KafkaTestUtils +import org.testcontainers.containers.KafkaContainer +import org.testcontainers.utility.DockerImageName + +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit + +import static datadog.trace.agent.test.asserts.TagsAssert.assertTags +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope + +class KafkaCodeOriginForkedTest extends VersionedNamingTestBase { + static final SHARED_TOPIC = "shared.topic" + static final String MESSAGE = "Testing without headers for certain topics" + + @Override + boolean useStrictTraceWrites() { + // TODO fix this by making sure that spans get closed properly + return false + } + + @Override + void configurePreAgent() { + super.configurePreAgent() + + injectSysConfig("dd.kafka.e2e.duration.enabled", "false") + injectSysConfig("dd.kafka.legacy.tracing.enabled", "false") + injectSysConfig("dd.service", "KafkaClientTest") + codeOriginSetup() + } + + // filter out Kafka poll, since the function is called in a loop, giving inconsistent results + final ListWriter.Filter dropKafkaPoll = new ListWriter.Filter() { + @Override + boolean accept(List trace) { + return !(trace.size() == 1 && + trace.get(0).getResourceName().toString().equals("kafka.poll")) + } + } + + final ListWriter.Filter dropEmptyKafkaPoll = new ListWriter.Filter() { + @Override + boolean accept(List trace) { + return !(trace.size() == 1 && + trace.get(0).getResourceName().toString().equals("kafka.poll") && + trace.get(0).getTag(InstrumentationTags.KAFKA_RECORDS_COUNT).equals(0)) + } + } + + // TraceID, start times & names changed based on the configuration, so overriding the sort to give consistent test results + private static class SortKafkaTraces implements Comparator> { + @Override + int compare(List o1, List o2) { + return rootSpanTrace(o1) - rootSpanTrace(o2) + } + + int rootSpanTrace(List trace) { + assert !trace.isEmpty() + def rootSpan = trace.get(0).localRootSpan + switch (rootSpan.operationName.toString()) { + case "parent": + return 3 + case "kafka.poll": + return 2 + default: + return 1 + } + } + } + + def setup() { + TEST_WRITER.setFilter(dropKafkaPoll) + } + + @Override + int version() { + 1 + } + + @Override + String operation() { + return null + } + + String operationForProducer() { + "kafka.send" + } + + String operationForConsumer() { + return "kafka.process" + } + + String serviceForTimeInQueue() { + "kafka-queue" + } + + def "test with code origin"() { + setup: + // Create and start a Kafka container using Testcontainers + KafkaContainer kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")).withEmbeddedZookeeper().withEnv("KAFKA_CREATE_TOPICS", SHARED_TOPIC) + kafkaContainer.start() + + def senderProps = KafkaTestUtils.producerProps(kafkaContainer.getBootstrapServers()) + if (isDataStreamsEnabled()) { + senderProps.put(ProducerConfig.METADATA_MAX_AGE_CONFIG, 1000) + } + TEST_WRITER.setFilter(dropEmptyKafkaPoll) + KafkaProducer producer = new KafkaProducer<>(senderProps, new StringSerializer(), new StringSerializer()) + // set up the Kafka consumer properties + def consumerProperties = KafkaTestUtils.consumerProps( kafkaContainer.getBootstrapServers(),"sender", "false") + // create a Kafka consumer factory + def consumerFactory = new DefaultKafkaConsumerFactory(consumerProperties) + // set the topic that needs to be consumed + def containerProperties = new ContainerProperties(SHARED_TOPIC) + // create a Kafka MessageListenerContainer + def container = new KafkaMessageListenerContainer<>(consumerFactory, containerProperties) + // create a thread safe queue to store the received message + def records = new LinkedBlockingQueue>() + // setup a Kafka message listener + container.setupMessageListener(new MessageListener() { + @Override + void onMessage(ConsumerRecord record) { + TEST_WRITER.waitForTraces(1) + // ensure consistent ordering of traces + records.add(record) + } + }) + // start the container and underlying message listener + container.start() + // wait until the container has the required number of assigned partitions + ContainerTestUtils.waitForAssignment(container, container.assignedPartitions.size()) + when: + String greeting = "Hello Spring Kafka Sender!" + runUnderTrace("parent") { + producer.send(new ProducerRecord(SHARED_TOPIC,greeting)) { meta, ex -> + assert activeScope().isAsyncPropagating() + if (ex == null) { + runUnderTrace("producer callback") {} + } else { + runUnderTrace("producer exception: " + ex) {} + } + } + blockUntilChildSpansFinished(2) + } + + then: + // // check that the message was received + def received = records.poll(10, TimeUnit.SECONDS) + received.value() == greeting + received.key() == null + int nTraces = 2 + TEST_WRITER.waitForTraces(nTraces) + def traces = (Arrays.asList(TEST_WRITER.toArray()) as List>) + Collections.sort(traces, new SortKafkaTraces()) + assertTags(traces[0][0], { + it.codeOriginTags() + }, false) + + cleanup: + producer.close() + container?.stop() + kafkaContainer.stop() + } + + @Override + String service() { + return "KafkaClientTest" + } +} + + + + diff --git a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java index 273f92292b4..fcf7385a3b3 100644 --- a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java +++ b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java @@ -7,6 +7,7 @@ public class EntrySpanOriginAdvice { @Advice.OnMethodEnter + @SuppressWarnings("bytebuddy-exception-suppression") public static void onEnter(@Advice.Origin final Method method) { CodeOriginInfo.entry(method); } diff --git a/dd-java-agent/testing/build.gradle b/dd-java-agent/testing/build.gradle index 83b89c4f4b3..859a5d9ecd5 100644 --- a/dd-java-agent/testing/build.gradle +++ b/dd-java-agent/testing/build.gradle @@ -66,6 +66,8 @@ dependencies { implementation 'org.junit.platform:junit-platform-runner:1.9.0' implementation project(':dd-java-agent:appsec') + implementation project(':dd-java-agent:agent-debugger') + testImplementation project(':utils:test-utils') testImplementation project(':dd-java-agent:instrumentation:trace-annotation') diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy index 7e5397b362a..237f0c87dc3 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy @@ -3,6 +3,17 @@ package datadog.trace.agent.test import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger import ch.qos.logback.classic.util.ContextInitializer +import com.datadog.debugger.agent.ClassesToRetransformFinder +import com.datadog.debugger.agent.Configuration +import com.datadog.debugger.agent.ConfigurationUpdater +import com.datadog.debugger.agent.DebuggerTransformer +import com.datadog.debugger.agent.DenyListHelper +import com.datadog.debugger.agent.JsonSnapshotSerializer +import com.datadog.debugger.codeorigin.DefaultCodeOriginRecorder +import com.datadog.debugger.instrumentation.InstrumentationResult +import com.datadog.debugger.probe.ProbeDefinition +import com.datadog.debugger.sink.DebuggerSink +import com.datadog.debugger.sink.ProbeStatusSink import com.google.common.collect.Sets import datadog.communication.ddagent.DDAgentFeaturesDiscovery import datadog.communication.monitor.Monitoring @@ -28,6 +39,7 @@ import datadog.trace.api.time.SystemTimeSource import datadog.trace.bootstrap.ActiveSubsystems import datadog.trace.bootstrap.CallDepthThreadLocalMap import datadog.trace.bootstrap.InstrumentationErrors +import datadog.trace.bootstrap.debugger.DebuggerContext import datadog.trace.bootstrap.instrumentation.api.AgentDataStreamsMonitoring import datadog.trace.bootstrap.instrumentation.api.AgentSpan import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI @@ -41,6 +53,7 @@ import datadog.trace.core.DDSpan import datadog.trace.core.PendingTrace import datadog.trace.core.datastreams.DefaultDataStreamsMonitoring import datadog.trace.test.util.DDSpecification +import datadog.trace.util.AgentTaskScheduler import datadog.trace.util.Strings import de.thetaphi.forbiddenapis.SuppressForbidden import edu.umd.cs.findbugs.annotations.SuppressFBWarnings @@ -71,7 +84,11 @@ import static datadog.communication.http.OkHttpUtils.buildHttpClient import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_HOST import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_TIMEOUT import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_PORT +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_ENABLED +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_VERIFY_BYTECODE +import static datadog.trace.api.config.TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.closePrevious +import static datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER /** * A spock test runner which automatically applies instrumentation and exposes a global trace @@ -288,6 +305,40 @@ abstract class AgentTestRunner extends DDSpecification implements AgentBuilder.L ((Logger) LoggerFactory.getLogger("org.testcontainers")).setLevel(Level.DEBUG) } + def codeOriginSetup() { + injectSysConfig(CODE_ORIGIN_FOR_SPANS_ENABLED, "true", true) + injectSysConfig(DEBUGGER_ENABLED, "false", true) + injectSysConfig(DEBUGGER_VERIFY_BYTECODE, "false", true) + + def configuration = Configuration.builder() + .setService("code origin test") + .build() + + rebuildConfig() + + def config = Config.get() + + def probeStatusSink = new ProbeStatusSink(config, "http://datadoghq.com", false) + + def sink = new DebuggerSink(config, probeStatusSink) + ConfigurationUpdater configurationUpdater = new ConfigurationUpdater(INSTRUMENTATION, DebuggerTransformer::new, config, sink, + new ClassesToRetransformFinder()) + + INSTRUMENTATION.addTransformer(new DebuggerTransformer(config, configuration, { + ProbeDefinition definition, InstrumentationResult result -> + }, sink)) + DebuggerContext.initProbeResolver(configurationUpdater) + DebuggerContext.initClassFilter(new DenyListHelper(null)) + DebuggerContext.initValueSerializer(new JsonSnapshotSerializer()) + + DebuggerContext.initCodeOrigin(new DefaultCodeOriginRecorder(config, configurationUpdater, new AgentTaskScheduler(TASK_SCHEDULER) { + @Override + void execute(Runnable target) { + target.run() + } + })) + } + @SuppressForbidden void setupSpec() { diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy index 33b8233af24..98bbf2da0f5 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy @@ -13,6 +13,9 @@ import groovy.transform.stc.SimpleType import java.util.regex.Pattern +import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_FRAME +import static java.lang.String.format + class TagsAssert { private final long spanParentId private final Map tags @@ -111,6 +114,15 @@ class TagsAssert { } } + def codeOriginTags() { + assert tags[DDTags.DD_CODE_ORIGIN_TYPE] != null + assert tags[format(DD_CODE_ORIGIN_FRAME, 0, "file")] != null + assert tags[format(DD_CODE_ORIGIN_FRAME, 0, "method")] != null + assert tags[format(DD_CODE_ORIGIN_FRAME, 0, "line")] != null + assert tags[format(DD_CODE_ORIGIN_FRAME, 0, "type")] != null + assert tags[format(DD_CODE_ORIGIN_FRAME, 0, "signature")] != null + } + def errorTags(Throwable error) { errorTags(error.getClass(), error.getMessage()) }