diff --git a/Cargo.lock b/Cargo.lock index 2ab8bec9268..58f3f331f52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -594,7 +594,7 @@ checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" dependencies = [ "clap", "heck 0.4.1", - "indexmap 2.12.0", + "indexmap 2.12.1", "log", "proc-macro2", "quote", @@ -613,7 +613,7 @@ checksum = "975982cdb7ad6a142be15bdf84aea7ec6a9e5d4d797c004d43185b24cfe4e684" dependencies = [ "clap", "heck 0.5.0", - "indexmap 2.12.0", + "indexmap 2.12.1", "log", "proc-macro2", "quote", @@ -1307,6 +1307,7 @@ dependencies = [ "libdd-trace-utils", "paste", "rmp-serde", + "serde_json", "tempfile", "tracing", ] @@ -1776,7 +1777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe" dependencies = [ "fallible-iterator", - "indexmap 2.12.0", + "indexmap 2.12.1", "stable_deref_trait", ] @@ -1818,7 +1819,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.12.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -1874,9 +1875,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hdrhistogram" @@ -2308,12 +2309,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -2465,7 +2466,7 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "indexmap 2.12.0", + "indexmap 2.12.1", "libc 0.2.177", "maplit", "nix", @@ -2695,7 +2696,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-multipart-rfc7578", - "indexmap 2.12.0", + "indexmap 2.12.1", "libdd-alloc", "libdd-common 1.0.0 (git+https://github.com/DataDog/libdatadog?tag=v24.0.2)", "libdd-profiling-protobuf", @@ -2733,6 +2734,7 @@ dependencies = [ "hyper-util", "libc 0.2.177", "libdd-common 1.0.0", + "libdd-common-ffi", "libdd-ddsketch", "serde", "serde_json", @@ -2824,7 +2826,7 @@ dependencies = [ "http-body-util", "httpmock", "hyper", - "indexmap 2.12.0", + "indexmap 2.12.1", "libdd-common 1.0.0", "libdd-tinybytes", "libdd-trace-normalization", @@ -2866,6 +2868,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.7.4" @@ -2939,7 +2947,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix", + "rustix 0.38.43", ] [[package]] @@ -3337,7 +3345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.12.0", + "indexmap 2.12.1", ] [[package]] @@ -3527,7 +3535,7 @@ checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d" dependencies = [ "autocfg", "equivalent", - "indexmap 2.12.0", + "indexmap 2.12.1", ] [[package]] @@ -4033,7 +4041,20 @@ dependencies = [ "bitflags 2.8.0", "errno", "libc 0.2.177", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc 0.2.177", + "linux-raw-sys 0.11.0", "windows-sys 0.59.0", ] @@ -4306,7 +4327,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", "serde_derive", "serde_json", @@ -4332,7 +4353,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -4649,15 +4670,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.2.15", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.1.3", "windows-sys 0.59.0", ] @@ -4925,7 +4945,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime", "winnow 0.5.40", ] @@ -4936,7 +4956,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", "serde_spanned", "toml_datetime", @@ -5056,9 +5076,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -5393,7 +5413,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.43", ] [[package]] diff --git a/Makefile b/Makefile index 52376a98821..50aabe0dfb1 100644 --- a/Makefile +++ b/Makefile @@ -1210,8 +1210,8 @@ define run_tests_debug (set -o pipefail; { $(call run_tests,$(1)) 2>&1 >&3 | \ tee >(grep --line-buffered -vE '\[ddtrace\] \[debug\]|\[ddtrace\] \[info\]' >&2) | \ { ! (grep --line-buffered -E '\[error\]|\[warning\]|\[deprecated\]' >/dev/null && \ - echo $$'\033[41m'"ERROR: Found debug log errors in the output."$$'\033[0m'); }; } 3>&1); \ - timeout 10 bash -c 'wait' 2>/dev/null || true + echo $$'\033[41m'"ERROR: Found debug log errors in the output."$$'\033[0m'); }; } 3>&1 \ + ) && ([ -n "${DD_TRACE_DOCKER_DEBUG}" ] || timeout 10 bash -c 'wait' 2>/dev/null || true) \ $(eval TEST_EXTRA_ENV=) endef diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index a3c93dbb140..aafb5549686 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -324,6 +324,7 @@ def buildTracerTask = { String version, String variant -> } def buildAppSecTask = { String version, String variant -> + def buildType = variant.contains('debug') ? 'Debug' : 'RelWithDebInfo' buildRunInDockerTask( baseName: 'buildAppsec', baseTag: 'php', @@ -346,17 +347,17 @@ def buildAppSecTask = { String version, String variant -> ], command: [ '-e', '-c', - ''' + """ git config --global --add safe.directory '*' cd /appsec test -f CMakeCache.txt || \\ - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \\ + cmake -DCMAKE_BUILD_TYPE=$buildType \\ -DCMAKE_INSTALL_PREFIX=/appsec \\ -DDD_APPSEC_ENABLE_PATCHELF_LIBC=ON \\ -DDD_APPSEC_TESTING=ON /project/appsec make -j extension ddappsec-helper && \\ touch ddappsec.so libddappsec-helper.so - ''' + """ ] ) } diff --git a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy index 24208464b74..85f768bab08 100644 --- a/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy +++ b/appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/TelemetryHelpers.groovy @@ -1,6 +1,10 @@ package com.datadog.appsec.php import groovy.transform.Canonical +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import com.datadog.appsec.php.docker.AppSecContainer +import static java.net.http.HttpResponse.BodyHandlers.ofString /** * @link https://github.com/DataDog/instrumentation-telemetry-api-docs/blob/main/GeneratedDocumentation/ApiDocs/v2/producing-telemetry.md @@ -22,6 +26,29 @@ class TelemetryHelpers { } } + static class AppEndpoints { + static names = ['app-endpoints'] + List endpoints + + AppEndpoints(Map m) { + endpoints = m.endpoints.collect { new Endpoint(it as Map) } + } + } + + static class Endpoint { + String method + String operationName + String path + String resourceName + + Endpoint(Map m) { + method = m.method + operationName = m.operation_name + path = m.path + resourceName = m.resource_name + } + } + static class Metric { String namespace String name @@ -137,4 +164,43 @@ class TelemetryHelpers { autoEnabled = m.autoEnabled } } + + public static List waitForTelemetryData(AppSecContainer container, int timeoutSec, Closure cl, Class cls, String path = '/hello.php') { + List messages = [] + def deadline = System.currentTimeSeconds() + timeoutSec + def lastHttpReq = System.currentTimeSeconds() - 6 + while (System.currentTimeSeconds() < deadline) { + if (System.currentTimeSeconds() - lastHttpReq > 5) { + lastHttpReq = System.currentTimeSeconds() + // used to flush global (not request-bound) telemetry metrics + def request = container.buildReq(path).GET().build() + def trace = container.traceFromRequest(request, ofString()) { HttpResponse resp -> + assert resp.body().size() > 0 + } + } + def telData = container.drainTelemetry(500) + messages.addAll( + TelemetryHelpers.filterMessages(telData, cls)) + if (cl.call(messages)) { + break + } + } + messages + } + + public static List waitForAppEndpoints(AppSecContainer container, int timeoutSec, Closure cl, String path = '/') { + waitForTelemetryData(container, timeoutSec, cl, AppEndpoints, path) + } + + public static List waitForMetrics(AppSecContainer container, int timeoutSec, Closure cl) { + waitForTelemetryData(container, timeoutSec, cl, GenerateMetrics) + } + + public static List waitForIntegrations(AppSecContainer container, int timeoutSec, Closure cl) { + waitForTelemetryData(container, timeoutSec, cl, WithIntegrations) + } + + public static List waitForLogs(AppSecContainer container, int timeoutSec, Closure cl) { + waitForTelemetryData(container, timeoutSec, cl, Logs) + } } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy index 2a14aefc98d..c8d1922843a 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Laravel8xTests.groovy @@ -15,6 +15,7 @@ import java.net.http.HttpResponse import static com.datadog.appsec.php.integration.TestParams.getPhpVersion import static com.datadog.appsec.php.integration.TestParams.getVariant +import com.datadog.appsec.php.TelemetryHelpers import static java.net.http.HttpResponse.BodyHandlers.ofString @Testcontainers @@ -103,4 +104,28 @@ class Laravel8xTests { assert span.meta."_dd.appsec.event_rules.version" != '' assert span.meta."appsec.blocked" == "true" } + + @Test + void 'Endpoints are sended'() { + def trace = container.traceFromRequest('/') { HttpResponse resp -> + assert resp.statusCode() == 200 + } + + assert trace.traceId != null + + List endpoints + + TelemetryHelpers.waitForAppEndpoints(container, 30, { List messages -> + endpoints = messages.collectMany { it.endpoints } + endpoints.size() > 0 + }) + + assert endpoints.size() == 6 + assert endpoints.find { it.path == '/' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /' } != null + assert endpoints.find { it.path == 'authenticate' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET authenticate' } != null + assert endpoints.find { it.path == 'register' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET register' } != null + assert endpoints.find { it.path == 'dynamic-path/{param01}' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET dynamic-path/{param01}' } != null + assert endpoints.find { it.path == 'sanctum/csrf-cookie' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET sanctum/csrf-cookie' } != null + assert endpoints.find { it.path == 'api/user' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET api/user' } != null + } } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy index 98b6cae4960..cf895ab6562 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/Symfony62Tests.groovy @@ -17,6 +17,7 @@ import java.net.http.HttpResponse import static com.datadog.appsec.php.integration.TestParams.getPhpVersion import static com.datadog.appsec.php.integration.TestParams.getVariant +import com.datadog.appsec.php.TelemetryHelpers import static java.net.http.HttpResponse.BodyHandlers.ofString @Testcontainers @@ -134,4 +135,28 @@ class Symfony62Tests { assert res.exitCode == 0 } } + + @Test + void 'Endpoints are sended'() { + def trace = container.traceFromRequest('/') { HttpResponse resp -> + assert resp.statusCode() == 200 + } + + assert trace.traceId != null + + List endpoints + + TelemetryHelpers.waitForAppEndpoints(container, 30, { List messages -> + endpoints = messages.collectMany { it.endpoints } + endpoints.size() > 0 + }) + + assert endpoints.size() == 6 + assert endpoints.find { it.path == '/' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /' } != null + assert endpoints.find { it.path == '/dynamic-path/{param01}' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /dynamic-path/{param01}' } != null + assert endpoints.find { it.path == '/login' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /login' } != null + assert endpoints.find { it.path == '/_error/{code}.{_format}' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /_error/{code}.{_format}' } != null + assert endpoints.find { it.path == '/register' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /register' } != null + assert endpoints.find { it.path == '/caminho-dinamico/{param01}' && it.method == 'GET' && it.operationName == 'http.request' && it.resourceName == 'GET /caminho-dinamico/{param01}' } != null + } } diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/TelemetryTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/TelemetryTests.groovy index 67da553a11d..502cff21f03 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/TelemetryTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/TelemetryTests.groovy @@ -96,7 +96,7 @@ class TelemetryTests { TelemetryHelpers.Metric wafReq1 TelemetryHelpers.Metric wafReq2 - waitForMetrics(30) { List messages -> + TelemetryHelpers.waitForMetrics(CONTAINER, 30) { List messages -> def allSeries = messages.collectMany { it.series } wafInit = allSeries.find { it.name == 'waf.init' } wafReq1 = allSeries.find { it.name == 'waf.requests' && it.tags.size() == 2 } @@ -195,7 +195,7 @@ class TelemetryTests { ] ]) - def messages = waitForMetrics(30) { List messages -> + def messages = TelemetryHelpers.waitForMetrics(CONTAINER, 30) { List messages -> def allSeries = messages .collectMany { it.series } .findAll { @@ -281,7 +281,7 @@ class TelemetryTests { ] ]) - def messages = waitForTelemetryLogs(30) { List logs -> + def messages = TelemetryHelpers.waitForLogs(CONTAINER, 30) { List logs -> def relevantLogs = logs.collectMany { it.logs.findAll { it.tags.contains('log_type:rc::') } } relevantLogs.size() >= 3 }.collectMany { it.logs } @@ -327,7 +327,7 @@ class TelemetryTests { List allIntegrations = [] boolean foundRedis = false - waitForIntegrations(30) { List messages -> + TelemetryHelpers.waitForIntegrations(CONTAINER, 30) { List messages -> allIntegrations.addAll(messages.collectMany { it.integrations }) foundRedis = allIntegrations.find { it.name == 'phpredis' && it.enabled == Boolean.TRUE } != null } @@ -339,7 +339,7 @@ class TelemetryTests { allIntegrations = [] foundRedis = false boolean foundExec = false - waitForIntegrations(15) { List messages -> + TelemetryHelpers.waitForIntegrations(CONTAINER, 15) { List messages -> allIntegrations.addAll(messages.collectMany { it.integrations }) foundRedis = allIntegrations.find { it.name == 'phpredis' && it.enabled == Boolean.TRUE } != null foundExec = allIntegrations.find { it.name == 'exec' && it.enabled == Boolean.TRUE } != null @@ -349,41 +349,6 @@ class TelemetryTests { assert foundExec } - private static List waitForMetrics(int timeoutSec, Closure cl) { - waitForTelemetryData(timeoutSec, cl, TelemetryHelpers.GenerateMetrics) - } - - private static List waitForIntegrations(int timeoutSec, Closure cl) { - waitForTelemetryData(timeoutSec, cl, TelemetryHelpers.WithIntegrations) - } - - private static List waitForTelemetryLogs(int timeoutSec, Closure cl) { - waitForTelemetryData(timeoutSec, cl, TelemetryHelpers.Logs) - } - - private static List waitForTelemetryData(int timeoutSec, Closure cl, Class cls) { - List messages = [] - def deadline = System.currentTimeSeconds() + timeoutSec - def lastHttpReq = System.currentTimeSeconds() - 6 - while (System.currentTimeSeconds() < deadline) { - if (System.currentTimeSeconds() - lastHttpReq > 5) { - lastHttpReq = System.currentTimeSeconds() - // used to flush global (not request-bound) telemetry metrics - def request = CONTAINER.buildReq('/hello.php').GET().build() - def trace = CONTAINER.traceFromRequest(request, ofString()) { HttpResponse resp -> - assert resp.body().size() > 0 - } - } - def telData = CONTAINER.drainTelemetry(500) - messages.addAll( - TelemetryHelpers.filterMessages(telData, cls)) - if (cl.call(messages)) { - break - } - } - messages - } - /** * This test takes a long time (around 10-12 seconds) because the metric * interval is hardcoded to 10 seconds in the metrics.rs. @@ -423,7 +388,7 @@ class TelemetryTests { TelemetryHelpers.Metric lfiTimeout TelemetryHelpers.Metric ssrfTimeout - waitForMetrics(30) { List messages -> + TelemetryHelpers.waitForMetrics(CONTAINER, 30) { List messages -> def allSeries = messages.collectMany { it.series } wafReq1 = allSeries.find { it.name == 'waf.requests' && it.tags.size() == 2 } lfiEval = allSeries.find{ it.name == 'rasp.rule.eval' && 'rule_type:lfi' in it.tags} @@ -514,7 +479,7 @@ class TelemetryTests { TelemetryHelpers.Metric loginSuccess TelemetryHelpers.Metric loginFailure - waitForMetrics(30) { List messages -> + TelemetryHelpers.waitForMetrics(CONTAINER, 30) { List messages -> def allSeries = messages.collectMany { it.series } println allSeries loginSuccess = allSeries.find{ it.name == 'sdk.event' && 'event_type:login_success' in it.tags} diff --git a/components-rs/common.h b/components-rs/common.h index bfe1502bce3..395882afdf4 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -264,15 +264,19 @@ typedef struct _zend_string _zend_string; #define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100 -typedef enum ddog_ConfigurationOrigin { - DDOG_CONFIGURATION_ORIGIN_ENV_VAR, - DDOG_CONFIGURATION_ORIGIN_CODE, - DDOG_CONFIGURATION_ORIGIN_DD_CONFIG, - DDOG_CONFIGURATION_ORIGIN_REMOTE_CONFIG, - DDOG_CONFIGURATION_ORIGIN_DEFAULT, - DDOG_CONFIGURATION_ORIGIN_LOCAL_STABLE_CONFIG, - DDOG_CONFIGURATION_ORIGIN_FLEET_STABLE_CONFIG, -} ddog_ConfigurationOrigin; +typedef enum ddog_Log { + DDOG_LOG_ERROR = 1, + DDOG_LOG_WARN = 2, + DDOG_LOG_INFO = 3, + DDOG_LOG_DEBUG = 4, + DDOG_LOG_TRACE = 5, + DDOG_LOG_DEPRECATED = (3 | ddog_LOG_ONCE), + DDOG_LOG_STARTUP = (3 | (2 << 4)), + DDOG_LOG_STARTUP_WARN = (1 | (2 << 4)), + DDOG_LOG_SPAN = (4 | (3 << 4)), + DDOG_LOG_SPAN_TRACE = (5 | (3 << 4)), + DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), +} ddog_Log; typedef enum ddog_DynamicConfigUpdateMode { DDOG_DYNAMIC_CONFIG_UPDATE_MODE_READ, @@ -281,30 +285,16 @@ typedef enum ddog_DynamicConfigUpdateMode { DDOG_DYNAMIC_CONFIG_UPDATE_MODE_RESTORE, } ddog_DynamicConfigUpdateMode; -typedef enum ddog_EvaluateAt { - DDOG_EVALUATE_AT_ENTRY, - DDOG_EVALUATE_AT_EXIT, -} ddog_EvaluateAt; - typedef enum ddog_InBodyLocation { DDOG_IN_BODY_LOCATION_NONE, DDOG_IN_BODY_LOCATION_START, DDOG_IN_BODY_LOCATION_END, } ddog_InBodyLocation; -typedef enum ddog_Log { - DDOG_LOG_ERROR = 1, - DDOG_LOG_WARN = 2, - DDOG_LOG_INFO = 3, - DDOG_LOG_DEBUG = 4, - DDOG_LOG_TRACE = 5, - DDOG_LOG_DEPRECATED = (3 | ddog_LOG_ONCE), - DDOG_LOG_STARTUP = (3 | (2 << 4)), - DDOG_LOG_STARTUP_WARN = (1 | (2 << 4)), - DDOG_LOG_SPAN = (4 | (3 << 4)), - DDOG_LOG_SPAN_TRACE = (5 | (3 << 4)), - DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), -} ddog_Log; +typedef enum ddog_EvaluateAt { + DDOG_EVALUATE_AT_ENTRY, + DDOG_EVALUATE_AT_EXIT, +} ddog_EvaluateAt; typedef enum ddog_MetricKind { DDOG_METRIC_KIND_COUNT, @@ -313,6 +303,37 @@ typedef enum ddog_MetricKind { DDOG_METRIC_KIND_DISTRIBUTION, } ddog_MetricKind; +typedef enum ddog_SpanProbeTarget { + DDOG_SPAN_PROBE_TARGET_ACTIVE, + DDOG_SPAN_PROBE_TARGET_ROOT, +} ddog_SpanProbeTarget; + +typedef enum ddog_ProbeStatus { + DDOG_PROBE_STATUS_RECEIVED, + DDOG_PROBE_STATUS_INSTALLED, + DDOG_PROBE_STATUS_EMITTING, + DDOG_PROBE_STATUS_ERROR, + DDOG_PROBE_STATUS_BLOCKED, + DDOG_PROBE_STATUS_WARNING, +} ddog_ProbeStatus; + +typedef enum ddog_ConfigurationOrigin { + DDOG_CONFIGURATION_ORIGIN_ENV_VAR, + DDOG_CONFIGURATION_ORIGIN_CODE, + DDOG_CONFIGURATION_ORIGIN_DD_CONFIG, + DDOG_CONFIGURATION_ORIGIN_REMOTE_CONFIG, + DDOG_CONFIGURATION_ORIGIN_DEFAULT, + DDOG_CONFIGURATION_ORIGIN_LOCAL_STABLE_CONFIG, + DDOG_CONFIGURATION_ORIGIN_FLEET_STABLE_CONFIG, + DDOG_CONFIGURATION_ORIGIN_CALCULATED, +} ddog_ConfigurationOrigin; + +typedef enum ddog_MetricType { + DDOG_METRIC_TYPE_GAUGE, + DDOG_METRIC_TYPE_COUNT, + DDOG_METRIC_TYPE_DISTRIBUTION, +} ddog_MetricType; + typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_TRACERS, DDOG_METRIC_NAMESPACE_PROFILERS, @@ -327,20 +348,16 @@ typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_SIDECAR, } ddog_MetricNamespace; -typedef enum ddog_MetricType { - DDOG_METRIC_TYPE_GAUGE, - DDOG_METRIC_TYPE_COUNT, - DDOG_METRIC_TYPE_DISTRIBUTION, -} ddog_MetricType; - -typedef enum ddog_ProbeStatus { - DDOG_PROBE_STATUS_RECEIVED, - DDOG_PROBE_STATUS_INSTALLED, - DDOG_PROBE_STATUS_EMITTING, - DDOG_PROBE_STATUS_ERROR, - DDOG_PROBE_STATUS_BLOCKED, - DDOG_PROBE_STATUS_WARNING, -} ddog_ProbeStatus; +typedef enum ddog_RemoteConfigProduct { + DDOG_REMOTE_CONFIG_PRODUCT_AGENT_CONFIG, + DDOG_REMOTE_CONFIG_PRODUCT_AGENT_TASK, + DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, + DDOG_REMOTE_CONFIG_PRODUCT_ASM, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DATA, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DD, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES, + DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, +} ddog_RemoteConfigProduct; typedef enum ddog_RemoteConfigCapabilities { DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_ACTIVATION = 1, @@ -388,22 +405,6 @@ typedef enum ddog_RemoteConfigCapabilities { DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRACE_TAGGING_RULES = 43, } ddog_RemoteConfigCapabilities; -typedef enum ddog_RemoteConfigProduct { - DDOG_REMOTE_CONFIG_PRODUCT_AGENT_CONFIG, - DDOG_REMOTE_CONFIG_PRODUCT_AGENT_TASK, - DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, - DDOG_REMOTE_CONFIG_PRODUCT_ASM, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_DATA, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_DD, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES, - DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, -} ddog_RemoteConfigProduct; - -typedef enum ddog_SpanProbeTarget { - DDOG_SPAN_PROBE_TARGET_ACTIVE, - DDOG_SPAN_PROBE_TARGET_ROOT, -} ddog_SpanProbeTarget; - typedef struct ddog_DebuggerPayload ddog_DebuggerPayload; typedef struct ddog_DslString ddog_DslString; @@ -774,17 +775,18 @@ typedef struct ddog_DebuggerValue ddog_DebuggerValue; #define ddog_EVALUATOR_RESULT_REDACTED (const void*)-2 -typedef enum ddog_DebuggerType { - DDOG_DEBUGGER_TYPE_DIAGNOSTICS, - DDOG_DEBUGGER_TYPE_LOGS, -} ddog_DebuggerType; - typedef enum ddog_FieldType { DDOG_FIELD_TYPE_STATIC, DDOG_FIELD_TYPE_ARG, DDOG_FIELD_TYPE_LOCAL, } ddog_FieldType; +typedef enum ddog_DebuggerType { + DDOG_DEBUGGER_TYPE_DIAGNOSTICS, + DDOG_DEBUGGER_TYPE_SNAPSHOTS, + DDOG_DEBUGGER_TYPE_LOGS, +} ddog_DebuggerType; + typedef struct ddog_Entry ddog_Entry; typedef struct ddog_HashMap_CowStr__Value ddog_HashMap_CowStr__Value; @@ -913,16 +915,6 @@ typedef struct ddog_OwnedCharSlice { void (*free)(ddog_CharSlice); } ddog_OwnedCharSlice; -typedef enum ddog_LogLevel { - DDOG_LOG_LEVEL_ERROR, - DDOG_LOG_LEVEL_WARN, - DDOG_LOG_LEVEL_DEBUG, -} ddog_LogLevel; - -typedef enum ddog_TelemetryWorkerBuilderBoolProperty { - DDOG_TELEMETRY_WORKER_BUILDER_BOOL_PROPERTY_CONFIG_TELEMETRY_DEBUG_LOGGING_ENABLED, -} ddog_TelemetryWorkerBuilderBoolProperty; - typedef enum ddog_TelemetryWorkerBuilderEndpointProperty { DDOG_TELEMETRY_WORKER_BUILDER_ENDPOINT_PROPERTY_CONFIG_ENDPOINT, } ddog_TelemetryWorkerBuilderEndpointProperty; @@ -941,6 +933,16 @@ typedef enum ddog_TelemetryWorkerBuilderStrProperty { DDOG_TELEMETRY_WORKER_BUILDER_STR_PROPERTY_RUNTIME_ID, } ddog_TelemetryWorkerBuilderStrProperty; +typedef enum ddog_TelemetryWorkerBuilderBoolProperty { + DDOG_TELEMETRY_WORKER_BUILDER_BOOL_PROPERTY_CONFIG_TELEMETRY_DEBUG_LOGGING_ENABLED, +} ddog_TelemetryWorkerBuilderBoolProperty; + +typedef enum ddog_LogLevel { + DDOG_LOG_LEVEL_ERROR, + DDOG_LOG_LEVEL_WARN, + DDOG_LOG_LEVEL_DEBUG, +} ddog_LogLevel; + typedef struct ddog_TelemetryWorkerBuilder ddog_TelemetryWorkerBuilder; /** @@ -954,6 +956,20 @@ typedef struct ddog_TelemetryWorkerBuilder ddog_TelemetryWorkerBuilder; */ typedef struct ddog_TelemetryWorkerHandle ddog_TelemetryWorkerHandle; +typedef enum ddog_Option_U64_Tag { + DDOG_OPTION_U64_SOME_U64, + DDOG_OPTION_U64_NONE_U64, +} ddog_Option_U64_Tag; + +typedef struct ddog_Option_U64 { + ddog_Option_U64_Tag tag; + union { + struct { + uint64_t some; + }; + }; +} ddog_Option_U64; + typedef enum ddog_Option_Bool_Tag { DDOG_OPTION_BOOL_SOME_BOOL, DDOG_OPTION_BOOL_NONE_BOOL, @@ -980,6 +996,19 @@ typedef struct ddog_AttributeAnyValueBytes ddog_AttributeAnyValueBytes; typedef struct ddog_AttributeArrayValueBytes ddog_AttributeArrayValueBytes; +typedef enum ddog_Method { + DDOG_METHOD_GET = 0, + DDOG_METHOD_POST = 1, + DDOG_METHOD_PUT = 2, + DDOG_METHOD_DELETE = 3, + DDOG_METHOD_PATCH = 4, + DDOG_METHOD_HEAD = 5, + DDOG_METHOD_OPTIONS = 6, + DDOG_METHOD_TRACE = 7, + DDOG_METHOD_CONNECT = 8, + DDOG_METHOD_OTHER = 9, +} ddog_Method; + typedef enum ddog_DynamicInstrumentationConfigState { DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_ENABLED, DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_DISABLED, @@ -1065,37 +1094,28 @@ typedef struct ddog_SenderParameters { ddog_CharSlice url; } ddog_SenderParameters; -typedef enum ddog_crasht_BuildIdType { - DDOG_CRASHT_BUILD_ID_TYPE_GNU, - DDOG_CRASHT_BUILD_ID_TYPE_GO, - DDOG_CRASHT_BUILD_ID_TYPE_PDB, - DDOG_CRASHT_BUILD_ID_TYPE_SHA1, -} ddog_crasht_BuildIdType; - /** - * Result type for runtime callback registration + * Stacktrace collection occurs in the context of a crashing process. + * If the stack is sufficiently corruputed, it is possible (but unlikely), + * for stack trace collection itself to crash. + * We recommend fully enabling stacktrace collection, but having an environment + * variable to allow downgrading the collector. */ -typedef enum ddog_crasht_CallbackResult { - DDOG_CRASHT_CALLBACK_RESULT_OK, - DDOG_CRASHT_CALLBACK_RESULT_ERROR, -} ddog_crasht_CallbackResult; - -typedef enum ddog_crasht_DemangleOptions { - DDOG_CRASHT_DEMANGLE_OPTIONS_COMPLETE, - DDOG_CRASHT_DEMANGLE_OPTIONS_NAME_ONLY, -} ddog_crasht_DemangleOptions; - -typedef enum ddog_crasht_ErrorKind { - DDOG_CRASHT_ERROR_KIND_PANIC, - DDOG_CRASHT_ERROR_KIND_UNHANDLED_EXCEPTION, - DDOG_CRASHT_ERROR_KIND_UNIX_SIGNAL, -} ddog_crasht_ErrorKind; - -typedef enum ddog_crasht_FileType { - DDOG_CRASHT_FILE_TYPE_APK, - DDOG_CRASHT_FILE_TYPE_ELF, - DDOG_CRASHT_FILE_TYPE_PE, -} ddog_crasht_FileType; +typedef enum ddog_crasht_StacktraceCollection { + /** + * Stacktrace collection occurs in the + */ + DDOG_CRASHT_STACKTRACE_COLLECTION_DISABLED, + DDOG_CRASHT_STACKTRACE_COLLECTION_WITHOUT_SYMBOLS, + /** + * This option uses `backtrace::resolve_frame_unsynchronized()` to gather symbol information + * and also unwind inlined functions. Enabling this feature will not only provide symbolic + * details, but may also yield additional or less stack frames compared to other + * configurations. + */ + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER, +} ddog_crasht_StacktraceCollection; /** * This enum represents operations a the tracked library might be engaged in. @@ -1120,6 +1140,12 @@ typedef enum ddog_crasht_OpTypes { DDOG_CRASHT_OP_TYPES_SIZE, } ddog_crasht_OpTypes; +typedef enum ddog_crasht_ErrorKind { + DDOG_CRASHT_ERROR_KIND_PANIC, + DDOG_CRASHT_ERROR_KIND_UNHANDLED_EXCEPTION, + DDOG_CRASHT_ERROR_KIND_UNIX_SIGNAL, +} ddog_crasht_ErrorKind; + /** * See https://man7.org/linux/man-pages/man2/sigaction.2.html * MUST REMAIN IN SYNC WITH THE ENUM IN emit_sigcodes.c @@ -1192,28 +1218,31 @@ typedef enum ddog_crasht_SignalNames { DDOG_CRASHT_SIGNAL_NAMES_UNKNOWN, } ddog_crasht_SignalNames; +typedef enum ddog_crasht_BuildIdType { + DDOG_CRASHT_BUILD_ID_TYPE_GNU, + DDOG_CRASHT_BUILD_ID_TYPE_GO, + DDOG_CRASHT_BUILD_ID_TYPE_PDB, + DDOG_CRASHT_BUILD_ID_TYPE_SHA1, +} ddog_crasht_BuildIdType; + +typedef enum ddog_crasht_FileType { + DDOG_CRASHT_FILE_TYPE_APK, + DDOG_CRASHT_FILE_TYPE_ELF, + DDOG_CRASHT_FILE_TYPE_PE, +} ddog_crasht_FileType; + +typedef enum ddog_crasht_DemangleOptions { + DDOG_CRASHT_DEMANGLE_OPTIONS_COMPLETE, + DDOG_CRASHT_DEMANGLE_OPTIONS_NAME_ONLY, +} ddog_crasht_DemangleOptions; + /** - * Stacktrace collection occurs in the context of a crashing process. - * If the stack is sufficiently corruputed, it is possible (but unlikely), - * for stack trace collection itself to crash. - * We recommend fully enabling stacktrace collection, but having an environment - * variable to allow downgrading the collector. + * Result type for runtime callback registration */ -typedef enum ddog_crasht_StacktraceCollection { - /** - * Stacktrace collection occurs in the - */ - DDOG_CRASHT_STACKTRACE_COLLECTION_DISABLED, - DDOG_CRASHT_STACKTRACE_COLLECTION_WITHOUT_SYMBOLS, - /** - * This option uses `backtrace::resolve_frame_unsynchronized()` to gather symbol information - * and also unwind inlined functions. Enabling this feature will not only provide symbolic - * details, but may also yield additional or less stack frames compared to other - * configurations. - */ - DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, - DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER, -} ddog_crasht_StacktraceCollection; +typedef enum ddog_crasht_CallbackResult { + DDOG_CRASHT_CALLBACK_RESULT_OK, + DDOG_CRASHT_CALLBACK_RESULT_ERROR, +} ddog_crasht_CallbackResult; typedef struct ddog_crasht_CrashInfo ddog_crasht_CrashInfo; @@ -1291,7 +1320,7 @@ typedef struct ddog_crasht_Config { /** * Timeout in milliseconds before the signal handler starts tearing things down to return. * If 0, uses the default timeout as specified in - * `datadog_crashtracker::shared::constants::DD_CRASHTRACK_DEFAULT_TIMEOUT`. Otherwise, uses + * `libdd_crashtracker::shared::constants::DD_CRASHTRACK_DEFAULT_TIMEOUT`. Otherwise, uses * the specified timeout value. * This is given as a uint32_t, but the actual timeout needs to fit inside of an i32 (max * 2^31-1). This is a limitation of the various interfaces used to guarantee the timeout. @@ -1411,13 +1440,17 @@ typedef struct ddog_crasht_CrashInfoBuilder_NewResult { }; } ddog_crasht_CrashInfoBuilder_NewResult; -typedef enum ddog_crasht_CrashInfo_NewResult_Tag { - DDOG_CRASHT_CRASH_INFO_NEW_RESULT_OK, - DDOG_CRASHT_CRASH_INFO_NEW_RESULT_ERR, -} ddog_crasht_CrashInfo_NewResult_Tag; +/** + * A generic result type for when an operation may fail, + * or may return in case of success. + */ +typedef enum ddog_crasht_Result_HandleCrashInfo_Tag { + DDOG_CRASHT_RESULT_HANDLE_CRASH_INFO_OK_HANDLE_CRASH_INFO, + DDOG_CRASHT_RESULT_HANDLE_CRASH_INFO_ERR_HANDLE_CRASH_INFO, +} ddog_crasht_Result_HandleCrashInfo_Tag; -typedef struct ddog_crasht_CrashInfo_NewResult { - ddog_crasht_CrashInfo_NewResult_Tag tag; +typedef struct ddog_crasht_Result_HandleCrashInfo { + ddog_crasht_Result_HandleCrashInfo_Tag tag; union { struct { struct ddog_crasht_Handle_CrashInfo ok; @@ -1426,7 +1459,9 @@ typedef struct ddog_crasht_CrashInfo_NewResult { struct ddog_Error err; }; }; -} ddog_crasht_CrashInfo_NewResult; +} ddog_crasht_Result_HandleCrashInfo; + +typedef struct ddog_crasht_Result_HandleCrashInfo ddog_crasht_CrashInfo_NewResult; typedef struct ddog_crasht_OsInfo { ddog_CharSlice architecture; diff --git a/components-rs/crashtracker.h b/components-rs/crashtracker.h index 42209ff18cc..96ed333d622 100644 --- a/components-rs/crashtracker.h +++ b/components-rs/crashtracker.h @@ -451,7 +451,7 @@ void ddog_crasht_CrashInfoBuilder_drop(struct ddog_crasht_Handle_CrashInfoBuilde * which has not previously been dropped. */ DDOG_CHECK_RETURN -struct ddog_crasht_CrashInfo_NewResult ddog_crasht_CrashInfoBuilder_build(struct ddog_crasht_Handle_CrashInfoBuilder *builder); +ddog_crasht_CrashInfo_NewResult ddog_crasht_CrashInfoBuilder_build(struct ddog_crasht_Handle_CrashInfoBuilder *builder); /** * # Safety @@ -854,7 +854,7 @@ struct ddog_StringWrapperResult ddog_crasht_demangle(ddog_CharSlice name, * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't * allowed to do in the handler. * - * See comments in [datadog-crashtracker/lib.rs] for a full architecture description. + * See comments in [libdd-crashtracker/lib.rs] for a full architecture description. * # Safety * No safety concerns */ @@ -868,7 +868,7 @@ DDOG_CHECK_RETURN struct ddog_VoidResult ddog_crasht_receiver_entry_point_stdin( * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't * allowed to do in the handler. * - * See comments in [datadog-crashtracker/lib.rs] for a full architecture + * See comments in [libdd-crashtracker/lib.rs] for a full architecture * description. * # Safety * No safety concerns diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 35d06a61820..de7ea97972a 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -181,6 +181,10 @@ ddog_MaybeError ddog_sidecar_telemetry_filter_flush(struct ddog_SidecarTransport ddog_CharSlice service, ddog_CharSlice env); +bool ddog_sidecar_telemetry_are_endpoints_collected(ddog_ShmCacheMap *cache, + ddog_CharSlice service, + ddog_CharSlice env); + void ddog_init_span_func(void (*free_func)(ddog_OwnedZendString), void (*addref_func)(struct _zend_string*), ddog_OwnedZendString (*init_func)(ddog_CharSlice)); diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 4746e0d2163..76aa150c415 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -118,7 +118,19 @@ ddog_MaybeError ddog_sidecar_telemetry_enqueueConfig(struct ddog_SidecarTranspor ddog_CharSlice config_key, ddog_CharSlice config_value, enum ddog_ConfigurationOrigin origin, - ddog_CharSlice config_id); + ddog_CharSlice config_id, + struct ddog_Option_U64 seq_id); + +/** + * Reports an endpoint to the telemetry. + */ +ddog_MaybeError ddog_sidecar_telemetry_addEndpoint(struct ddog_SidecarTransport **transport, + const struct ddog_InstanceId *instance_id, + const ddog_QueueId *queue_id, + enum ddog_Method method, + ddog_CharSlice path, + ddog_CharSlice operation_name, + ddog_CharSlice resource_name); /** * Reports a dependency to the telemetry. diff --git a/components-rs/telemetry.h b/components-rs/telemetry.h index fd8ffadcdbf..5a6c6288ece 100644 --- a/components-rs/telemetry.h +++ b/components-rs/telemetry.h @@ -42,7 +42,8 @@ ddog_MaybeError ddog_telemetry_builder_with_config(struct ddog_TelemetryWorkerBu ddog_CharSlice name, ddog_CharSlice value, enum ddog_ConfigurationOrigin origin, - ddog_CharSlice config_id); + ddog_CharSlice config_id, + struct ddog_Option_U64 seq_id); /** * Builds the telemetry worker and return a handle to it diff --git a/components-rs/telemetry.rs b/components-rs/telemetry.rs index c572bceea84..1a230a268b6 100644 --- a/components-rs/telemetry.rs +++ b/components-rs/telemetry.rs @@ -5,6 +5,7 @@ use hashbrown::{Equivalent, HashMap}; use std::collections::HashSet; use std::ffi::CString; use std::path::PathBuf; +use std::time::{Duration, Instant, SystemTime}; use datadog_ipc::platform::NamedShmHandle; use datadog_sidecar::one_way_shared_memory::{open_named_shm, OneWayShmReader}; @@ -236,11 +237,12 @@ pub struct ShmCache { pub config_sent: bool, pub integrations: HashSet, pub composer_paths: HashSet, + pub last_endpoints_push: SystemTime, pub reader: OneWayShmReader, } #[derive(Hash, Eq, PartialEq)] -pub struct ShmCacheKey(String, String); +struct ShmCacheKey(String, String); impl Equivalent for (&str, &str) { fn equivalent(&self, key: &ShmCacheKey) -> bool { @@ -285,16 +287,18 @@ unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( cache.config_sent = false; cache.integrations.clear(); cache.composer_paths.clear(); + cache.last_endpoints_push = SystemTime::UNIX_EPOCH; return; } } - if let Ok((config_sent, integrations, composer_paths)) = - bincode::deserialize::<(bool, HashSet, HashSet)>(buf) + if let Ok((config_sent, integrations, composer_paths, last_endpoints_push)) = + bincode::deserialize::<(bool, HashSet, HashSet, SystemTime)>(buf) { cache.config_sent = config_sent; cache.integrations = integrations; cache.composer_paths = composer_paths; + cache.last_endpoints_push = last_endpoints_push; } } } @@ -315,6 +319,7 @@ unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( config_sent: false, integrations: HashSet::new(), composer_paths: HashSet::new(), + last_endpoints_push: SystemTime::UNIX_EPOCH, }).into_mut(); refresh_cache(cached_entry); @@ -358,3 +363,14 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_filter_flush( MaybeError::None } + +#[no_mangle] +pub unsafe extern "C" fn ddog_sidecar_telemetry_are_endpoints_collected( + cache: &mut ShmCacheMap, + service: CharSlice, + env: CharSlice, +) -> bool { + let cache_entry = ddog_sidecar_telemetry_cache_get_or_update(cache, service, env); + let result = cache_entry.last_endpoints_push.elapsed().map_or(false, |d| d < Duration::from_secs(60)); // 1 minute + result +} diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 359576d8aac..7bfdedee956 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -2769,6 +2769,91 @@ PHP_FUNCTION(DDTrace_dogstatsd_set) { RETURN_NULL(); } +PHP_FUNCTION(DDTrace_are_endpoints_collected) { + UNUSED(execute_data); + + if (!DDTRACE_G(last_service_name) || !DDTRACE_G(last_env_name)) { + RETURN_FALSE; + } + + ddog_CharSlice service_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_service_name)); + ddog_CharSlice env_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_env_name)); + + RETURN_BOOL(ddog_sidecar_telemetry_are_endpoints_collected(ddtrace_telemetry_cache(), service_name, env_name)); +} + +zend_string *get_env() { + if (get_DD_ENV() == NULL) { + return zend_string_init(ZEND_STRL("env"), 0); + } + return get_DD_ENV(); +} + +ddog_Method dd_string_to_method(zend_string *method) { + if (zend_string_equals_literal(method, "GET")) { + return DDOG_METHOD_GET; + } + if (zend_string_equals_literal(method, "POST")) { + return DDOG_METHOD_POST; + } + if (zend_string_equals_literal(method, "PUT")) { + return DDOG_METHOD_PUT; + } + if (zend_string_equals_literal(method, "DELETE")) { + return DDOG_METHOD_DELETE; + } + if (zend_string_equals_literal(method, "PATCH")) { + return DDOG_METHOD_PATCH; + } + if (zend_string_equals_literal(method, "HEAD")) { + return DDOG_METHOD_HEAD; + } + if (zend_string_equals_literal(method, "OPTIONS")) { + return DDOG_METHOD_OPTIONS; + } + if (zend_string_equals_literal(method, "TRACE")) { + return DDOG_METHOD_TRACE; + } + if (zend_string_equals_literal(method, "CONNECT")) { + return DDOG_METHOD_CONNECT; + } + return DDOG_METHOD_OTHER; +} + +PHP_FUNCTION(DDTrace_add_endpoint) { + UNUSED(execute_data); + zend_string *path = NULL; + zend_string *operation_name = NULL; + zend_string *resource_name = NULL; + zend_string *method = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSSS", &path, &operation_name, &resource_name, &method) == FAILURE) { + RETURN_FALSE; + } + + if (!ddtrace_sidecar || !ddtrace_sidecar_instance_id || !DDTRACE_G(sidecar_queue_id)) { + RETURN_FALSE; + } + + ddog_Method method_enum = dd_string_to_method(method); + ddog_CharSlice path_slice = dd_zend_string_to_CharSlice(path); + ddog_CharSlice operation_name_slice = dd_zend_string_to_CharSlice(operation_name); + ddog_CharSlice resource_name_slice = dd_zend_string_to_CharSlice(resource_name); + + ddog_MaybeError result = ddog_sidecar_telemetry_addEndpoint( + &ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), method_enum, path_slice, operation_name_slice, + resource_name_slice); + + if (result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { + ddog_CharSlice message = ddog_Error_message(&result.some); + LOG_LINE(ERROR, "Error submitting endpoint to sidecar: %.*s", (int)message.len, (char *)message.ptr); + ZVAL_FALSE(return_value); + } else { + ZVAL_TRUE(return_value); + } + ddog_MaybeError_drop(result); +} + PHP_FUNCTION(dd_trace_send_traces_via_thread) { char *payload = NULL; ddtrace_zpplong_t num_traces = 0; diff --git a/ext/ddtrace.stub.php b/ext/ddtrace.stub.php index fbc182c45c3..135b8511eb6 100644 --- a/ext/ddtrace.stub.php +++ b/ext/ddtrace.stub.php @@ -822,6 +822,23 @@ function resource_weak_store(mixed $resource, string $key, mixed $value): void { * @return mixed|null The stored value, or null if missing. */ function resource_weak_get(mixed $resource, string $key): mixed {} + + /** + * Check if endpoints are already collected + * + * @return bool + */ + function are_endpoints_collected(): bool {} + + /** + * Add an endpoint + * + * @param string $path The path of the endpoint + * @param string $operation_name The operation name of the endpoint + * @param string $resource_name The resource name of the endpoint + * @param string $method The method of the endpoint + */ + function add_endpoint(string $path, string $operation_name, string $resource_name, string $method): bool {} } namespace DDTrace\System { diff --git a/ext/ddtrace_arginfo.h b/ext/ddtrace_arginfo.h index 9c005f69d09..c5e0cefcc1c 100644 --- a/ext/ddtrace_arginfo.h +++ b/ext/ddtrace_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4936b63014f9af91c1d499f9871745fb9c642066 */ + * Stub hash: 9539356c50d39ee983e578d17d1796e50b761f86 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_trace_method, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, className, IS_STRING, 0) @@ -164,6 +164,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_resource_weak_get, 0, 2, ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_are_endpoints_collected, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_add_endpoint, 0, 4, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, operation_name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, resource_name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_System_container_id, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -175,8 +185,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_Config_integration_analy ZEND_ARG_TYPE_INFO(0, integrationName, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_UserRequest_has_listeners, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_DDTrace_UserRequest_has_listeners arginfo_DDTrace_are_endpoints_collected ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_UserRequest_notify_start, 0, 2, IS_ARRAY, 1) ZEND_ARG_OBJ_INFO(0, span, DDTrace\\RootSpanData, 0) @@ -226,9 +235,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dd_trace_env_config, 0, 1, IS_MI ZEND_ARG_TYPE_INFO(0, envName, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_dd_trace_disable_in_request arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_dd_trace_disable_in_request arginfo_DDTrace_are_endpoints_collected -#define arginfo_dd_trace_reset arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_dd_trace_reset arginfo_DDTrace_are_endpoints_collected ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_dd_trace_serialize_msgpack, 0, 1, MAY_BE_BOOL|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, traceArray, IS_ARRAY, 0) @@ -241,15 +250,15 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dd_trace_dd_get_memory_limit, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_dd_trace_check_memory_under_limit arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_dd_trace_check_memory_under_limit arginfo_DDTrace_are_endpoints_collected ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ddtrace_config_app_name, 0, 0, IS_STRING, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fallbackName, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -#define arginfo_ddtrace_config_distributed_tracing_enabled arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_ddtrace_config_distributed_tracing_enabled arginfo_DDTrace_are_endpoints_collected -#define arginfo_ddtrace_config_trace_enabled arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_ddtrace_config_trace_enabled arginfo_DDTrace_are_endpoints_collected #define arginfo_ddtrace_config_integration_enabled arginfo_DDTrace_Config_integration_analytics_enabled @@ -276,7 +285,7 @@ ZEND_END_ARG_INFO() #define arginfo_dd_trace_closed_spans_count arginfo_dd_trace_dd_get_memory_limit -#define arginfo_dd_trace_tracer_is_limited arginfo_DDTrace_UserRequest_has_listeners +#define arginfo_dd_trace_tracer_is_limited arginfo_DDTrace_are_endpoints_collected #define arginfo_dd_trace_compile_time_microseconds arginfo_dd_trace_dd_get_memory_limit @@ -374,6 +383,8 @@ ZEND_FUNCTION(DDTrace_dogstatsd_histogram); ZEND_FUNCTION(DDTrace_dogstatsd_set); ZEND_FUNCTION(DDTrace_resource_weak_store); ZEND_FUNCTION(DDTrace_resource_weak_get); +ZEND_FUNCTION(DDTrace_are_endpoints_collected); +ZEND_FUNCTION(DDTrace_add_endpoint); ZEND_FUNCTION(DDTrace_System_container_id); ZEND_FUNCTION(DDTrace_Config_integration_analytics_enabled); ZEND_FUNCTION(DDTrace_Config_integration_analytics_sample_rate); @@ -464,6 +475,8 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "dogstatsd_set"), zif_DDTrace_dogstatsd_set, arginfo_DDTrace_dogstatsd_set, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "resource_weak_store"), zif_DDTrace_resource_weak_store, arginfo_DDTrace_resource_weak_store, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "resource_weak_get"), zif_DDTrace_resource_weak_get, arginfo_DDTrace_resource_weak_get, 0, NULL, NULL) + ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "are_endpoints_collected"), zif_DDTrace_are_endpoints_collected, arginfo_DDTrace_are_endpoints_collected, 0, NULL, NULL) + ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "add_endpoint"), zif_DDTrace_add_endpoint, arginfo_DDTrace_add_endpoint, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\System", "container_id"), zif_DDTrace_System_container_id, arginfo_DDTrace_System_container_id, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\Config", "integration_analytics_enabled"), zif_DDTrace_Config_integration_analytics_enabled, arginfo_DDTrace_Config_integration_analytics_enabled, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\Config", "integration_analytics_sample_rate"), zif_DDTrace_Config_integration_analytics_sample_rate, arginfo_DDTrace_Config_integration_analytics_sample_rate, 0, NULL, NULL) @@ -566,15 +579,15 @@ static zend_class_entry *register_class_DDTrace_SpanEvent(zend_class_entry *clas zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_attributes_name, true); + zend_string_release(property_attributes_name); zval property_timestamp_default_value; ZVAL_UNDEF(&property_timestamp_default_value); - zend_string *property_timestamp_name = zend_string_init("timestamp", sizeof("timestamp") - 1, true); + zend_string *property_timestamp_name = zend_string_init("timestamp", sizeof("timestamp") - 1, 1); zend_declare_typed_property(class_entry, property_timestamp_name, &property_timestamp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release_ex(property_timestamp_name, true); + zend_string_release(property_timestamp_name); return class_entry; } @@ -588,10 +601,10 @@ static zend_class_entry *register_class_DDTrace_ExceptionSpanEvent(zend_class_en zval property_exception_default_value; ZVAL_UNDEF(&property_exception_default_value); - zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, true); + zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, 1); zend_string *property_exception_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); zend_declare_typed_property(class_entry, property_exception_name, &property_exception_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_exception_class_Throwable, 0, 0)); - zend_string_release_ex(property_exception_name, true); + zend_string_release(property_exception_name); return class_entry; } @@ -606,33 +619,33 @@ static zend_class_entry *register_class_DDTrace_SpanLink(zend_class_entry *class zval property_traceId_default_value; ZVAL_UNDEF(&property_traceId_default_value); - zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, true); + zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, 1); zend_declare_typed_property(class_entry, property_traceId_name, &property_traceId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_traceId_name, true); + zend_string_release(property_traceId_name); zval property_spanId_default_value; ZVAL_UNDEF(&property_spanId_default_value); - zend_string *property_spanId_name = zend_string_init("spanId", sizeof("spanId") - 1, true); + zend_string *property_spanId_name = zend_string_init("spanId", sizeof("spanId") - 1, 1); zend_declare_typed_property(class_entry, property_spanId_name, &property_spanId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_spanId_name, true); + zend_string_release(property_spanId_name); zval property_traceState_default_value; ZVAL_UNDEF(&property_traceState_default_value); - zend_string *property_traceState_name = zend_string_init("traceState", sizeof("traceState") - 1, true); + zend_string *property_traceState_name = zend_string_init("traceState", sizeof("traceState") - 1, 1); zend_declare_typed_property(class_entry, property_traceState_name, &property_traceState_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_traceState_name, true); + zend_string_release(property_traceState_name); zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_attributes_name, true); + zend_string_release(property_attributes_name); zval property_droppedAttributesCount_default_value; ZVAL_UNDEF(&property_droppedAttributesCount_default_value); - zend_string *property_droppedAttributesCount_name = zend_string_init("droppedAttributesCount", sizeof("droppedAttributesCount") - 1, true); + zend_string *property_droppedAttributesCount_name = zend_string_init("droppedAttributesCount", sizeof("droppedAttributesCount") - 1, 1); zend_declare_typed_property(class_entry, property_droppedAttributesCount_name, &property_droppedAttributesCount_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release_ex(property_droppedAttributesCount_name, true); + zend_string_release(property_droppedAttributesCount_name); return class_entry; } @@ -646,15 +659,15 @@ static zend_class_entry *register_class_DDTrace_GitMetadata(void) zval property_commitSha_default_value; ZVAL_EMPTY_STRING(&property_commitSha_default_value); - zend_string *property_commitSha_name = zend_string_init("commitSha", sizeof("commitSha") - 1, true); + zend_string *property_commitSha_name = zend_string_init("commitSha", sizeof("commitSha") - 1, 1); zend_declare_typed_property(class_entry, property_commitSha_name, &property_commitSha_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_commitSha_name, true); + zend_string_release(property_commitSha_name); zval property_repositoryUrl_default_value; ZVAL_EMPTY_STRING(&property_repositoryUrl_default_value); - zend_string *property_repositoryUrl_name = zend_string_init("repositoryUrl", sizeof("repositoryUrl") - 1, true); + zend_string *property_repositoryUrl_name = zend_string_init("repositoryUrl", sizeof("repositoryUrl") - 1, 1); zend_declare_typed_property(class_entry, property_repositoryUrl_name, &property_repositoryUrl_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_repositoryUrl_name, true); + zend_string_release(property_repositoryUrl_name); return class_entry; } @@ -676,27 +689,27 @@ static zend_class_entry *register_class_DDTrace_SpanData(void) zval property_service_default_value; ZVAL_EMPTY_STRING(&property_service_default_value); - zend_string *property_service_name = zend_string_init("service", sizeof("service") - 1, true); + zend_string *property_service_name = zend_string_init("service", sizeof("service") - 1, 1); zend_declare_typed_property(class_entry, property_service_name, &property_service_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); - zend_string_release_ex(property_service_name, true); + zend_string_release(property_service_name); zval property_env_default_value; ZVAL_EMPTY_STRING(&property_env_default_value); - zend_string *property_env_name = zend_string_init("env", sizeof("env") - 1, true); + zend_string *property_env_name = zend_string_init("env", sizeof("env") - 1, 1); zend_declare_typed_property(class_entry, property_env_name, &property_env_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_env_name, true); + zend_string_release(property_env_name); zval property_version_default_value; ZVAL_EMPTY_STRING(&property_version_default_value); - zend_string *property_version_name = zend_string_init("version", sizeof("version") - 1, true); + zend_string *property_version_name = zend_string_init("version", sizeof("version") - 1, 1); zend_declare_typed_property(class_entry, property_version_name, &property_version_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_version_name, true); + zend_string_release(property_version_name); zval property_meta_struct_default_value; ZVAL_EMPTY_ARRAY(&property_meta_struct_default_value); - zend_string *property_meta_struct_name = zend_string_init("meta_struct", sizeof("meta_struct") - 1, true); + zend_string *property_meta_struct_name = zend_string_init("meta_struct", sizeof("meta_struct") - 1, 1); zend_declare_typed_property(class_entry, property_meta_struct_name, &property_meta_struct_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_meta_struct_name, true); + zend_string_release(property_meta_struct_name); zval property_type_default_value; ZVAL_EMPTY_STRING(&property_type_default_value); @@ -704,70 +717,70 @@ static zend_class_entry *register_class_DDTrace_SpanData(void) zval property_meta_default_value; ZVAL_EMPTY_ARRAY(&property_meta_default_value); - zend_string *property_meta_name = zend_string_init("meta", sizeof("meta") - 1, true); + zend_string *property_meta_name = zend_string_init("meta", sizeof("meta") - 1, 1); zend_declare_typed_property(class_entry, property_meta_name, &property_meta_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_meta_name, true); + zend_string_release(property_meta_name); zval property_metrics_default_value; ZVAL_EMPTY_ARRAY(&property_metrics_default_value); - zend_string *property_metrics_name = zend_string_init("metrics", sizeof("metrics") - 1, true); + zend_string *property_metrics_name = zend_string_init("metrics", sizeof("metrics") - 1, 1); zend_declare_typed_property(class_entry, property_metrics_name, &property_metrics_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_metrics_name, true); + zend_string_release(property_metrics_name); zval property_exception_default_value; ZVAL_NULL(&property_exception_default_value); - zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, true); + zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, 1); zend_string *property_exception_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); zend_declare_typed_property(class_entry, property_exception_name, &property_exception_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_exception_class_Throwable, 0, MAY_BE_NULL)); - zend_string_release_ex(property_exception_name, true); + zend_string_release(property_exception_name); zval property_id_default_value; ZVAL_UNDEF(&property_id_default_value); - zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, true); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_id_name, true); + zend_string_release(property_id_name); zval property_links_default_value; ZVAL_EMPTY_ARRAY(&property_links_default_value); - zend_string *property_links_name = zend_string_init("links", sizeof("links") - 1, true); + zend_string *property_links_name = zend_string_init("links", sizeof("links") - 1, 1); zend_declare_typed_property(class_entry, property_links_name, &property_links_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_links_name, true); + zend_string_release(property_links_name); zval property_events_default_value; ZVAL_EMPTY_ARRAY(&property_events_default_value); - zend_string *property_events_name = zend_string_init("events", sizeof("events") - 1, true); + zend_string *property_events_name = zend_string_init("events", sizeof("events") - 1, 1); zend_declare_typed_property(class_entry, property_events_name, &property_events_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_events_name, true); + zend_string_release(property_events_name); zval property_peerServiceSources_default_value; ZVAL_EMPTY_ARRAY(&property_peerServiceSources_default_value); - zend_string *property_peerServiceSources_name = zend_string_init("peerServiceSources", sizeof("peerServiceSources") - 1, true); + zend_string *property_peerServiceSources_name = zend_string_init("peerServiceSources", sizeof("peerServiceSources") - 1, 1); zend_declare_typed_property(class_entry, property_peerServiceSources_name, &property_peerServiceSources_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_peerServiceSources_name, true); + zend_string_release(property_peerServiceSources_name); zval property_parent_default_value; - ZVAL_UNDEF(&property_parent_default_value); + ZVAL_NULL(&property_parent_default_value); zend_string *property_parent_class_DDTrace_SpanData = zend_string_init("DDTrace\\SpanData", sizeof("DDTrace\\SpanData")-1, 1); zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanData, 0, MAY_BE_NULL)); zval property_stack_default_value; ZVAL_UNDEF(&property_stack_default_value); - zend_string *property_stack_name = zend_string_init("stack", sizeof("stack") - 1, true); + zend_string *property_stack_name = zend_string_init("stack", sizeof("stack") - 1, 1); zend_string *property_stack_class_DDTrace_SpanStack = zend_string_init("DDTrace\\SpanStack", sizeof("DDTrace\\SpanStack")-1, 1); zend_declare_typed_property(class_entry, property_stack_name, &property_stack_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_stack_class_DDTrace_SpanStack, 0, 0)); - zend_string_release_ex(property_stack_name, true); + zend_string_release(property_stack_name); zval property_onClose_default_value; ZVAL_EMPTY_ARRAY(&property_onClose_default_value); - zend_string *property_onClose_name = zend_string_init("onClose", sizeof("onClose") - 1, true); + zend_string *property_onClose_name = zend_string_init("onClose", sizeof("onClose") - 1, 1); zend_declare_typed_property(class_entry, property_onClose_name, &property_onClose_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_onClose_name, true); + zend_string_release(property_onClose_name); zval property_baggage_default_value; ZVAL_EMPTY_ARRAY(&property_baggage_default_value); - zend_string *property_baggage_name = zend_string_init("baggage", sizeof("baggage") - 1, true); + zend_string *property_baggage_name = zend_string_init("baggage", sizeof("baggage") - 1, 1); zend_declare_typed_property(class_entry, property_baggage_name, &property_baggage_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_baggage_name, true); + zend_string_release(property_baggage_name); return class_entry; } @@ -791,65 +804,65 @@ static zend_class_entry *register_class_DDTrace_RootSpanData(zend_class_entry *c zval property_origin_default_value; ZVAL_UNDEF(&property_origin_default_value); - zend_string *property_origin_name = zend_string_init("origin", sizeof("origin") - 1, true); + zend_string *property_origin_name = zend_string_init("origin", sizeof("origin") - 1, 1); zend_declare_typed_property(class_entry, property_origin_name, &property_origin_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_origin_name, true); + zend_string_release(property_origin_name); zval property_propagatedTags_default_value; ZVAL_EMPTY_ARRAY(&property_propagatedTags_default_value); - zend_string *property_propagatedTags_name = zend_string_init("propagatedTags", sizeof("propagatedTags") - 1, true); + zend_string *property_propagatedTags_name = zend_string_init("propagatedTags", sizeof("propagatedTags") - 1, 1); zend_declare_typed_property(class_entry, property_propagatedTags_name, &property_propagatedTags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_propagatedTags_name, true); + zend_string_release(property_propagatedTags_name); zval property_samplingPriority_default_value; ZVAL_LONG(&property_samplingPriority_default_value, DDTRACE_PRIORITY_SAMPLING_UNKNOWN); - zend_string *property_samplingPriority_name = zend_string_init("samplingPriority", sizeof("samplingPriority") - 1, true); + zend_string *property_samplingPriority_name = zend_string_init("samplingPriority", sizeof("samplingPriority") - 1, 1); zend_declare_typed_property(class_entry, property_samplingPriority_name, &property_samplingPriority_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release_ex(property_samplingPriority_name, true); + zend_string_release(property_samplingPriority_name); zval property_propagatedSamplingPriority_default_value; ZVAL_UNDEF(&property_propagatedSamplingPriority_default_value); - zend_string *property_propagatedSamplingPriority_name = zend_string_init("propagatedSamplingPriority", sizeof("propagatedSamplingPriority") - 1, true); + zend_string *property_propagatedSamplingPriority_name = zend_string_init("propagatedSamplingPriority", sizeof("propagatedSamplingPriority") - 1, 1); zend_declare_typed_property(class_entry, property_propagatedSamplingPriority_name, &property_propagatedSamplingPriority_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release_ex(property_propagatedSamplingPriority_name, true); + zend_string_release(property_propagatedSamplingPriority_name); zval property_tracestate_default_value; ZVAL_UNDEF(&property_tracestate_default_value); - zend_string *property_tracestate_name = zend_string_init("tracestate", sizeof("tracestate") - 1, true); + zend_string *property_tracestate_name = zend_string_init("tracestate", sizeof("tracestate") - 1, 1); zend_declare_typed_property(class_entry, property_tracestate_name, &property_tracestate_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_tracestate_name, true); + zend_string_release(property_tracestate_name); zval property_tracestateTags_default_value; ZVAL_EMPTY_ARRAY(&property_tracestateTags_default_value); - zend_string *property_tracestateTags_name = zend_string_init("tracestateTags", sizeof("tracestateTags") - 1, true); + zend_string *property_tracestateTags_name = zend_string_init("tracestateTags", sizeof("tracestateTags") - 1, 1); zend_declare_typed_property(class_entry, property_tracestateTags_name, &property_tracestateTags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_tracestateTags_name, true); + zend_string_release(property_tracestateTags_name); zval property_parentId_default_value; ZVAL_UNDEF(&property_parentId_default_value); - zend_string *property_parentId_name = zend_string_init("parentId", sizeof("parentId") - 1, true); + zend_string *property_parentId_name = zend_string_init("parentId", sizeof("parentId") - 1, 1); zend_declare_typed_property(class_entry, property_parentId_name, &property_parentId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_parentId_name, true); + zend_string_release(property_parentId_name); zval property_traceId_default_value; ZVAL_EMPTY_STRING(&property_traceId_default_value); - zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, true); + zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, 1); zend_declare_typed_property(class_entry, property_traceId_name, &property_traceId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_traceId_name, true); + zend_string_release(property_traceId_name); zval property_gitMetadata_default_value; ZVAL_NULL(&property_gitMetadata_default_value); - zend_string *property_gitMetadata_name = zend_string_init("gitMetadata", sizeof("gitMetadata") - 1, true); + zend_string *property_gitMetadata_name = zend_string_init("gitMetadata", sizeof("gitMetadata") - 1, 1); zend_string *property_gitMetadata_class_DDTrace_GitMetadata = zend_string_init("DDTrace\\GitMetadata", sizeof("DDTrace\\GitMetadata")-1, 1); zend_declare_typed_property(class_entry, property_gitMetadata_name, &property_gitMetadata_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_gitMetadata_class_DDTrace_GitMetadata, 0, MAY_BE_NULL)); - zend_string_release_ex(property_gitMetadata_name, true); + zend_string_release(property_gitMetadata_name); zval property_inferredSpan_default_value; ZVAL_NULL(&property_inferredSpan_default_value); - zend_string *property_inferredSpan_name = zend_string_init("inferredSpan", sizeof("inferredSpan") - 1, true); + zend_string *property_inferredSpan_name = zend_string_init("inferredSpan", sizeof("inferredSpan") - 1, 1); zend_string *property_inferredSpan_class_DDTrace_InferredSpanData = zend_string_init("DDTrace\\InferredSpanData", sizeof("DDTrace\\InferredSpanData")-1, 1); zend_declare_typed_property(class_entry, property_inferredSpan_name, &property_inferredSpan_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_inferredSpan_class_DDTrace_InferredSpanData, 0, MAY_BE_NULL)); - zend_string_release_ex(property_inferredSpan_name, true); + zend_string_release(property_inferredSpan_name); return class_entry; } @@ -862,22 +875,22 @@ static zend_class_entry *register_class_DDTrace_SpanStack(void) class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); zval property_parent_default_value; - ZVAL_UNDEF(&property_parent_default_value); + ZVAL_NULL(&property_parent_default_value); zend_string *property_parent_class_DDTrace_SpanStack = zend_string_init("DDTrace\\SpanStack", sizeof("DDTrace\\SpanStack")-1, 1); zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanStack, 0, MAY_BE_NULL)); zval property_active_default_value; ZVAL_NULL(&property_active_default_value); - zend_string *property_active_name = zend_string_init("active", sizeof("active") - 1, true); + zend_string *property_active_name = zend_string_init("active", sizeof("active") - 1, 1); zend_string *property_active_class_DDTrace_SpanData = zend_string_init("DDTrace\\SpanData", sizeof("DDTrace\\SpanData")-1, 1); zend_declare_typed_property(class_entry, property_active_name, &property_active_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_active_class_DDTrace_SpanData, 0, MAY_BE_NULL)); - zend_string_release_ex(property_active_name, true); + zend_string_release(property_active_name); zval property_spanCreationObservers_default_value; ZVAL_EMPTY_ARRAY(&property_spanCreationObservers_default_value); - zend_string *property_spanCreationObservers_name = zend_string_init("spanCreationObservers", sizeof("spanCreationObservers") - 1, true); + zend_string *property_spanCreationObservers_name = zend_string_init("spanCreationObservers", sizeof("spanCreationObservers") - 1, 1); zend_declare_typed_property(class_entry, property_spanCreationObservers_name, &property_spanCreationObservers_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_spanCreationObservers_name, true); + zend_string_release(property_spanCreationObservers_name); return class_entry; } @@ -891,21 +904,21 @@ static zend_class_entry *register_class_DDTrace_Integration(void) zval const_NOT_LOADED_value; ZVAL_LONG(&const_NOT_LOADED_value, DD_TRACE_INTEGRATION_NOT_LOADED); - zend_string *const_NOT_LOADED_name = zend_string_init_interned("NOT_LOADED", sizeof("NOT_LOADED") - 1, true); + zend_string *const_NOT_LOADED_name = zend_string_init_interned("NOT_LOADED", sizeof("NOT_LOADED") - 1, 1); zend_declare_class_constant_ex(class_entry, const_NOT_LOADED_name, &const_NOT_LOADED_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release_ex(const_NOT_LOADED_name, true); + zend_string_release(const_NOT_LOADED_name); zval const_LOADED_value; ZVAL_LONG(&const_LOADED_value, DD_TRACE_INTEGRATION_LOADED); - zend_string *const_LOADED_name = zend_string_init_interned("LOADED", sizeof("LOADED") - 1, true); + zend_string *const_LOADED_name = zend_string_init_interned("LOADED", sizeof("LOADED") - 1, 1); zend_declare_class_constant_ex(class_entry, const_LOADED_name, &const_LOADED_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release_ex(const_LOADED_name, true); + zend_string_release(const_LOADED_name); zval const_NOT_AVAILABLE_value; ZVAL_LONG(&const_NOT_AVAILABLE_value, DD_TRACE_INTEGRATION_NOT_AVAILABLE); - zend_string *const_NOT_AVAILABLE_name = zend_string_init_interned("NOT_AVAILABLE", sizeof("NOT_AVAILABLE") - 1, true); + zend_string *const_NOT_AVAILABLE_name = zend_string_init_interned("NOT_AVAILABLE", sizeof("NOT_AVAILABLE") - 1, 1); zend_declare_class_constant_ex(class_entry, const_NOT_AVAILABLE_name, &const_NOT_AVAILABLE_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release_ex(const_NOT_AVAILABLE_name, true); + zend_string_release(const_NOT_AVAILABLE_name); return class_entry; } diff --git a/ext/sidecar.h b/ext/sidecar.h index b3593ceaf1c..f5a8ed163a7 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -50,6 +50,9 @@ void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *t bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_string *new_str); static inline ddog_CharSlice dd_zend_string_to_CharSlice(zend_string *str) { + if (str == NULL) { + return (ddog_CharSlice){ .len = 0, .ptr = NULL }; + } return (ddog_CharSlice){ .len = str->len, .ptr = str->val }; } diff --git a/libdatadog b/libdatadog index 629bce09547..42c1895a44e 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 629bce09547abc77d7bbda623921f97eb5611949 +Subproject commit 42c1895a44e000bd4de0efd54799f66b79632bac diff --git a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php index 3e1971666bd..3319bcb33f0 100644 --- a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php +++ b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php @@ -142,6 +142,17 @@ static function ($This, $scope, $args, $route) { } $rootSpan->meta[Tag::HTTP_METHOD] = $request->method(); $rootSpan->meta[Tag::SPAN_KIND] = 'server'; + + if (!\DDTrace\are_endpoints_collected()) { + $routeCollection = $This->getRoutes(); + foreach ($routeCollection as $value) { + $path = method_exists($value, 'uri') ? $value->uri() : ''; + $methods = method_exists($value, 'methods') ? $value->methods() : []; + $method = isset($methods[0]) ? $methods[0] : 'GET'; + $resourceName = $method . ' ' . $path; + \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); + } + } } ); diff --git a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php index 0d2221f4bd8..fc0d70a8e50 100644 --- a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php +++ b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php @@ -588,6 +588,23 @@ static function(HookData $hook) use ($controllerName) { $injectedActionInfo = true; } } + + if (strpos(self::$kernel::VERSION, '4.') !== 0 && self::$frameworkPrefix === SymfonyIntegration::NAME && self::$kernel !== null && !\DDTrace\are_endpoints_collected()) + { + /** @var ContainerInterface $container */ + $container = self::$kernel->getContainer(); + /** @var \Symfony\Bundle\FrameworkBundle\Routing\Router $router */ + $router = $container->get('router'); + $routes = $router && $router->getRouteCollection() ? $router->getRouteCollection()->all() : []; + /** @var \Symfony\Component\Routing\Route $route */ + foreach ($routes as $route) { + $path = method_exists($route, 'getPath') ? $route->getPath() : ''; + $methods = method_exists($route, 'getMethods') ? $route->getMethods() : []; + $method = isset($methods[0]) ? $methods[0] : 'GET'; + $resourceName = $method . ' ' . $path; + \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); + } + } } ]; \DDTrace\trace_method( diff --git a/src/DDTrace/Integrations/WordPress/WordPressIntegration.php b/src/DDTrace/Integrations/WordPress/WordPressIntegration.php index 73085a47543..40d7d3cc814 100644 --- a/src/DDTrace/Integrations/WordPress/WordPressIntegration.php +++ b/src/DDTrace/Integrations/WordPress/WordPressIntegration.php @@ -35,6 +35,23 @@ public static function init(): int }); \DDTrace\hook_method('WP', 'main', null, function ($This, $scope, $args) { + if (class_exists('WP_Query') && !\DDTrace\are_endpoints_collected()) { + $args = array('post_type' => 'any', 'posts_per_page' => -1); + $query = new \WP_Query($args); + foreach ($query->posts as $post) { + $path = property_exists($post, 'guid') ? $post->guid : ''; + $parsed = parse_url($path); + if (isset($parsed['path'])) { + $path = $parsed['path']; + } + if (isset($parsed['query'])) { + $path .= '?' . $parsed['query']; + } + $method = 'GET'; + $resourceName = $method . ' ' . $path; + \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); + } + } if (\property_exists($This, 'did_permalink') && $This->did_permalink === true) { if ( function_exists('\datadog\appsec\push_addresses') && diff --git a/tests/Frameworks/Laravel/Version_10_x/routes/web.php b/tests/Frameworks/Laravel/Version_10_x/routes/web.php index 3549b31c425..596d13d0494 100644 --- a/tests/Frameworks/Laravel/Version_10_x/routes/web.php +++ b/tests/Frameworks/Laravel/Version_10_x/routes/web.php @@ -24,3 +24,7 @@ Route::get('login/signup', [LoginTestController::class, 'register']); Route::get('/behind_auth', [LoginTestController::class, 'behind_auth'])->name('behind_auth')->middleware('auth'); Route::get('rasp', [RaspTestController::class, 'rasp']); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); \ No newline at end of file diff --git a/tests/Frameworks/Laravel/Version_11_x/routes/web.php b/tests/Frameworks/Laravel/Version_11_x/routes/web.php index 19af850864d..802b6c0003b 100644 --- a/tests/Frameworks/Laravel/Version_11_x/routes/web.php +++ b/tests/Frameworks/Laravel/Version_11_x/routes/web.php @@ -8,3 +8,7 @@ Route::get('simple_view', [CommonSpecsController::class, 'simple_view']); Route::get('error', [CommonSpecsController::class, 'error']); Route::get('rasp', [RaspTestController::class, 'rasp']); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); \ No newline at end of file diff --git a/tests/Frameworks/Laravel/Version_4_2/app/routes.php b/tests/Frameworks/Laravel/Version_4_2/app/routes.php index 9892d2c82d9..9a2550a00a8 100644 --- a/tests/Frameworks/Laravel/Version_4_2/app/routes.php +++ b/tests/Frameworks/Laravel/Version_4_2/app/routes.php @@ -28,3 +28,7 @@ Route::get('/behind_auth', 'LoginTestController@behind_auth'); }); Route::get('rasp', 'RaspTestController@rasp'); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); \ No newline at end of file diff --git a/tests/Frameworks/Laravel/Version_5_7/routes/web.php b/tests/Frameworks/Laravel/Version_5_7/routes/web.php index 8a37bf80299..3cfc2c4ec99 100644 --- a/tests/Frameworks/Laravel/Version_5_7/routes/web.php +++ b/tests/Frameworks/Laravel/Version_5_7/routes/web.php @@ -32,3 +32,7 @@ Route::get('login/signup', 'LoginTestController@register'); Route::get('/behind_auth', 'LoginTestController@behind_auth')->middleware('auth'); Route::get('rasp', 'RaspTestController@rasp'); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); diff --git a/tests/Frameworks/Laravel/Version_5_8/routes/web.php b/tests/Frameworks/Laravel/Version_5_8/routes/web.php index a24331bb8ed..b03d393e370 100644 --- a/tests/Frameworks/Laravel/Version_5_8/routes/web.php +++ b/tests/Frameworks/Laravel/Version_5_8/routes/web.php @@ -29,3 +29,7 @@ Route::get('login/signup', 'LoginTestController@register'); Route::get('/behind_auth', 'LoginTestController@behind_auth')->middleware('auth'); Route::get('rasp', 'RaspTestController@rasp'); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); diff --git a/tests/Frameworks/Laravel/Version_8_x/routes/web.php b/tests/Frameworks/Laravel/Version_8_x/routes/web.php index cec6fdcff41..b817b4f0818 100644 --- a/tests/Frameworks/Laravel/Version_8_x/routes/web.php +++ b/tests/Frameworks/Laravel/Version_8_x/routes/web.php @@ -43,5 +43,10 @@ Route::get('/behind_auth', [LoginTestController::class, 'behind_auth'])->name('behind_auth')->middleware('auth'); Route::get('rasp', [RaspTestController::class, 'rasp']); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); + // This route has to remain unnamed so we test both route cached and not cached. Route::get('/unnamed-route', [RouteCachingController::class, 'unnamed']); diff --git a/tests/Frameworks/Laravel/Version_9_x/routes/web.php b/tests/Frameworks/Laravel/Version_9_x/routes/web.php index 042d4d66521..46f515ae111 100644 --- a/tests/Frameworks/Laravel/Version_9_x/routes/web.php +++ b/tests/Frameworks/Laravel/Version_9_x/routes/web.php @@ -23,3 +23,7 @@ Route::get('login/signup', [LoginTestController::class, 'register']); Route::get('/behind_auth', [LoginTestController::class, 'behind_auth'])->name('behind_auth')->middleware('auth'); Route::get('rasp', [RaspTestController::class, 'rasp']); +Route::get('/telemetry', function () { + dd_trace_internal_fn("finalize_telemetry"); + return response('Done'); +}); \ No newline at end of file diff --git a/tests/Frameworks/Symfony/Latest/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Latest/src/Controller/CommonScenariosController.php index 9e985726985..0b8669603fb 100644 --- a/tests/Frameworks/Symfony/Latest/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Latest/src/Controller/CommonScenariosController.php @@ -39,4 +39,11 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + #[Route('/telemetry', name: 'telemetry')] + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php index 52cb335465a..4f77d888f30 100644 --- a/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_2_3/src/AppBundle/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php index 05712d989bf..6d4f444c65b 100644 --- a/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_2_8/src/AppBundle/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_3_0/src/AppBundle/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_3_0/src/AppBundle/Controller/CommonScenariosController.php index 05712d989bf..302038deb91 100644 --- a/tests/Frameworks/Symfony/Version_3_0/src/AppBundle/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_3_0/src/AppBundle/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_3_3/src/AppBundle/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_3_3/src/AppBundle/Controller/CommonScenariosController.php index aa283202169..e26821838b6 100644 --- a/tests/Frameworks/Symfony/Version_3_3/src/AppBundle/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_3_3/src/AppBundle/Controller/CommonScenariosController.php @@ -58,4 +58,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_3_4/src/AppBundle/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_3_4/src/AppBundle/Controller/CommonScenariosController.php index 5a2d4db3169..e631d603ffa 100644 --- a/tests/Frameworks/Symfony/Version_3_4/src/AppBundle/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_3_4/src/AppBundle/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_4_0/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_4_0/src/Controller/CommonScenariosController.php index b040ae56cd7..bbecd43628a 100644 --- a/tests/Frameworks/Symfony/Version_4_0/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_4_0/src/Controller/CommonScenariosController.php @@ -44,4 +44,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_4_2/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_4_2/src/Controller/CommonScenariosController.php index 0068cb8a018..2c24406f450 100644 --- a/tests/Frameworks/Symfony/Version_4_2/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_4_2/src/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_4_4/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_4_4/src/Controller/CommonScenariosController.php index 70f0d74f8ae..cfa276beec1 100644 --- a/tests/Frameworks/Symfony/Version_4_4/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_4_4/src/Controller/CommonScenariosController.php @@ -58,4 +58,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_5_0/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_5_0/src/Controller/CommonScenariosController.php index 0068cb8a018..2c24406f450 100644 --- a/tests/Frameworks/Symfony/Version_5_0/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_5_0/src/Controller/CommonScenariosController.php @@ -48,4 +48,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_5_1/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_5_1/src/Controller/CommonScenariosController.php index 4a6cb21fa70..a919f8bb3cf 100644 --- a/tests/Frameworks/Symfony/Version_5_1/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_5_1/src/Controller/CommonScenariosController.php @@ -45,4 +45,14 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_5_2/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_5_2/src/Controller/CommonScenariosController.php index 70f0d74f8ae..cfa276beec1 100644 --- a/tests/Frameworks/Symfony/Version_5_2/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_5_2/src/Controller/CommonScenariosController.php @@ -58,4 +58,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Frameworks/Symfony/Version_6_2/src/Controller/CommonScenariosController.php b/tests/Frameworks/Symfony/Version_6_2/src/Controller/CommonScenariosController.php index 3abff314618..48ede0d4916 100644 --- a/tests/Frameworks/Symfony/Version_6_2/src/Controller/CommonScenariosController.php +++ b/tests/Frameworks/Symfony/Version_6_2/src/Controller/CommonScenariosController.php @@ -54,4 +54,13 @@ public function behindAuthAction(Request $request) // replace this example code with whatever you need return new Response('Hi!'); } + + /** + * @Route("/telemetry", name="telemetry") + */ + public function telemetryAction(Request $request) + { + dd_trace_internal_fn("finalize_telemetry"); + return new Response('Done'); + } } diff --git a/tests/Integrations/Laravel/TelemetryTestSuite.php b/tests/Integrations/Laravel/TelemetryTestSuite.php new file mode 100644 index 00000000000..5630a0a449d --- /dev/null +++ b/tests/Integrations/Laravel/TelemetryTestSuite.php @@ -0,0 +1,63 @@ + 'laravel_test_app', + 'DD_SERVICE' => 'laravel_test_app', + 'DD_TRACE_AGENT_PORT' => 80, + 'DD_AGENT_HOST' => 'request-replayer', + 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, + 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, + ]); + } + + private function readTelemetryPayloads($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $innerJson) { + if (isset($json["application"])) { + $innerJson["application"] = $json["application"]; + } + $telemetryPayloads[] = $innerJson; + } + } + } + + // Filter the payloads from the trace background sender + return array_values($telemetryPayloads); + } + + public function testAppEndpointsAreSent() + { + $this->resetRequestDumper(); + + $this->call(GetSpec::create("autoloaded", "/telemetry")); + $response = $this->retrieveDumpedData($this->untilTelemetryRequest("app-endpoints"), true); + $payloads = $this->readTelemetryPayloads($response); + $endpointsPayloads = array_values(array_filter($payloads, function ($p) { return $p["request_type"] == "app-endpoints"; })); + + $this->assertCount(1, $endpointsPayloads); + + $endpointsPayload = $endpointsPayloads[0]; + $endpoints = $endpointsPayload["payload"]["endpoints"]; + $this->assertGreaterThan(0, $endpoints); + foreach ($endpoints as $endpoint) { + $this->assertNotEmpty($endpoint["path"]); + $this->assertNotEmpty($endpoint["method"]); + $this->assertEquals("http.request", $endpoint["operation_name"]); + $this->assertEquals($endpoint["method"] . " " . $endpoint["path"], $endpoint["resource_name"]); + } + } +} \ No newline at end of file diff --git a/tests/Integrations/Laravel/V10_x/TelemetryTest.php b/tests/Integrations/Laravel/V10_x/TelemetryTest.php new file mode 100644 index 00000000000..89d08d24ed0 --- /dev/null +++ b/tests/Integrations/Laravel/V10_x/TelemetryTest.php @@ -0,0 +1,13 @@ + 'laravel_test_app', + 'DD_TRACE_DEBUG' => 'true', + 'DD_SERVICE' => 'test_symfony_latest', + 'DD_TRACE_AGENT_PORT' => 80, + 'DD_AGENT_HOST' => 'request-replayer', + 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, + 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, + ]); + } + + private function readTelemetryPayloads($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $innerJson) { + if (isset($json["application"])) { + $innerJson["application"] = $json["application"]; + } + $telemetryPayloads[] = $innerJson; + } + } + } + + // Filter the payloads from the trace background sender + return array_values($telemetryPayloads); + } + + public function testAppEndpointsAreSent() + { + if (extension_loaded('xdebug')) { + $this->markTestSkipped('Xdebug extension is loaded'); + } + + $this->resetRequestDumper(); + + $this->call(GetSpec::create("telemetry", "/telemetry")); + usleep(500000); + $response = $this->retrieveDumpedData($this->untilTelemetryRequest("app-endpoints"), true); + $payloads = $this->readTelemetryPayloads($response); + $endpointsPayloads = array_values(array_filter($payloads, function ($p) { return $p["request_type"] == "app-endpoints"; })); + + $this->assertCount(1, $endpointsPayloads); + + $endpointsPayload = $endpointsPayloads[0]; + $endpoints = $endpointsPayload["payload"]["endpoints"]; + $this->assertGreaterThan(0, $endpoints); + foreach ($endpoints as $endpoint) { + $this->assertNotEmpty($endpoint["path"]); + $this->assertNotEmpty($endpoint["method"]); + $this->assertEquals("http.request", $endpoint["operation_name"]); + $this->assertEquals($endpoint["method"] . " " . $endpoint["path"], $endpoint["resource_name"]); + } + } +} diff --git a/tests/Integrations/Symfony/TelemetryTestSuite.php b/tests/Integrations/Symfony/TelemetryTestSuite.php new file mode 100644 index 00000000000..c99decfc99c --- /dev/null +++ b/tests/Integrations/Symfony/TelemetryTestSuite.php @@ -0,0 +1,79 @@ + 'symfony_test_app', + 'DD_TRACE_DEBUG' => 'true', + 'DD_SERVICE' => 'test_symfony', + 'DD_TRACE_AGENT_PORT' => 80, + 'DD_AGENT_HOST' => 'request-replayer', + 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, + 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, + ]); + } + + private function readTelemetryPayloads($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $innerJson) { + if (isset($json["application"])) { + $innerJson["application"] = $json["application"]; + } + $telemetryPayloads[] = $innerJson; + } + } + } + + // Filter the payloads from the trace background sender + return array_values($telemetryPayloads); + } + + public function testAppEndpointsAreSent() + { + if (extension_loaded('xdebug')) { + $this->markTestSkipped('Xdebug extension is loaded'); + } + + $this->resetRequestDumper(); + + $this->call(GetSpec::create("telemetry", $this->getBase() . "/telemetry")); + + usleep(500000); + $response = $this->retrieveDumpedData($this->untilTelemetryRequest("app-endpoints"), true); + $payloads = $this->readTelemetryPayloads($response); + $endpointsPayloads = array_values(array_filter($payloads, function ($p) { return $p["request_type"] == "app-endpoints"; })); + + $this->assertCount(1, $endpointsPayloads); + + $endpointsPayload = $endpointsPayloads[0]; + $endpoints = $endpointsPayload["payload"]["endpoints"]; + $this->assertGreaterThan(0, $endpoints); + foreach ($endpoints as $endpoint) { + $this->assertNotEmpty($endpoint["path"]); + $this->assertNotEmpty($endpoint["method"]); + $this->assertEquals("http.request", $endpoint["operation_name"]); + $this->assertEquals($endpoint["method"] . " " . $endpoint["path"], $endpoint["resource_name"]); + } + } + + protected function getBase() + { + return ''; + } +} diff --git a/tests/Integrations/Symfony/V2_3/TelemetryTest.php b/tests/Integrations/Symfony/V2_3/TelemetryTest.php new file mode 100644 index 00000000000..2f9b382cab5 --- /dev/null +++ b/tests/Integrations/Symfony/V2_3/TelemetryTest.php @@ -0,0 +1,28 @@ + 'wordpress_test_app', + 'DD_TRACE_WORDPRESS_CALLBACKS' => '0', + 'DD_TRACE_MYSQLI_ENABLED' => '0', + 'DD_TRACE_AGENT_PORT' => 80, + 'DD_AGENT_HOST' => 'request-replayer', + 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, + 'DD_LOGS_INJECTION' => 'false', + 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, + ]); + } + + protected function readEndpointsTelemetry($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $innerJson) { + if (isset($innerJson["request_type"]) && $innerJson["request_type"] == "app-endpoints") { + $telemetryPayloads[] = $innerJson["payload"]["endpoints"]; + } + } + } + } + return $telemetryPayloads; + } + + protected function expectedEndpoints() { + return [ + [ + "method" => "GET", + "operation_name" => "http.request", + "path" => "/?page_id=2", + "resource_name" => "GET /?page_id=2", + ], + [ + "method" => "GET", + "operation_name" => "http.request", + "path" => "/?p=1", + "resource_name" => "GET /?p=1", + ], + [ + "method" => "GET", + "operation_name" => "http.request", + "path" => "/?p=5", + "resource_name" => "GET /?p=5", + ] + ]; + } + public function testAppEndpointsAreSent() + { + $this->call( + GetSpec::create( + 'A simple GET request returning a string', + '/simple?key=value&pwd=should_redact' + ) + ); + + $found_app_endpoints = false; + $until = function ($request) use (&$found_app_endpoints) { + if (strpos($request["body"] ?? "", "app-endpoints") !== false) { + $found_app_endpoints = true; + } + return $found_app_endpoints; + }; + $response = $this->retrieveDumpedData($until); + + $endpoints = $this->readEndpointsTelemetry($response); + $endpoints = isset($endpoints[0]) ? $endpoints[0] : []; + + $expected_endpoints = $this->expectedEndpoints(); + + $this->assertCount(count($expected_endpoints), $endpoints); + + foreach ($expected_endpoints as $expected_endpoint) { + $this->assertContains($expected_endpoint, $endpoints); + } + } +} diff --git a/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php b/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php index d91f47d2dd3..379105117ba 100644 --- a/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php +++ b/tests/Integrations/WordPress/V4_8/CommonScenariosTest.php @@ -30,9 +30,31 @@ protected static function getEnvs() 'DD_SERVICE' => 'wordpress_test_app', 'DD_TRACE_WORDPRESS_CALLBACKS' => '0', 'DD_TRACE_MYSQLI_ENABLED' => '0', + 'DD_TRACE_AGENT_PORT' => 80, + 'DD_AGENT_HOST' => 'request-replayer', + 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, + 'DD_LOGS_INJECTION' => 'false', + 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, ]); } + private function readEndpointsTelemetry($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $innerJson) { + if (isset($innerJson["request_type"]) && $innerJson["request_type"] == "app-endpoints") { + $telemetryPayloads[] = $innerJson["payload"]["endpoints"]; + } + } + } + } + return $telemetryPayloads; + } + public static function getTestedLibrary() { return 'wordpress'; @@ -45,18 +67,41 @@ protected static function getTestedVersion($testedLibrary) public function testScenarioGetReturnString() { - if (\getenv('PHPUNIT_COVERAGE')) { - $this->markTestSkipped('Test is too flaky under coverage mode'); + $this->call( + GetSpec::create( + 'A simple GET request returning a string', + '/simple?key=value&pwd=should_redact' + ) + ); + + $found_app_endpoints = false; + $until = function ($request) use (&$found_app_endpoints) { + if (strpos($request["body"] ?? "", "app-endpoints") !== false) { + $found_app_endpoints = true; + } + return $found_app_endpoints; + }; + $response = $this->retrieveDumpedData($until); + + $endpoints = $this->readEndpointsTelemetry($response); + $endpoints = isset($endpoints[0]) ? $endpoints[0] : []; + $this->assertCount(2, $endpoints); + + $first_endpoint = $endpoints[0]; + $second_endpoint = $endpoints[1]; + if ($first_endpoint['path'] !== '/?p=1') { + $first_endpoint = $endpoints[1]; + $second_endpoint = $endpoints[0]; } - $this->tracesFromWebRequestSnapshot(function () { - $this->call( - GetSpec::create( - 'A simple GET request returning a string', - '/simple?key=value&pwd=should_redact' - ) - ); - }); + $this->assertSame('/?p=1', $first_endpoint['path']); + $this->assertSame('GET', $first_endpoint['method']); + $this->assertSame('http.request', $first_endpoint['operation_name']); + $this->assertSame('GET /?p=1', $first_endpoint['resource_name']); + $this->assertSame('/?page_id=2', $second_endpoint['path']); + $this->assertSame('GET', $second_endpoint['method']); + $this->assertSame('http.request', $second_endpoint['operation_name']); + $this->assertSame('GET /?page_id=2', $second_endpoint['resource_name']); } public function testScenarioGetWithView() diff --git a/tests/Integrations/WordPress/V4_8/TelemetryTest.php b/tests/Integrations/WordPress/V4_8/TelemetryTest.php new file mode 100644 index 00000000000..bf40bea842f --- /dev/null +++ b/tests/Integrations/WordPress/V4_8/TelemetryTest.php @@ -0,0 +1,40 @@ + "GET", + "operation_name" => "http.request", + "path" => "/?p=1", + "resource_name" => "GET /?p=1", + ], + [ + "method" => "GET", + "operation_name" => "http.request", + "path" => "/?page_id=2", + "resource_name" => "GET /?page_id=2", + ] + ]; + } +} diff --git a/tests/Integrations/WordPress/V5_5/TelemetryTest.php b/tests/Integrations/WordPress/V5_5/TelemetryTest.php new file mode 100644 index 00000000000..763cb6639f4 --- /dev/null +++ b/tests/Integrations/WordPress/V5_5/TelemetryTest.php @@ -0,0 +1,23 @@ +