diff --git a/dd-java-agent/instrumentation/grpc-1.5/build.gradle b/dd-java-agent/instrumentation/grpc-1.5/build.gradle index c3f3600f9c4..f0510e3be39 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/build.gradle +++ b/dd-java-agent/instrumentation/grpc-1.5/build.gradle @@ -1,52 +1,56 @@ plugins { id 'com.google.protobuf' version '0.10.0' - id 'dd-trace-java.module.instrumentation' - id 'idea' } -muzzle { - pass { - group = "io.grpc" - module = "grpc-core" - versions = "[1.5.0,)" - } -} +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') -// First version with Mac M1 support def grpcVersion = '1.42.2' -protobuf { - protoc { - // Download compiler rather than using locally installed version: - // First version with Mac M1 support - artifact = 'com.google.protobuf:protoc:3.17.3' - } - plugins { - // First version with aarch support - grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" } - } - generateProtoTasks { - ofSourceSet("test").configureEach { - plugins { grpc {} } - } + +muzzle { + pass { + group = 'io.grpc' + module = 'grpc-core' + versions = '[1.5.0,)' + assertInverse = true } } -addTestSuiteForDir('latestDepTest', 'test') - dependencies { compileOnly group: 'io.grpc', name: 'grpc-core', version: grpcVersion + testImplementation group: 'io.grpc', name: 'grpc-core', version: grpcVersion testImplementation group: 'io.grpc', name: 'grpc-netty', version: grpcVersion testImplementation group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion testImplementation group: 'io.grpc', name: 'grpc-stub', version: grpcVersion testImplementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2' - testImplementation project(':dd-java-agent:agent-debugger:debugger-bootstrap') - latestDepTestImplementation sourceSets.test.output // include the protobuf generated classes - latestDepTestCompileOnly group: 'io.grpc', name: 'grpc-core', version: '1.+' - - latestDepTestImplementation group: 'io.grpc', name: 'grpc-inprocess', version: '1.+' + latestDepTestImplementation group: 'io.grpc', name: 'grpc-core', version: '1.+' latestDepTestImplementation group: 'io.grpc', name: 'grpc-netty', version: '1.+' latestDepTestImplementation group: 'io.grpc', name: 'grpc-protobuf', version: '1.+' latestDepTestImplementation group: 'io.grpc', name: 'grpc-stub', version: '1.+' } + +def protocVersion = '3.17.3' +// aarch_64 support was added in 3.17.3 +def protocArch = System.getProperty("os.arch") == "aarch64" ? ":osx-x86_64" : "" +def grpcArch = System.getProperty("os.arch") == "aarch64" ? ":osx-x86_64" : "" + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:${protocVersion}${protocArch}" + } + plugins { + grpc { + artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}${grpcArch}" + } + } + generateProtoTasks { + ofSourceSet("test").configureEach { + plugins { + grpc {} + } + } + } +} diff --git a/dd-java-agent/instrumentation/grpc-1.5/gradle.lockfile b/dd-java-agent/instrumentation/grpc-1.5/gradle.lockfile deleted file mode 100644 index 2e29e9b84cf..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/gradle.lockfile +++ /dev/null @@ -1,181 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:grpc-1.5:dependencies --write-locks -cafe.cryptography:curve25519-elisabeth:0.1.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -cafe.cryptography:ed25519-elisabeth:0.1.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -ch.qos.logback:logback-classic:1.2.13=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.13=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq.okhttp3:okhttp:3.12.15=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq.okio:okio:1.17.6=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq:dd-instrument-java:0.0.4=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq:java-dogstatsd-client:4.4.5=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.datadoghq:sketches-java:0.8.3=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.javaparser:javaparser-core:3.25.6=codenarc -com.github.jnr:jffi:1.3.15=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-a64asm:1.0.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-constants:0.10.4=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-enxio:0.32.20=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-ffi:2.2.19=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-posix:3.1.22=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-unixsocket:0.38.25=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.jnr:jnr-x86asm:1.0.2=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.github.spotbugs:spotbugs-annotations:4.10.3=compileClasspath,compileProtoPath,spotbugs -com.github.spotbugs:spotbugs:4.10.3=spotbugs -com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs -com.google.android:annotations:4.1.1.4=compileProtoPath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.api.grpc:proto-google-common-protos:2.0.1=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.api.grpc:proto-google-common-protos:2.64.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,compileProtoPath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,testAnnotationProcessor,testCompileClasspath,testCompileProtoPath -com.google.auto.service:auto-service:1.1.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.auto:auto-common:1.2.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,compileProtoPath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.code.gson:gson:2.14.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,spotbugs -com.google.code.gson:gson:2.8.6=compileProtoPath,testCompileProtoPath,testRuntimeClasspath -com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.errorprone:error_prone_annotations:2.48.0=spotbugs -com.google.errorprone:error_prone_annotations:2.50.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -com.google.errorprone:error_prone_annotations:2.9.0=compileProtoPath -com.google.guava:failureaccess:1.0.1=annotationProcessor,compileProtoPath,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:failureaccess:1.0.3=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.guava:guava:30.1.1-android=compileProtoPath -com.google.guava:guava:32.0.1-jre=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:guava:33.6.0-jre=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,compileProtoPath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testAnnotationProcessor,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.j2objc:j2objc-annotations:1.3=compileProtoPath -com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.j2objc:j2objc-annotations:3.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.protobuf:protobuf-java:3.18.2=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.google.protobuf:protobuf-java:3.25.9=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -com.google.protobuf:protoc:3.17.3=protobufToolsLocator_protoc -com.google.re2j:re2j:1.8=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:3.12.12=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.squareup.okio:okio:1.17.5=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -com.thoughtworks.qdox:qdox:1.12.1=codenarc -commons-fileupload:commons-fileupload:1.5=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -commons-io:commons-io:2.11.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -commons-io:commons-io:2.21.0=spotbugs -de.thetaphi:forbiddenapis:3.10=compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-api:1.42.2=compileClasspath,compileProtoPath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-api:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-context:1.42.2=compileClasspath,compileProtoPath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-context:1.83.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-core:1.42.2=compileClasspath,compileProtoPath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-core:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-inprocess:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-netty:1.42.2=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-netty:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-protobuf-lite:1.42.2=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-protobuf-lite:1.83.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-protobuf:1.42.2=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-protobuf:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-stub:1.42.2=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.grpc:grpc-stub:1.83.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:grpc-util:1.83.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.grpc:protoc-gen-grpc-java:1.42.2=protobufToolsLocator_grpc -io.leangen.geantyref:geantyref:1.3.16=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-buffer:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-buffer:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec-base:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec-compression:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec-http2:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-codec-http2:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec-http:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-codec-http:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec-socks:4.1.63.Final=testCompileProtoPath,testRuntimeClasspath -io.netty:netty-codec-socks:4.2.15.Final=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-codec:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-common:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-common:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-handler-proxy:4.1.63.Final=testCompileProtoPath,testRuntimeClasspath -io.netty:netty-handler-proxy:4.2.15.Final=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-handler:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-handler:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-resolver:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-resolver:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.netty:netty-transport:4.1.63.Final=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -io.netty:netty-transport:4.2.15.Final=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.perfmark:perfmark-api:0.23.0=compileProtoPath,testCompileProtoPath,testRuntimeClasspath -io.perfmark:perfmark-api:0.27.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -io.sqreen:libsqreen:17.5.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -javax.annotation:javax.annotation-api:1.3.2=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -jaxen:jaxen:2.0.6=spotbugs -junit:junit:4.13.2=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -net.bytebuddy:byte-buddy-agent:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -net.java.dev.jna:jna-platform:5.8.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -net.java.dev.jna:jna:5.8.0=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -net.sf.saxon:Saxon-HE:12.10=spotbugs -org.apache.ant:ant-antlr:1.10.14=codenarc -org.apache.ant:ant-junit:1.10.14=codenarc -org.apache.bcel:bcel:6.12.0=spotbugs -org.apache.commons:commons-lang3:3.20.0=spotbugs -org.apache.commons:commons-text:1.15.0=spotbugs -org.apache.logging.log4j:log4j-api:2.26.1=spotbugs -org.apache.logging.log4j:log4j-core:2.26.1=spotbugs -org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,testCompileClasspath -org.checkerframework:checker-compat-qual:2.5.5=compileProtoPath -org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -org.codehaus.groovy:groovy-ant:3.0.23=codenarc -org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc -org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.25=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.codehaus.groovy:groovy-templates:3.0.23=codenarc -org.codehaus.groovy:groovy-xml:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.25=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.codehaus.mojo:animal-sniffer-annotations:1.19=compileProtoPath,testCompileProtoPath,testRuntimeClasspath -org.codehaus.mojo:animal-sniffer-annotations:1.27=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath -org.codenarc:CodeNarc:3.7.0=codenarc -org.dom4j:dom4j:2.2.0=spotbugs -org.gmetrics:GMetrics:2.1.0=codenarc -org.hamcrest:hamcrest-core:1.3=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.hamcrest:hamcrest:3.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.jctools:jctools-core-jdk11:4.0.6=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.jctools:jctools-core:4.0.6=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.jspecify:jspecify:1.0.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.14.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:5.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.14.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-runner:1.14.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-api:1.14.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-commons:1.14.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit:junit-bom:5.14.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.junit:junit-bom:6.1.2=compileProtoPath,spotbugs -org.mockito:mockito-core:4.4.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.3=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.10.1=spotbugs -org.ow2.asm:asm-analysis:9.7.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.10.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,spotbugs,testCompileProtoPath,testRuntimeClasspath -org.ow2.asm:asm-tree:9.10.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,spotbugs,testCompileProtoPath,testRuntimeClasspath -org.ow2.asm:asm-util:9.10.1=spotbugs -org.ow2.asm:asm-util:9.7.1=latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.slf4j:jcl-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.slf4j:log4j-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,compileProtoPath,muzzleBootstrap,muzzleTooling,runtimeClasspath -org.slf4j:slf4j-api:1.7.32=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.17=spotbugsSlf4j -org.slf4j:slf4j-api:2.0.18=spotbugs -org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j -org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,compileProtoPath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileProtoPath,testRuntimeClasspath -org.spockframework:spock-bom:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.spockframework:spock-core:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.tabletest:tabletest-junit:1.2.2=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.tabletest:tabletest-parser:1.2.1=latestDepTestCompileClasspath,latestDepTestCompileProtoPath,latestDepTestRuntimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath -org.xmlresolver:xmlresolver:5.3.3=spotbugs -empty=latestDepTestProtobuf,protobuf,spotbugsPlugins,testProtobuf diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/QueuedCommandInstrumentation.java b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/QueuedCommandInstrumentation.java index d44454e6c1d..34d75ef9049 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/QueuedCommandInstrumentation.java +++ b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/QueuedCommandInstrumentation.java @@ -67,12 +67,6 @@ public static final class Construct { public static void after(@Advice.This Object command) { ContextStore contextStore = InstrumentationContext.get(QUEUED_COMMAND, STATE); capture(contextStore, command); - // FIXME hard to handle both the lifecyle and get access to the queue instance in the same - // frame within the WriteQueue class. - // This means we can't get the queue length. A (bad) alternative would be to instrument - // ConcurrentLinkedQueue broadly, - // or we could write more brittle instrumentation targeting code patterns in different gRPC - // versions. QueueTimerHelper.startQueuingTimer( contextStore, Channel.class, ConcurrentLinkedQueue.class, 0, command); } diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientCallImplInstrumentation.java b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientCallImplInstrumentation.java index fcb8615aaa2..8db041f8695 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientCallImplInstrumentation.java +++ b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientCallImplInstrumentation.java @@ -129,7 +129,6 @@ public static void after(@Advice.Enter AgentScope scope) { public static final class SendMessage { @Advice.OnMethodEnter public static AgentScope before(@Advice.This ClientCall call) { - // could create a message span here for the request AgentSpan span = InstrumentationContext.get(ClientCall.class, AgentSpan.class).get(call); if (span != null) { return activateSpan(span); diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientStreamListenerImplInstrumentation.java b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientStreamListenerImplInstrumentation.java index 953a21be327..657d3477b49 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientStreamListenerImplInstrumentation.java +++ b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/ClientStreamListenerImplInstrumentation.java @@ -41,7 +41,6 @@ public void methodAdvice(MethodTransformer transformer) { public static final class Construct { @Advice.OnMethodExit public static void capture(@Advice.This ClientStreamListener listener) { - // instrumentation of ClientCallImpl::start ensures this scope is present and valid AgentSpan span = activeSpan(); if (null != span) { InstrumentationContext.get(ClientStreamListener.class, AgentSpan.class).put(listener, span); @@ -66,11 +65,8 @@ public static void exceptionThrown( } public static final class RecordActivity { - @Advice.OnMethodEnter public static AgentScope before(@Advice.This ClientStreamListener listener) { - // activate the span so serialisation work is accounted for, whichever thread the work is done - // on AgentSpan span = InstrumentationContext.get(ClientStreamListener.class, AgentSpan.class).get(listener); if (span != null) { @@ -87,18 +83,9 @@ public static void after(@Advice.Enter AgentScope scope) { } } - /* - A call to 'headersAvailable' is optional - meaning that it may not appear at all but if it appears - it will be followed by a call to `messageRead`. In order to properly cooperate with the `messageRead` instrumentation - we must make sure that when this method is finished the associated span is 'migrated' - such that `messageRead` - instrumentation can correctly 'resume' the span. - */ public static final class RecordHeaders { - @Advice.OnMethodEnter public static AgentScope before(@Advice.This ClientStreamListener listener) { - // activate the span so serialisation work is accounted for, whichever thread the work is done - // on AgentSpan span = InstrumentationContext.get(ClientStreamListener.class, AgentSpan.class).get(listener); if (span != null) { diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcClientDecorator.java b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcClientDecorator.java index bcf6a5461b5..6ceda7d1efa 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcClientDecorator.java +++ b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcClientDecorator.java @@ -46,7 +46,6 @@ private static DataStreamsContext createDsmContext() { private static final ClassValue MESSAGE_TYPES = GenericClassValue.of( - // Uses inner class for predictable name for Instrumenter.Default.helperClassNames() new Function, UTF8BytesString>() { @Override public UTF8BytesString apply(Class input) { @@ -93,15 +92,12 @@ protected String service() { public AgentSpan startCall(MethodDescriptor method) { if (IGNORED_METHODS.contains(method.getFullMethodName())) { - // if the method is ignored we want to preserve the old behaviour and not make injection - // happen return AgentTracer.blackholeSpan(); } AgentSpan span = startSpan(COMPONENT_NAME.toString(), OPERATION_NAME) .setTag("request.type", requestMessageType(method)) .setTag("response.type", responseMessageType(method)) - // method.getServiceName() may not be available on some grpc versions .setTag( Tags.RPC_SERVICE, RPC_SERVICE_CACHE.computeIfAbsent( @@ -124,7 +120,6 @@ public void onClose(final AgentSpan span, final Status status) { span.setTag(InstrumentationTags.GRPC_STATUS_CODE, status.getCode().value()); span.setTag("status.description", status.getDescription()); - // TODO why is there a mismatch between client / server for calling the onError method? onError(span, status.getCause()); span.setError(CLIENT_ERROR_STATUSES.get(status.getCode().value())); } diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcInjectAdapter.java b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcInjectAdapter.java index 24a6c7df1aa..d896ba4e26d 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcInjectAdapter.java +++ b/dd-java-agent/instrumentation/grpc-1.5/src/main/java/datadog/trace/instrumentation/grpc/client/GrpcInjectAdapter.java @@ -20,8 +20,7 @@ public final class GrpcInjectAdapter implements CarrierSetter { public void set(final Metadata carrier, final String key, final String value) { Metadata.Key metadataKey = KEY_CACHE.computeIfAbsent(key, KEY_MAKER); if (carrier.containsKey(metadataKey)) { - carrier.removeAll( - metadataKey); // Remove existing to ensure identical behavior with other carriers + carrier.removeAll(metadataKey); } carrier.put(metadataKey, value); } diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcInjectAdapterTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcInjectAdapterTest.groovy deleted file mode 100644 index abb6ad7db8c..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcInjectAdapterTest.groovy +++ /dev/null @@ -1,47 +0,0 @@ -import datadog.trace.agent.test.naming.VersionedNamingTestBase -import io.grpc.Metadata -import static datadog.trace.instrumentation.grpc.client.GrpcInjectAdapter.SETTER - -class GrpcInjectAdapterTest extends VersionedNamingTestBase { - def "carrier overrides values for duplicate keys"() { - setup: - def carrier = new Metadata() - - def baggage = [ - ["ot-baggage-foo", "v1"], - ["ot-baggage-foo", "v2"], - ["ot-baggage-bar", "v3"] - ] - - when: - baggage.each { pair -> - def (key, value) = pair - SETTER.set(carrier, key, value) - } - - then: - carrier.headerCount() == 2 - carrier.get(getKey("ot-baggage-foo")) == "v2" // overridden value wins - carrier.get(getKey("ot-baggage-bar")) == "v3" - } - - Metadata.Key getKey(String key){ - Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER) - } - - - @Override - int version() { - return 0 - } - - @Override - String service() { - return null - } - - @Override - String operation() { - return null - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcStreamingTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcStreamingTest.groovy deleted file mode 100644 index 6d28463e9d7..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcStreamingTest.groovy +++ /dev/null @@ -1,288 +0,0 @@ -import com.google.common.util.concurrent.MoreExecutors -import datadog.trace.agent.test.naming.VersionedNamingTestBase -import datadog.trace.api.DDSpanTypes -import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags -import datadog.trace.bootstrap.instrumentation.api.Tags -import example.GreeterGrpc -import example.Helloworld -import io.grpc.BindableService -import io.grpc.ManagedChannel -import io.grpc.Server -import io.grpc.inprocess.InProcessChannelBuilder -import io.grpc.inprocess.InProcessServerBuilder -import io.grpc.stub.StreamObserver - -import java.util.concurrent.CopyOnWriteArrayList -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicReference - -abstract class GrpcStreamingTest extends VersionedNamingTestBase { - - @Override - final String service() { - return null - } - - @Override - final String operation() { - return null - } - - protected abstract String clientOperation() - - protected abstract String serverOperation() - - protected boolean hasClientMessageSpans() { - false - } - - @Override - protected void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("dd.trace.grpc.ignored.inbound.methods", "example.Greeter/IgnoreInbound") - injectSysConfig("dd.trace.grpc.ignored.outbound.methods", "example.Greeter/Ignore") - if (hasClientMessageSpans()) { - injectSysConfig("integration.grpc-message.enabled", "true") - } - // here to trigger wrapping to record scheduling time - the logic is trivial so it's enough to verify - // that ClassCastExceptions do not arise from the wrapping - injectSysConfig("dd.profiling.enabled", "true") - } - - def "test conversation #name"() { - setup: - - def msgCount = serverMessageCount - def serverReceived = new CopyOnWriteArrayList<>() - def clientReceived = new CopyOnWriteArrayList<>() - def error = new AtomicReference() - - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - StreamObserver conversation(StreamObserver observer) { - return new StreamObserver() { - @Override - void onNext(Helloworld.Response value) { - - serverReceived << value.message - - (1..msgCount).each { - if (TEST_TRACER.isAsyncPropagationEnabled()) { - observer.onNext(value) - } else { - observer.onError(new IllegalStateException("not async propagating!")) - } - } - } - - @Override - void onError(Throwable t) { - if (TEST_TRACER.isAsyncPropagationEnabled()) { - error.set(t) - observer.onError(t) - } else { - observer.onError(new IllegalStateException("not async propagating!")) - } - } - - @Override - void onCompleted() { - if (TEST_TRACER.isAsyncPropagationEnabled()) { - observer.onCompleted() - } else { - observer.onError(new IllegalStateException("not async propagating!")) - } - } - } - } - } - Server server = InProcessServerBuilder.forName(getClass().name).addService(greeter) - .executor(directExecutor ? MoreExecutors.directExecutor() : Executors.newCachedThreadPool()) - .build().start() - - ManagedChannel channel = InProcessChannelBuilder.forName(getClass().name).build() - GreeterGrpc.GreeterStub client = GreeterGrpc.newStub(channel).withWaitForReady() - - when: - def streamObserver = client.conversation(new StreamObserver() { - @Override - void onNext(Helloworld.Response value) { - if (TEST_TRACER.isAsyncPropagationEnabled()) { - clientReceived << value.message - } else { - error.set(new IllegalStateException("not async propagating!")) - } - } - - @Override - void onError(Throwable t) { - if (TEST_TRACER.isAsyncPropagationEnabled()) { - error.set(t) - } else { - error.set(new IllegalStateException("not async propagating!")) - } - } - - @Override - void onCompleted() { - if (!TEST_TRACER.isAsyncPropagationEnabled()) { - error.set(new IllegalStateException("not async propagating!")) - } - } - }) - - clientRange.each { - def message = Helloworld.Response.newBuilder().setMessage("call $it").build() - streamObserver.onNext(message) - } - streamObserver.onCompleted() - - then: - error.get() == null - TEST_WRITER.waitForTraces(2) - error.get() == null - serverReceived == clientRange.collect { "call $it" } - clientReceived == serverRange.collect { - clientRange.collect { - "call $it" - } - }.flatten().sort() - - 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" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - "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" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - 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() - } - } - } - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - - where: - name | clientMessageCount | serverMessageCount | directExecutor - "A" | 1 | 1 | false - "B" | 2 | 1 | false - "C" | 1 | 2 | false - "D" | 2 | 2 | false - "E" | 3 | 3 | false - "A" | 1 | 1 | true - "B" | 2 | 1 | true - "C" | 1 | 2 | true - "D" | 2 | 2 | true - "E" | 3 | 3 | true - - clientRange = 1..clientMessageCount - serverRange = 1..serverMessageCount - } -} - -class GrpcStreamingV0Test extends GrpcStreamingTest { - - @Override - int version() { - return 0 - } - - @Override - protected String clientOperation() { - return "grpc.client" - } - - @Override - protected String serverOperation() { - return "grpc.server" - } -} - -class GrpcStreamingV1ForkedTest extends GrpcStreamingTest { - - @Override - int version() { - return 1 - } - - @Override - protected String clientOperation() { - return "grpc.client.request" - } - - @Override - protected String serverOperation() { - return "grpc.server.request" - } -} - -class GrpcStreamingClientMessageEnabledTest extends GrpcStreamingV0Test { - @Override - protected boolean hasClientMessageSpans() { - true - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcTest.groovy deleted file mode 100644 index 4f29f063841..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/GrpcTest.groovy +++ /dev/null @@ -1,745 +0,0 @@ - -import static datadog.trace.agent.test.asserts.TagsAssert.codeOriginTags -import static datadog.trace.api.config.TraceInstrumentationConfig.GRPC_SERVER_ERROR_STATUSES - -import com.google.common.util.concurrent.MoreExecutors -import datadog.trace.agent.test.naming.VersionedNamingTestBase -import datadog.trace.api.DDSpanId -import datadog.trace.api.DDSpanTypes -import datadog.trace.api.DDTags -import datadog.trace.api.function.TriConsumer -import datadog.trace.api.gateway.Flow -import datadog.trace.api.gateway.RequestContext -import datadog.trace.api.gateway.RequestContextSlot -import datadog.trace.bootstrap.instrumentation.api.AgentPropagation -import datadog.trace.bootstrap.instrumentation.api.AgentTracer -import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags -import datadog.trace.bootstrap.instrumentation.api.Tags -import datadog.trace.core.datastreams.StatsGroup -import datadog.trace.instrumentation.grpc.server.GrpcExtractAdapter -import example.GreeterGrpc -import example.Helloworld -import io.grpc.BindableService -import io.grpc.ManagedChannel -import io.grpc.Metadata -import io.grpc.Server -import io.grpc.Status -import io.grpc.StatusRuntimeException -import io.grpc.inprocess.InProcessChannelBuilder -import io.grpc.inprocess.InProcessServerBuilder -import io.grpc.netty.NettyChannelBuilder -import io.grpc.netty.NettyServerBuilder -import io.grpc.stub.StreamObserver -import spock.lang.Shared - -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors -import java.util.concurrent.ForkJoinPool -import java.util.concurrent.TimeUnit -import java.util.function.BiFunction -import java.util.function.Function -import java.util.function.Supplier - -import static datadog.trace.agent.test.utils.TraceUtils.basicSpan -import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -import static datadog.trace.api.gateway.Events.EVENTS - -abstract class GrpcTest extends VersionedNamingTestBase { - - @Shared - def ig - - def collectedAppSecHeaders = [:] - boolean appSecHeaderDone = false - def collectedAppSecReqMsgs = [] - def collectedAppSecServerMethods = [] - - @Override - final String service() { - return null - } - - @Override - final String operation() { - return null - } - - protected abstract String clientOperation() - - protected abstract String serverOperation() - - protected boolean hasClientMessageSpans() { - false - } - - @Override - protected void configurePreAgent() { - super.configurePreAgent() - codeOriginSetup() - injectSysConfig("dd.trace.grpc.ignored.inbound.methods", "example.Greeter/IgnoreInbound") - injectSysConfig("dd.trace.grpc.ignored.outbound.methods", "example.Greeter/Ignore") - if (hasClientMessageSpans()) { - injectSysConfig("integration.grpc-message.enabled", "true") - } - // here to trigger wrapping to record scheduling time - the logic is trivial so it's enough to verify - // that ClassCastExceptions do not arise from the wrapping - injectSysConfig("dd.profiling.enabled", "true") - injectSysConfig(GRPC_SERVER_ERROR_STATUSES, "2-14", true) - } - - def setupSpec() { - ig = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC) - } - - def setup() { - ig.registerCallback(EVENTS.requestStarted(), { -> new Flow.ResultFlow(new Object()) } as Supplier) - ig.registerCallback(EVENTS.requestHeader(), { reqCtx, name, value -> - collectedAppSecHeaders[name] = value - } as TriConsumer) - ig.registerCallback(EVENTS.requestHeaderDone(), { - appSecHeaderDone = true - Flow.ResultFlow.empty() - } as Function>) - ig.registerCallback(EVENTS.grpcServerRequestMessage(), { reqCtx, obj -> - collectedAppSecReqMsgs << obj - Flow.ResultFlow.empty() - } as BiFunction>) - ig.registerCallback(EVENTS.grpcServerMethod(), { reqCtx, method -> - collectedAppSecServerMethods << method - Flow.ResultFlow.empty() - } as BiFunction>) - } - - def cleanup() { - ig.reset() - } - - def "test request-response #name #executor.class.simpleName"() { - setup: - - ExecutorService responseExecutor = Executors.newSingleThreadExecutor() - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - void sayHello( - final Helloworld.Request req, final StreamObserver responseObserver) { - final Helloworld.Response reply = Helloworld.Response.newBuilder().setMessage("Hello $req.name").build() - responseExecutor.execute { - if (TEST_TRACER.activeSpan() == null) { - responseObserver.onError(new IllegalStateException("no active span")) - } else { - responseObserver.onNext(reply) - responseObserver.onCompleted() - } - } - } - } - def builder = NettyServerBuilder.forPort(0).addService(greeter).executor(executor) - (0..extraBuildCalls).each { builder.build() } - Server server = builder.build().start() - - - ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", server.getPort()).usePlaintext().build() - GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel) - - when: - def response = runUnderTrace("parent") { - def resp = client.sayHello(Helloworld.Request.newBuilder().setName(name).build()) - return resp - } - // wait here to make checkpoint asserts deterministic - TEST_WRITER.waitForTraces(2) - if (isDataStreamsEnabled()) { - TEST_DATA_STREAMS_WRITER.waitForGroups(2) - } - - then: - response.message == "Hello $name" - codeOriginTags(TEST_WRITER) - assertTraces(2) { - trace(hasClientMessageSpans() ? 3 : 2) { - basicSpan(it, "parent") - span { - operationName clientOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.RPC_SERVICE" "example.Greeter" - "$Tags.PEER_HOSTNAME" "localhost" - "$Tags.PEER_HOST_IPV4" "127.0.0.1" - "$Tags.PEER_PORT" server.port - "status.code" "OK" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - "request.type" "example.Helloworld\$Request" - "response.type" "example.Helloworld\$Response" - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - peerServiceFrom(Tags.RPC_SERVICE) - defaultTags() - } - } - if (hasClientMessageSpans()) { - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(1) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "message.type" "example.Helloworld\$Response" - defaultTagsNoPeerService() - } - } - } - } - trace(2) { - span { - operationName serverOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - childOf trace(0).get(1) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "status.code" "OK" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - defaultTags(true) - } - } - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "message.type" "example.Helloworld\$Request" - defaultTags() - } - } - } - } - - and: - def traceId = TEST_WRITER[0].traceId.first() - traceId.toLong() as String == collectedAppSecHeaders['x-datadog-trace-id'] - collectedAppSecReqMsgs.size() == 1 - collectedAppSecReqMsgs.first().name == name - collectedAppSecServerMethods.size() == 1 - collectedAppSecServerMethods.first() == 'example.Greeter/SayHello' - - and: - if (isDataStreamsEnabled()) { - StatsGroup first = TEST_DATA_STREAMS_WRITER.groups.find { it.parentHash == 0 } - verifyAll(first) { - tags.hasAllTags("direction:out", "type:grpc") - } - - StatsGroup second = TEST_DATA_STREAMS_WRITER.groups.find { it.parentHash == first.hash } - verifyAll(second) { - tags.hasAllTags("direction:in", "type:grpc") - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - if (executor instanceof ExecutorService) { - (executor as ExecutorService).shutdownNow() - } - - where: - name | executor | extraBuildCalls - "some name" | MoreExecutors.directExecutor() | 0 - "some other name" | MoreExecutors.directExecutor() | 0 - "some name" | newWorkStealingPool() | 0 - "some other name" | newWorkStealingPool() | 0 - "some name" | Executors.newSingleThreadExecutor() | 0 - "some other name" | Executors.newSingleThreadExecutor() | 0 - "some name" | MoreExecutors.directExecutor() | 1 - "some other name" | MoreExecutors.directExecutor() | 1 - "some name" | newWorkStealingPool() | 1 - "some other name" | newWorkStealingPool() | 1 - "some name" | Executors.newSingleThreadExecutor() | 1 - "some other name" | Executors.newSingleThreadExecutor() | 1 - } - - def "test error - #name"() { - setup: - def error = status.asException() - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - void sayHello( - final Helloworld.Request req, final StreamObserver responseObserver) { - responseObserver.onError(error) - } - } - Server server = InProcessServerBuilder.forName(getClass().name).addService(greeter).directExecutor().build().start() - - ManagedChannel channel = InProcessChannelBuilder.forName(getClass().name).build() - GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel) - - when: - client.sayHello(Helloworld.Request.newBuilder().setName(name).build()) - // wait here to make checkpoint asserts deterministic - TEST_WRITER.waitForTraces(2) - - then: - thrown StatusRuntimeException - - assertTraces(2) { - trace(1) { - span { - operationName clientOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - parent() - errored true - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.RPC_SERVICE" "example.Greeter" - "status.code" "${status.code.name()}" - "grpc.status.code" "${status.code.name()}" - "$InstrumentationTags.GRPC_STATUS_CODE" status.code.value() - "status.description" description - "request.type" "example.Helloworld\$Request" - "response.type" "example.Helloworld\$Response" - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - peerServiceFrom(Tags.RPC_SERVICE) - defaultTags() - } - } - } - trace(2) { - span { - operationName serverOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - childOf trace(0).get(0) - errored true - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "status.code" "${status.code.name()}" - "grpc.status.code" "${status.code.name()}" - "$InstrumentationTags.GRPC_STATUS_CODE" status.code.value() - "status.description" description - if (status.cause != null) { - errorTags status.cause.class, status.cause.message - } - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - defaultTags(true) - } - } - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "message.type" "example.Helloworld\$Request" - defaultTags() - } - } - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - - where: - name | status | description - "Runtime - cause" | Status.UNKNOWN.withCause(new RuntimeException("some error")) | null - "Status - cause" | Status.PERMISSION_DENIED.withCause(new RuntimeException("some error")) | null - "StatusRuntime - cause" | Status.UNIMPLEMENTED.withCause(new RuntimeException("some error")) | null - "Runtime - description" | Status.UNKNOWN.withDescription("some description") | "some description" - "Status - description" | Status.PERMISSION_DENIED.withDescription("some description") | "some description" - "StatusRuntime - description" | Status.UNIMPLEMENTED.withDescription("some description") | "some description" - } - - def "test error thrown - #name"() { - setup: - - def error = status.asRuntimeException() - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - void sayHello( - final Helloworld.Request req, final StreamObserver responseObserver) { - throw error - } - } - Server server = NettyServerBuilder.forPort(0).addService(greeter).directExecutor().build().start() - - ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", server.getPort()).usePlaintext().build() - GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel) - - when: - client.sayHello(Helloworld.Request.newBuilder().setName(name).build()) - // wait here to make checkpoint asserts deterministic - TEST_WRITER.waitForTraces(2) - - then: - thrown StatusRuntimeException - - assertTraces(2) { - trace(1) { - span { - operationName clientOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - parent() - errored true - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.RPC_SERVICE" "example.Greeter" - "$Tags.PEER_HOSTNAME" "localhost" - "$Tags.PEER_HOST_IPV4" "127.0.0.1" - "$Tags.PEER_PORT" server.port - "status.code" "UNKNOWN" - "grpc.status.code" "UNKNOWN" - "$InstrumentationTags.GRPC_STATUS_CODE" 2 - "request.type" "example.Helloworld\$Request" - "response.type" "example.Helloworld\$Response" - "status.description" { it == null || String} - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - peerServiceFrom(Tags.RPC_SERVICE) - defaultTags() - } - } - } - trace(2) { - span { - operationName serverOperation() - resourceName "example.Greeter/SayHello" - spanType DDSpanTypes.RPC - childOf trace(0).get(0) - errored errorFlag - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "status.code" "${status.code.name()}" - "grpc.status.code" "${status.code.name()}" - "$InstrumentationTags.GRPC_STATUS_CODE" status.code.value() - "status.description" { it == null || String} - errorTags error.class, error.message - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - defaultTags(true) - } - } - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "message.type" "example.Helloworld\$Request" - defaultTags() - } - } - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - - where: - name | status | errorFlag - "Runtime - cause" | Status.UNKNOWN.withCause(new RuntimeException("some error")) | true - "Status - cause" | Status.PERMISSION_DENIED.withCause(new RuntimeException("some error")) | true - "StatusRuntime - cause" | Status.UNIMPLEMENTED.withCause(new RuntimeException("some error")) | true - "Runtime - description" | Status.UNKNOWN.withDescription("some description") | true - "Status - description" | Status.PERMISSION_DENIED.withDescription("some description") | true - "StatusRuntime - description" | Status.UNIMPLEMENTED.withDescription("some description") | true - "StatusRuntime - Not errored no cause" | Status.fromCodeValue(15).withDescription("some description") | false - "StatusRuntime - Not errored with cause" | Status.fromCodeValue(15).withCause(new RuntimeException("some error")) | false - } - - def "skip binary headers"() { - setup: - def meta = new Metadata() - meta.put(Metadata.Key. of("test", Metadata.ASCII_STRING_MARSHALLER), "val") - meta.put(Metadata.Key. of("test-bin", Metadata.BINARY_BYTE_MARSHALLER), "bin-val".bytes) - - when: - def keys = new ArrayList() - GrpcExtractAdapter.GETTER.forEachKey(meta, new AgentPropagation.KeyClassifier() { - - @Override - boolean accept(String key, String value) { - keys.add(key.toLowerCase()) - return true - } - }) - - then: - keys == ["test"] - } - - def "test ignore ignored methods"() { - setup: - - ExecutorService responseExecutor = Executors.newSingleThreadExecutor() - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - void ignore( - final Helloworld.Request req, final StreamObserver responseObserver) { - final Helloworld.Response reply = Helloworld.Response.newBuilder().setMessage("Hello $req.name").build() - responseExecutor.execute { - responseObserver.onNext(reply) - responseObserver.onCompleted() - } - } - } - Server server = InProcessServerBuilder.forName(getClass().name).addService(greeter).directExecutor().build().start() - ManagedChannel channel = InProcessChannelBuilder.forName(getClass().name).build() - GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel) - - when: - def response = runUnderTrace("parent") { - def resp = client.ignore(Helloworld.Request.newBuilder().setName("whatever").build()) - return resp - } - - then: - response.message == "Hello whatever" - assertTraces(2) { - trace(1) { - basicSpan(it, "parent") - } - trace(2) { - span { - operationName serverOperation() - resourceName "example.Greeter/Ignore" - spanType DDSpanTypes.RPC - parentSpanId DDSpanId.ZERO - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "status.code" "OK" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - defaultTags(true) - } - } - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-server" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER - "message.type" "example.Helloworld\$Request" - defaultTags() - } - } - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - } - - def "test ignore ignored inbound methods"() { - setup: - - ExecutorService responseExecutor = Executors.newSingleThreadExecutor() - BindableService greeter = new GreeterGrpc.GreeterImplBase() { - @Override - void ignoreInbound( - final Helloworld.Request req, final StreamObserver responseObserver) { - final Helloworld.Response reply = Helloworld.Response.newBuilder().setMessage("Hello $req.name").build() - responseExecutor.execute { - responseObserver.onNext(reply) - responseObserver.onCompleted() - } - } - } - Server server = InProcessServerBuilder.forName(getClass().name).addService(greeter).directExecutor().build().start() - ManagedChannel channel = InProcessChannelBuilder.forName(getClass().name).build() - GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel) - - when: - def response = client.ignoreInbound(Helloworld.Request.newBuilder().setName("whatever").build()) - - then: - response.message == "Hello whatever" - assertTraces(1) { - trace(hasClientMessageSpans() ? 2 : 1) { - span { - operationName clientOperation() - resourceName "example.Greeter/IgnoreInbound" - spanType DDSpanTypes.RPC - parent() - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "$Tags.RPC_SERVICE" "example.Greeter" - "status.code" "OK" - "grpc.status.code" "OK" - "$InstrumentationTags.GRPC_STATUS_CODE" 0 - "request.type" "example.Helloworld\$Request" - "response.type" "example.Helloworld\$Response" - if ({ isDataStreamsEnabled() }) { - "$DDTags.PATHWAY_HASH" { String } - } - peerServiceFrom(Tags.RPC_SERVICE) - defaultTags() - } - } - if (hasClientMessageSpans()) { - span { - operationName "grpc.message" - resourceName "grpc.message" - spanType DDSpanTypes.RPC - childOf span(0) - errored false - measured true - tags { - "$Tags.COMPONENT" "grpc-client" - "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT - "message.type" "example.Helloworld\$Response" - defaultTagsNoPeerService() - } - } - } - } - } - - cleanup: - channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) - server?.shutdownNow()?.awaitTermination() - } - - def newWorkStealingPool() { - // Executors.newWorkStealingPool() not available in JDK7 - return new ForkJoinPool - (Runtime.getRuntime().availableProcessors(), - ForkJoinPool.defaultForkJoinWorkerThreadFactory, - null, true) - } -} - -abstract class GrpcDataStreamsEnabledForkedTest extends GrpcTest { - @Override - protected boolean isDataStreamsEnabled() { - return true - } -} - -class GrpcDataStreamsEnabledV0Test extends GrpcDataStreamsEnabledForkedTest { - - @Override - int version() { - return 0 - } - - @Override - protected String clientOperation() { - return "grpc.client" - } - - @Override - protected String serverOperation() { - return "grpc.server" - } -} - -class GrpcDataStreamsEnabledV1ForkedTest extends GrpcDataStreamsEnabledForkedTest { - - @Override - int version() { - return 1 - } - - @Override - protected String clientOperation() { - return "grpc.client.request" - } - - @Override - protected String serverOperation() { - return "grpc.server.request" - } -} - -class GrpcDataStreamsDisabledForkedTest extends GrpcTest { - @Override - protected boolean isDataStreamsEnabled() { - return false - } - - @Override - int version() { - return 0 - } - - @Override - protected String clientOperation() { - return "grpc.client" - } - - @Override - protected String serverOperation() { - return "grpc.server" - } -} - -class GrpcClientMessagesEnabledTest extends GrpcDataStreamsEnabledV0Test { - @Override - protected boolean hasClientMessageSpans() { - true - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcStreamingTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcStreamingTest.groovy deleted file mode 100644 index cbb5d858c99..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcStreamingTest.groovy +++ /dev/null @@ -1,22 +0,0 @@ -class HierarchyMatcherGrpcStreamingTest extends GrpcStreamingTest { - @Override - protected void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("dd.integration.grpc.matching.shortcut.enabled", "false") - } - - @Override - int version() { - return 0 - } - - @Override - String clientOperation() { - return "grpc.client" - } - - @Override - String serverOperation() { - return "grpc.server" - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcTest.groovy deleted file mode 100644 index efe773cd840..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/HierarchyMatcherGrpcTest.groovy +++ /dev/null @@ -1,22 +0,0 @@ - -class HierarchyMatcherGrpcTest extends GrpcTest { - @Override - protected void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("dd.integration.grpc.matching.shortcut.enabled", "false") - } - @Override - int version() { - return 0 - } - - @Override - String clientOperation() { - return "grpc.client" - } - - @Override - String serverOperation() { - return "grpc.server" - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/UnwrapGrpcContextForkedTest.groovy b/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/UnwrapGrpcContextForkedTest.groovy deleted file mode 100644 index e688155f27c..00000000000 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/groovy/UnwrapGrpcContextForkedTest.groovy +++ /dev/null @@ -1,26 +0,0 @@ -import datadog.trace.agent.test.InstrumentationSpecification -import datadog.trace.bootstrap.instrumentation.api.TaskWrapper -import io.grpc.Context - -class UnwrapGrpcContextForkedTest extends InstrumentationSpecification { - @Override - protected void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("dd.profiling.enabled", "true") - } - - def "test unwrap gRPC context wrappers"() { - // relies on TaskUnwrappingInstrumentation (java-concurrent) targeting grpc context - setup: - def runnable = {} - def callable = {"result"} - when: - def unwrappedRunnableType = TaskWrapper.getUnwrappedType(Context.ROOT.wrap(runnable)) - then: - unwrappedRunnableType == runnable.getClass() - when: - def unwrappedCallableType = TaskWrapper.getUnwrappedType(Context.ROOT.wrap(callable)) - then: - unwrappedCallableType == callable.getClass() - } -} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcErrorTest.java b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcErrorTest.java new file mode 100644 index 00000000000..9a9ef5f6bdf --- /dev/null +++ b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcErrorTest.java @@ -0,0 +1,342 @@ +package datadog.trace.instrumentation.grpc; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.core.DDSpan; +import example.GreeterGrpc; +import example.HelloworldProto.Request; +import example.HelloworldProto.Response; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +/** + * Tests gRPC instrumentation error handling. Verifies that error tags (error.type, error.message, + * error.stack) and status codes are correctly set on spans when RPCs fail with various gRPC status + * codes and server-side exceptions. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class GrpcErrorTest extends AbstractInstrumentationTest { + + private static final int PORT = 18883; + + private Server grpcServer; + private ManagedChannel grpcChannel; + private GreeterGrpc.GreeterBlockingStub blockingStub; + private GreeterGrpc.GreeterStub asyncStub; + + @BeforeAll + void setupGrpc() throws IOException { + grpcServer = + ServerBuilder.forPort(PORT) + .addService(new ErrorGreeterImpl()) + .build() + .start(); + + grpcChannel = + ManagedChannelBuilder.forAddress("localhost", PORT).usePlaintext().build(); + blockingStub = GreeterGrpc.newBlockingStub(grpcChannel); + asyncStub = GreeterGrpc.newStub(grpcChannel); + } + + @AfterAll + void tearDownGrpc() throws InterruptedException { + if (grpcChannel != null) { + grpcChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + if (grpcServer != null) { + grpcServer.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + } + + @Test + void unaryErrorSetsErrorTagsOnSpans() throws InterruptedException, TimeoutException { + StatusRuntimeException thrown = + assertThrows( + StatusRuntimeException.class, + () -> + blockingStub.sayHello( + Request.newBuilder().setName("UNIMPLEMENTED").build())); + + assertEquals( + Status.UNIMPLEMENTED.getCode(), + thrown.getStatus().getCode(), + "Exception should carry UNIMPLEMENTED status"); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span on error path"); + assertEquals( + "example.Greeter/SayHello", + clientSpan.getResourceName().toString(), + "Resource name should still reflect the called method"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("client", String.valueOf(clientSpan.getTag("span.kind"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals("grpc", String.valueOf(clientSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(clientSpan.getTag("rpc.service"))); + assertTrue(clientSpan.isError(), "Client span should be marked as errored"); + assertEquals( + String.valueOf(Status.UNIMPLEMENTED.getCode().value()), + String.valueOf(clientSpan.getTag("status.code")), + "Client status code should be UNIMPLEMENTED"); + assertNotNull( + clientSpan.getTag("error.message"), + "Client span should have error.message tag"); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span on error path"); + assertEquals( + "example.Greeter/SayHello", + serverSpan.getResourceName().toString(), + "Server resource name should match the called method"); + assertEquals("grpc", String.valueOf(serverSpan.getTag("component"))); + assertEquals("server", String.valueOf(serverSpan.getTag("span.kind"))); + assertEquals("rpc", serverSpan.getSpanType()); + assertEquals("grpc", String.valueOf(serverSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(serverSpan.getTag("rpc.service"))); + assertEquals( + String.valueOf(Status.UNIMPLEMENTED.getCode().value()), + String.valueOf(serverSpan.getTag("status.code")), + "Server status code should be UNIMPLEMENTED"); + } + + @Test + void serverExceptionSetsErrorTagsWithDetails() throws InterruptedException, TimeoutException { + StatusRuntimeException thrown = + assertThrows( + StatusRuntimeException.class, + () -> + blockingStub.sayHello( + Request.newBuilder().setName("INTERNAL_ERROR").build())); + + assertEquals( + Status.INTERNAL.getCode(), + thrown.getStatus().getCode(), + "Exception should carry INTERNAL status"); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for internal error"); + assertTrue(clientSpan.isError(), "Client span should be errored on INTERNAL status"); + assertEquals( + String.valueOf(Status.INTERNAL.getCode().value()), + String.valueOf(clientSpan.getTag("status.code")), + "Client status code should be INTERNAL"); + assertNotNull( + clientSpan.getTag("error.message"), + "Client span should have error.message for INTERNAL error"); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for internal error"); + assertEquals( + "example.Greeter/SayHello", + serverSpan.getResourceName().toString()); + assertEquals( + String.valueOf(Status.INTERNAL.getCode().value()), + String.valueOf(serverSpan.getTag("status.code")), + "Server status code should be INTERNAL"); + assertNotNull( + serverSpan.getTag("error.message"), + "Server span should have error.message for INTERNAL error"); + } + + @Test + void notFoundStatusSetsCorrectStatusCode() throws InterruptedException, TimeoutException { + StatusRuntimeException thrown = + assertThrows( + StatusRuntimeException.class, + () -> + blockingStub.sayHello( + Request.newBuilder().setName("NOT_FOUND").build())); + + assertEquals( + Status.NOT_FOUND.getCode(), + thrown.getStatus().getCode(), + "Exception should carry NOT_FOUND status"); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for NOT_FOUND"); + assertEquals( + "example.Greeter/SayHello", + clientSpan.getResourceName().toString()); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals( + String.valueOf(Status.NOT_FOUND.getCode().value()), + String.valueOf(clientSpan.getTag("status.code")), + "Client status code should be NOT_FOUND"); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for NOT_FOUND"); + assertEquals( + String.valueOf(Status.NOT_FOUND.getCode().value()), + String.valueOf(serverSpan.getTag("status.code")), + "Server status code should be NOT_FOUND"); + } + + @Test + void streamingErrorSetsErrorTagsOnSpans() throws InterruptedException, TimeoutException { + CountDownLatch latch = new CountDownLatch(1); + CopyOnWriteArrayList errors = new CopyOnWriteArrayList<>(); + + StreamObserver requestObserver = + asyncStub.clientStreamHello( + new StreamObserver() { + @Override + public void onNext(Response value) {} + + @Override + public void onError(Throwable t) { + errors.add(t); + latch.countDown(); + } + + @Override + public void onCompleted() { + latch.countDown(); + } + }); + + requestObserver.onNext(Request.newBuilder().setName("STREAM_ERROR").build()); + requestObserver.onCompleted(); + + assertTrue(latch.await(10, TimeUnit.SECONDS), "Error callback should arrive within timeout"); + assertEquals(1, errors.size(), "Expected one error from streaming call"); + assertTrue( + errors.get(0) instanceof StatusRuntimeException, + "Error should be StatusRuntimeException"); + assertEquals( + Status.ABORTED.getCode(), + ((StatusRuntimeException) errors.get(0)).getStatus().getCode(), + "Error status should be ABORTED"); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for streaming error"); + assertEquals( + "example.Greeter/ClientStreamHello", + clientSpan.getResourceName().toString(), + "Resource should be the streaming method path"); + assertTrue(clientSpan.isError(), "Client span should be errored on streaming failure"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("rpc", clientSpan.getSpanType()); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for streaming error"); + assertEquals( + "example.Greeter/ClientStreamHello", + serverSpan.getResourceName().toString()); + assertEquals( + String.valueOf(Status.ABORTED.getCode().value()), + String.valueOf(serverSpan.getTag("status.code")), + "Server status code should be ABORTED"); + } + + private List flattenTraces() { + List result = new ArrayList<>(); + for (List trace : writer) { + result.addAll(trace); + } + return result; + } + + private DDSpan findSpan(List spans, String operationName, String spanKind) { + for (DDSpan span : spans) { + if (span.getOperationName().toString().equals(operationName) + && spanKind.equals(String.valueOf(span.getTag("span.kind")))) { + return span; + } + } + return null; + } + + static class ErrorGreeterImpl extends GreeterGrpc.GreeterImplBase { + @Override + public void sayHello(Request request, StreamObserver responseObserver) { + String name = request.getName(); + switch (name) { + case "UNIMPLEMENTED": + responseObserver.onError( + Status.UNIMPLEMENTED + .withDescription("Method not implemented") + .asRuntimeException()); + break; + case "INTERNAL_ERROR": + responseObserver.onError( + Status.INTERNAL + .withDescription("Internal server error") + .asRuntimeException()); + break; + case "NOT_FOUND": + responseObserver.onError( + Status.NOT_FOUND + .withDescription("Resource not found") + .asRuntimeException()); + break; + default: + responseObserver.onNext( + Response.newBuilder().setMessage("Hello " + name).build()); + responseObserver.onCompleted(); + break; + } + } + + @Override + public StreamObserver clientStreamHello(StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onNext(Request value) { + if ("STREAM_ERROR".equals(value.getName())) { + responseObserver.onError( + Status.ABORTED + .withDescription("Stream aborted by server") + .asRuntimeException()); + } + } + + @Override + public void onError(Throwable t) { + responseObserver.onError(t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + } +} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcStreamingTest.java b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcStreamingTest.java new file mode 100644 index 00000000000..d27c19b14fb --- /dev/null +++ b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcStreamingTest.java @@ -0,0 +1,364 @@ +package datadog.trace.instrumentation.grpc; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.core.DDSpan; +import example.GreeterGrpc; +import example.HelloworldProto.Request; +import example.HelloworldProto.Response; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +/** + * Tests gRPC instrumentation for streaming RPC patterns: server streaming, client streaming, and + * bidirectional streaming. Verifies span creation, tags, and parent-child relationships across + * streaming calls. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class GrpcStreamingTest extends AbstractInstrumentationTest { + + private static final int PORT = 18882; + + private Server grpcServer; + private ManagedChannel grpcChannel; + private GreeterGrpc.GreeterBlockingStub blockingStub; + private GreeterGrpc.GreeterStub asyncStub; + + @BeforeAll + void setupGrpc() throws IOException { + grpcServer = + ServerBuilder.forPort(PORT) + .addService(new StreamingGreeterImpl()) + .build() + .start(); + + grpcChannel = + ManagedChannelBuilder.forAddress("localhost", PORT).usePlaintext().build(); + blockingStub = GreeterGrpc.newBlockingStub(grpcChannel); + asyncStub = GreeterGrpc.newStub(grpcChannel); + } + + @AfterAll + void tearDownGrpc() throws InterruptedException { + if (grpcChannel != null) { + grpcChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + if (grpcServer != null) { + grpcServer.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + } + + @Test + void serverStreamingCreatesClientAndServerSpans() + throws InterruptedException, TimeoutException { + AgentSpan parentSpan = startSpan("test", "parent"); + AgentScope parentScope = activateSpan(parentSpan); + List responses = new ArrayList<>(); + try { + Iterator iterator = + blockingStub.serverStreamHello(Request.newBuilder().setName("stream").build()); + while (iterator.hasNext()) { + responses.add(iterator.next()); + } + } finally { + parentScope.close(); + parentSpan.finish(); + } + + assertEquals(3, responses.size(), "Expected 3 streamed responses"); + assertEquals("Hello stream 0", responses.get(0).getMessage()); + assertEquals("Hello stream 1", responses.get(1).getMessage()); + assertEquals("Hello stream 2", responses.get(2).getMessage()); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for server streaming"); + assertEquals( + "example.Greeter/ServerStreamHello", + clientSpan.getResourceName().toString(), + "Resource name should be the server streaming method path"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("client", String.valueOf(clientSpan.getTag("span.kind"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals("grpc", String.valueOf(clientSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(clientSpan.getTag("rpc.service"))); + assertFalse(clientSpan.isError()); + assertEquals( + parentSpan.getSpanId(), + clientSpan.getParentId(), + "Client span should be child of parent"); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for server streaming"); + assertEquals( + "example.Greeter/ServerStreamHello", + serverSpan.getResourceName().toString(), + "Server resource name should match the streaming method path"); + assertEquals("grpc", String.valueOf(serverSpan.getTag("component"))); + assertEquals("server", String.valueOf(serverSpan.getTag("span.kind"))); + assertEquals("rpc", serverSpan.getSpanType()); + assertEquals("grpc", String.valueOf(serverSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(serverSpan.getTag("rpc.service"))); + assertFalse(serverSpan.isError()); + assertEquals( + clientSpan.getSpanId(), + serverSpan.getParentId(), + "Server span should be child of client span"); + } + + @Test + void clientStreamingCreatesClientAndServerSpans() + throws InterruptedException, TimeoutException { + AgentSpan parentSpan = startSpan("test", "parent"); + AgentScope parentScope = activateSpan(parentSpan); + + CountDownLatch latch = new CountDownLatch(1); + CopyOnWriteArrayList responses = new CopyOnWriteArrayList<>(); + + StreamObserver requestObserver; + try { + requestObserver = + asyncStub.clientStreamHello( + new StreamObserver() { + @Override + public void onNext(Response value) { + responses.add(value); + } + + @Override + public void onError(Throwable t) { + latch.countDown(); + } + + @Override + public void onCompleted() { + latch.countDown(); + } + }); + + requestObserver.onNext(Request.newBuilder().setName("msg1").build()); + requestObserver.onNext(Request.newBuilder().setName("msg2").build()); + requestObserver.onNext(Request.newBuilder().setName("msg3").build()); + requestObserver.onCompleted(); + } finally { + parentScope.close(); + parentSpan.finish(); + } + + assertTrue(latch.await(10, TimeUnit.SECONDS), "Response should arrive within timeout"); + assertEquals(1, responses.size(), "Client streaming should produce a single response"); + assertEquals( + "Hello msg1, msg2, msg3", + responses.get(0).getMessage(), + "Response should aggregate all client messages"); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for client streaming"); + assertEquals( + "example.Greeter/ClientStreamHello", + clientSpan.getResourceName().toString(), + "Resource name should be the client streaming method path"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals("grpc", String.valueOf(clientSpan.getTag("rpc.system"))); + assertFalse(clientSpan.isError()); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for client streaming"); + assertEquals( + "example.Greeter/ClientStreamHello", + serverSpan.getResourceName().toString(), + "Server resource name should match the client streaming method path"); + assertEquals("grpc", String.valueOf(serverSpan.getTag("component"))); + assertEquals("rpc", serverSpan.getSpanType()); + assertEquals("grpc", String.valueOf(serverSpan.getTag("rpc.system"))); + assertFalse(serverSpan.isError()); + assertEquals( + clientSpan.getSpanId(), + serverSpan.getParentId(), + "Server span should be child of client span"); + } + + @Test + void bidiStreamingCreatesClientAndServerSpans() + throws InterruptedException, TimeoutException { + AgentSpan parentSpan = startSpan("test", "parent"); + AgentScope parentScope = activateSpan(parentSpan); + + CountDownLatch latch = new CountDownLatch(1); + CopyOnWriteArrayList responses = new CopyOnWriteArrayList<>(); + + StreamObserver requestObserver; + try { + requestObserver = + asyncStub.bidiStreamHello( + new StreamObserver() { + @Override + public void onNext(Response value) { + responses.add(value); + } + + @Override + public void onError(Throwable t) { + latch.countDown(); + } + + @Override + public void onCompleted() { + latch.countDown(); + } + }); + + requestObserver.onNext(Request.newBuilder().setName("bidi1").build()); + requestObserver.onNext(Request.newBuilder().setName("bidi2").build()); + requestObserver.onCompleted(); + } finally { + parentScope.close(); + parentSpan.finish(); + } + + assertTrue(latch.await(10, TimeUnit.SECONDS), "Response should arrive within timeout"); + assertEquals(2, responses.size(), "Bidi streaming should echo each request as a response"); + assertEquals("Hello bidi1", responses.get(0).getMessage()); + assertEquals("Hello bidi2", responses.get(1).getMessage()); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span for bidi streaming"); + assertEquals( + "example.Greeter/BidiStreamHello", + clientSpan.getResourceName().toString(), + "Resource name should be the bidi streaming method path"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("client", String.valueOf(clientSpan.getTag("span.kind"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals("grpc", String.valueOf(clientSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(clientSpan.getTag("rpc.service"))); + assertFalse(clientSpan.isError()); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span for bidi streaming"); + assertEquals( + "example.Greeter/BidiStreamHello", + serverSpan.getResourceName().toString(), + "Server resource name should match the bidi streaming method path"); + assertEquals("grpc", String.valueOf(serverSpan.getTag("component"))); + assertEquals("server", String.valueOf(serverSpan.getTag("span.kind"))); + assertEquals("rpc", serverSpan.getSpanType()); + assertEquals("grpc", String.valueOf(serverSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(serverSpan.getTag("rpc.service"))); + assertFalse(serverSpan.isError()); + assertEquals( + clientSpan.getSpanId(), + serverSpan.getParentId(), + "Server span should be child of client span"); + } + + private List flattenTraces() { + List result = new ArrayList<>(); + for (List trace : writer) { + result.addAll(trace); + } + return result; + } + + private DDSpan findSpan(List spans, String operationName, String spanKind) { + for (DDSpan span : spans) { + if (span.getOperationName().toString().equals(operationName) + && spanKind.equals(String.valueOf(span.getTag("span.kind")))) { + return span; + } + } + return null; + } + + static class StreamingGreeterImpl extends GreeterGrpc.GreeterImplBase { + @Override + public void serverStreamHello(Request request, StreamObserver responseObserver) { + for (int i = 0; i < 3; i++) { + responseObserver.onNext( + Response.newBuilder() + .setMessage("Hello " + request.getName() + " " + i) + .build()); + } + responseObserver.onCompleted(); + } + + @Override + public StreamObserver clientStreamHello(StreamObserver responseObserver) { + List names = new ArrayList<>(); + return new StreamObserver() { + @Override + public void onNext(Request value) { + names.add(value.getName()); + } + + @Override + public void onError(Throwable t) { + responseObserver.onError(t); + } + + @Override + public void onCompleted() { + responseObserver.onNext( + Response.newBuilder() + .setMessage("Hello " + String.join(", ", names)) + .build()); + responseObserver.onCompleted(); + } + }; + } + + @Override + public StreamObserver bidiStreamHello(StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onNext(Request value) { + responseObserver.onNext( + Response.newBuilder().setMessage("Hello " + value.getName()).build()); + } + + @Override + public void onError(Throwable t) { + responseObserver.onError(t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + } +} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcTest.java b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcTest.java new file mode 100644 index 00000000000..337c5ccce10 --- /dev/null +++ b/dd-java-agent/instrumentation/grpc-1.5/src/test/java/datadog/trace/instrumentation/grpc/GrpcTest.java @@ -0,0 +1,219 @@ +package datadog.trace.instrumentation.grpc; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.core.DDSpan; +import example.GreeterGrpc; +import example.HelloworldProto.Request; +import example.HelloworldProto.Response; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +/** + * Tests gRPC client and server instrumentation for unary RPC calls. Verifies that spans are created + * with correct operation names, resource names, tags, and parent-child relationships for both client + * and server sides. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class GrpcTest extends AbstractInstrumentationTest { + + private static final int PORT = 18881; + + private Server grpcServer; + private ManagedChannel grpcChannel; + private GreeterGrpc.GreeterBlockingStub blockingStub; + + @BeforeAll + void setupGrpc() throws IOException { + grpcServer = + ServerBuilder.forPort(PORT) + .addService(new GreeterImpl()) + .build() + .start(); + + grpcChannel = + ManagedChannelBuilder.forAddress("localhost", PORT).usePlaintext().build(); + blockingStub = GreeterGrpc.newBlockingStub(grpcChannel); + } + + @AfterAll + void tearDownGrpc() throws InterruptedException { + if (grpcChannel != null) { + grpcChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + if (grpcServer != null) { + grpcServer.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + } + + @Test + void unaryCallCreatesClientAndServerSpans() throws InterruptedException, TimeoutException { + AgentSpan parentSpan = startSpan("test", "parent"); + AgentScope parentScope = activateSpan(parentSpan); + Response response; + try { + response = blockingStub.sayHello(Request.newBuilder().setName("World").build()); + } finally { + parentScope.close(); + parentSpan.finish(); + } + + assertEquals("Hello World", response.getMessage()); + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + assertNotNull(clientSpan, "Expected grpc.client span"); + assertEquals( + "example.Greeter/SayHello", + clientSpan.getResourceName().toString(), + "Resource name should be the full gRPC method path"); + assertEquals("grpc", String.valueOf(clientSpan.getTag("component"))); + assertEquals("client", String.valueOf(clientSpan.getTag("span.kind"))); + assertEquals("rpc", clientSpan.getSpanType()); + assertEquals( + "example.Greeter/SayHello", String.valueOf(clientSpan.getTag("rpc.method"))); + assertEquals("grpc", String.valueOf(clientSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(clientSpan.getTag("rpc.service"))); + assertEquals( + parentSpan.getSpanId(), + clientSpan.getParentId(), + "Client span should be child of parent"); + assertFalse(clientSpan.isError(), "Client span should not be errored on success"); + assertEquals( + String.valueOf(Status.OK.getCode().value()), + String.valueOf(clientSpan.getTag("status.code")), + "Status code should be OK (0)"); + + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + assertNotNull(serverSpan, "Expected grpc.server span"); + assertEquals( + "example.Greeter/SayHello", + serverSpan.getResourceName().toString(), + "Server resource name should match the gRPC method path"); + assertEquals("grpc", String.valueOf(serverSpan.getTag("component"))); + assertEquals("server", String.valueOf(serverSpan.getTag("span.kind"))); + assertEquals("rpc", serverSpan.getSpanType()); + assertEquals( + "example.Greeter/SayHello", String.valueOf(serverSpan.getTag("rpc.method"))); + assertEquals("grpc", String.valueOf(serverSpan.getTag("rpc.system"))); + assertEquals("example.Greeter", String.valueOf(serverSpan.getTag("rpc.service"))); + assertEquals( + clientSpan.getSpanId(), + serverSpan.getParentId(), + "Server span should be child of client span (distributed trace)"); + assertFalse(serverSpan.isError(), "Server span should not be errored on success"); + assertEquals( + String.valueOf(Status.OK.getCode().value()), + String.valueOf(serverSpan.getTag("status.code")), + "Server status code should be OK (0)"); + } + + @Test + void traceContextPropagatesFromClientToServer() throws InterruptedException, TimeoutException { + AgentSpan parentSpan = startSpan("test", "parent"); + AgentScope parentScope = activateSpan(parentSpan); + try { + blockingStub.sayHello(Request.newBuilder().setName("propagation-test").build()); + } finally { + parentScope.close(); + parentSpan.finish(); + } + + writer.waitForTraces(2); + List allSpans = flattenTraces(); + + DDSpan clientSpan = findSpan(allSpans, "grpc.client", "client"); + DDSpan serverSpan = findSpan(allSpans, "grpc.server", "server"); + + assertNotNull(clientSpan, "Expected grpc.client span"); + assertNotNull(serverSpan, "Expected grpc.server span"); + + assertEquals( + parentSpan.getTraceId(), + clientSpan.getTraceId(), + "Client span should share trace ID with parent"); + assertEquals( + clientSpan.getTraceId(), + serverSpan.getTraceId(), + "Server span should share trace ID with client (distributed trace)"); + assertEquals( + clientSpan.getSpanId(), + serverSpan.getParentId(), + "Server span parent should be the client span"); + } + + @Test + void multipleUnaryCallsProduceSeparateTraces() throws InterruptedException, TimeoutException { + blockingStub.sayHello(Request.newBuilder().setName("first").build()); + blockingStub.sayHello(Request.newBuilder().setName("second").build()); + + writer.waitForTraces(4); + + int clientSpanCount = 0; + int serverSpanCount = 0; + for (List trace : writer) { + for (DDSpan span : trace) { + if ("grpc.client".equals(span.getOperationName().toString())) { + clientSpanCount++; + } + if ("grpc.server".equals(span.getOperationName().toString())) { + serverSpanCount++; + } + } + } + + assertEquals(2, clientSpanCount, "Expected 2 client spans for 2 RPC calls"); + assertEquals(2, serverSpanCount, "Expected 2 server spans for 2 RPC calls"); + } + + private List flattenTraces() { + List result = new ArrayList<>(); + for (List trace : writer) { + result.addAll(trace); + } + return result; + } + + private DDSpan findSpan(List spans, String operationName, String spanKind) { + for (DDSpan span : spans) { + if (span.getOperationName().toString().equals(operationName) + && spanKind.equals(String.valueOf(span.getTag("span.kind")))) { + return span; + } + } + return null; + } + + static class GreeterImpl extends GreeterGrpc.GreeterImplBase { + @Override + public void sayHello(Request request, StreamObserver responseObserver) { + responseObserver.onNext( + Response.newBuilder().setMessage("Hello " + request.getName()).build()); + responseObserver.onCompleted(); + } + } +} diff --git a/dd-java-agent/instrumentation/grpc-1.5/src/test/proto/helloworld.proto b/dd-java-agent/instrumentation/grpc-1.5/src/test/proto/helloworld.proto index 26d9919e53e..0ed794969d1 100644 --- a/dd-java-agent/instrumentation/grpc-1.5/src/test/proto/helloworld.proto +++ b/dd-java-agent/instrumentation/grpc-1.5/src/test/proto/helloworld.proto @@ -2,18 +2,14 @@ syntax = "proto3"; package example; -service Greeter { - rpc SayHello (Request) returns (Response) { - } - - rpc Conversation (stream Response) returns (stream Response) { - } +option java_package = "example"; +option java_outer_classname = "HelloworldProto"; - rpc Ignore (Request) returns (Response) { - } - - rpc IgnoreInbound (Request) returns (Response) { - } +service Greeter { + rpc SayHello (Request) returns (Response) {} + rpc ServerStreamHello (Request) returns (stream Response) {} + rpc ClientStreamHello (stream Request) returns (Response) {} + rpc BidiStreamHello (stream Request) returns (stream Response) {} } message Request {