diff --git a/Cargo.lock b/Cargo.lock index 7cc464f0ad9..666f097ac1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,7 +57,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -317,7 +317,7 @@ checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", - "libc 0.2.173", + "libc 0.2.177", "miniz_oxide", "object 0.36.7", "rustc-demangle", @@ -400,7 +400,7 @@ checksum = "95824d1dd4f20b4a4dfa63b72954e81914a718357231468180b30314e85057fa" dependencies = [ "cpp_demangle", "gimli 0.32.0", - "libc 0.2.173", + "libc 0.2.177", "memmap2", "miniz_oxide", "rustc-demangle", @@ -641,7 +641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "jobserver", - "libc 0.2.173", + "libc 0.2.177", "shlex", ] @@ -723,7 +723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", - "libc 0.2.173", + "libc 0.2.177", "libloading", ] @@ -885,7 +885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -909,7 +909,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "winapi 0.3.9", ] @@ -919,7 +919,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -1108,13 +1108,14 @@ dependencies = [ "clap", "criterion", "datadog-ddsketch", + "datadog-log", "datadog-trace-protobuf", + "datadog-trace-stats", "datadog-trace-utils", "ddcommon 0.0.1", "ddtelemetry", "dogstatsd-client", "either", - "hashbrown 0.15.2", "http", "http-body-util", "httpmock", @@ -1140,7 +1141,7 @@ version = "21.0.0" source = "git+https://github.com/DataDog/libdatadog?rev=0b59f64c4fc08105e5b73c5a0752ced3cf8f653e#0b59f64c4fc08105e5b73c5a0752ced3cf8f653e" dependencies = [ "allocator-api2", - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.52.0", ] @@ -1159,7 +1160,7 @@ dependencies = [ "ddtelemetry", "goblin", "http", - "libc 0.2.173", + "libc 0.2.177", "nix", "num-derive", "num-traits", @@ -1189,7 +1190,7 @@ dependencies = [ "ddcommon 0.0.1", "ddcommon-ffi 0.0.1", "function_name", - "libc 0.2.173", + "libc 0.2.177", "serde", "serde_json", "symbolic-common", @@ -1219,7 +1220,7 @@ dependencies = [ "futures", "glibc_version", "io-lifetimes", - "libc 0.2.173", + "libc 0.2.177", "memfd", "nix", "page_size", @@ -1338,6 +1339,18 @@ dependencies = [ "uuid", ] +[[package]] +name = "datadog-log" +version = "0.0.1" +dependencies = [ + "chrono", + "ddcommon-ffi 0.0.1", + "tempfile", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "datadog-php-profiling" version = "0.0.0" @@ -1360,7 +1373,7 @@ dependencies = [ "ddcommon 21.0.0", "env_logger 0.11.6", "lazy_static", - "libc 0.2.173", + "libc 0.2.177", "log", "once_cell", "perfcnt", @@ -1469,7 +1482,7 @@ dependencies = [ "http-body-util", "httpmock", "hyper", - "libc 0.2.173", + "libc 0.2.177", "manual_future", "memory-stats", "microseh", @@ -1513,9 +1526,10 @@ dependencies = [ "ddtelemetry-ffi", "dogstatsd-client", "http", - "libc 0.2.173", + "libc 0.2.177", "paste", "rmp-serde", + "serde_json", "tempfile", "tinybytes", "tracing", @@ -1553,6 +1567,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "datadog-trace-stats" +version = "0.0.1" +dependencies = [ + "criterion", + "datadog-ddsketch", + "datadog-trace-protobuf", + "datadog-trace-utils", + "hashbrown 0.15.2", + "rand 0.8.5", +] + [[package]] name = "datadog-trace-utils" version = "0.0.1" @@ -1606,7 +1632,7 @@ dependencies = [ "hyper-rustls", "hyper-util", "indexmap 2.7.1", - "libc 0.2.173", + "libc 0.2.177", "maplit", "nix", "pin-project", @@ -1641,7 +1667,7 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "libc 0.2.173", + "libc 0.2.177", "nix", "pin-project", "regex", @@ -1692,12 +1718,14 @@ dependencies = [ "base64 0.22.1", "datadog-ddsketch", "ddcommon 0.0.1", + "ddcommon-ffi 0.0.1", "futures", "hashbrown 0.15.2", "http", "http-body-util", "hyper", "hyper-util", + "libc 0.2.177", "serde", "serde_json", "sys-info", @@ -1706,6 +1734,7 @@ dependencies = [ "tracing", "tracing-subscriber", "uuid", + "winver", ] [[package]] @@ -1717,7 +1746,7 @@ dependencies = [ "ddcommon-ffi 0.0.1", "ddtelemetry", "function_name", - "libc 0.2.173", + "libc 0.2.177", "paste", "tempfile", ] @@ -1748,7 +1777,7 @@ dependencies = [ "http", "itertools 0.11.0", "lazy_static", - "libc 0.2.173", + "libc 0.2.177", "log", "paste", "regex", @@ -1923,7 +1952,7 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.59.0", ] @@ -2162,7 +2191,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", - "libc 0.2.173", + "libc 0.2.177", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2174,7 +2203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", - "libc 0.2.173", + "libc 0.2.177", "r-efi", "wasi 0.14.2+wasi-0.2.4", ] @@ -2549,7 +2578,7 @@ dependencies = [ "http", "http-body", "hyper", - "libc 0.2.173", + "libc 0.2.177", "pin-project-lite", "socket2", "tokio", @@ -2760,7 +2789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.9", - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.48.0", ] @@ -2771,7 +2800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi 0.4.0", - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.52.0", ] @@ -2820,7 +2849,7 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -2863,9 +2892,9 @@ checksum = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" [[package]] name = "libc" -version = "0.2.173" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" @@ -2971,7 +3000,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -2989,7 +3018,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c73f5c649995a115e1a0220b35e4df0a1294500477f97a91d0660fb5abeb574a" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.52.0", ] @@ -3000,7 +3029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26b2a7c5ccfb370edd57fda423f3a551516ee127e10bc22a6215e8c63b20a38" dependencies = [ "cc", - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.42.0", ] @@ -3042,7 +3071,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -3081,7 +3110,7 @@ dependencies = [ "bitflags 2.8.0", "cfg-if", "cfg_aliases", - "libc 0.2.173", + "libc 0.2.177", "memoffset", ] @@ -3168,7 +3197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi 0.3.9", - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -3286,7 +3315,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "winapi 0.3.9", ] @@ -3313,7 +3342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", - "libc 0.2.173", + "libc 0.2.177", "redox_syscall", "smallvec", "windows-targets 0.52.6", @@ -3347,7 +3376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ba1fd955270ca6f8bd8624ec0c4ee1a251dd3cc0cc18e1e2665ca8f5acb1501" dependencies = [ "bitflags 1.3.2", - "libc 0.2.173", + "libc 0.2.177", "mmap", "nom 4.2.3", "x86", @@ -3512,7 +3541,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" dependencies = [ - "libc 0.2.173", + "libc 0.1.12", "nix", ] @@ -3742,7 +3771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ "fuchsia-cprng", - "libc 0.2.173", + "libc 0.2.177", "rand_core 0.3.1", "rdrand", "winapi 0.3.9", @@ -3754,7 +3783,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -3980,7 +4009,7 @@ dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", - "libc 0.2.173", + "libc 0.2.177", "untrusted", "windows-sys 0.52.0", ] @@ -3991,7 +4020,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8a29d87a652dc4d43c586328706bb5cdff211f3f39a530f240b53f7221dab8e" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -4055,7 +4084,7 @@ checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags 2.8.0", "errno", - "libc 0.2.173", + "libc 0.2.177", "linux-raw-sys", "windows-sys 0.59.0", ] @@ -4205,7 +4234,7 @@ dependencies = [ "bitflags 2.8.0", "core-foundation", "core-foundation-sys", - "libc 0.2.173", + "libc 0.2.177", "security-framework-sys", ] @@ -4216,7 +4245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -4234,7 +4263,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604b71b8fc267e13bb3023a2c901126c8f349393666a6d98ac1ae5729b701798" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "tokio", ] @@ -4402,7 +4431,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -4465,7 +4494,7 @@ version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ - "libc 0.2.173", + "libc 0.2.177", "windows-sys 0.52.0", ] @@ -4478,6 +4507,7 @@ dependencies = [ "fastrand", "io-lifetimes", "kernel32-sys", + "libc 0.2.177", "memfd", "nix", "rlimit", @@ -4586,7 +4616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" dependencies = [ "cc", - "libc 0.2.173", + "libc 0.2.177", ] [[package]] @@ -4849,7 +4879,7 @@ checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", - "libc 0.2.173", + "libc 0.2.177", "mio", "parking_lot", "pin-project-lite", @@ -5057,6 +5087,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror 1.0.69", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.28" @@ -5102,6 +5144,16 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -5112,12 +5164,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -5440,6 +5495,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows" version = "0.51.1" @@ -5824,6 +5888,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winver" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e0e7162b9e282fd75a0a832cce93994bdb21208d848a418cd05a5fdd9b9ab33" +dependencies = [ + "windows 0.48.0", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 7a2a3088f84..d17494d2fd2 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -312,6 +312,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', @@ -334,17 +335,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..5d069670040 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) { + 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 + } + + public static List waitForAppEndpoints(AppSecContainer container, int timeoutSec, Closure cl) { + waitForTelemetryData(container, timeoutSec, cl, AppEndpoints) + } + + 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/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 34588fad71d..083c8f9f2ba 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -430,17 +430,27 @@ typedef struct ddog_SidecarTransport ddog_SidecarTransport; * Holds the raw parts of a Rust Vec; it should only be created from Rust, * never from C. */ -typedef struct ddog_Vec_CChar { - const char *ptr; +typedef struct ddog_Vec_CharSlice { + const ddog_CharSlice *ptr; uintptr_t len; uintptr_t capacity; -} ddog_Vec_CChar; +} ddog_Vec_CharSlice; typedef struct ddog_Tag { ddog_CharSlice name; const struct ddog_DslString *value; } ddog_Tag; +/** + * Holds the raw parts of a Rust Vec; it should only be created from Rust, + * never from C. + */ +typedef struct ddog_Vec_CChar { + const char *ptr; + uintptr_t len; + uintptr_t capacity; +} ddog_Vec_CChar; + typedef struct ddog_Vec_CChar *(*ddog_DynamicConfigUpdate)(ddog_CharSlice config, ddog_CharSlice value, bool return_old); @@ -966,6 +976,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 struct ddog_AgentInfoReader ddog_AgentInfoReader; typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader; diff --git a/components-rs/crashtracker.h b/components-rs/crashtracker.h index 04ff4e1ec23..e2fc9e6cfc5 100644 --- a/components-rs/crashtracker.h +++ b/components-rs/crashtracker.h @@ -410,6 +410,16 @@ DDOG_CHECK_RETURN struct ddog_VoidResult ddog_crasht_CrashInfo_resolve_names(struct ddog_crasht_Handle_CrashInfo *crash_info, uint32_t pid); +/** + * # Safety + * The `crash_info` can be null, but if non-null it must point to a Builder made by this module, + * which has not previously been dropped. + * This function will: + */ +DDOG_CHECK_RETURN +struct ddog_VoidResult ddog_crasht_CrashInfo_enrich_callstacks(struct ddog_crasht_Handle_CrashInfo *crash_info, + uint32_t pid); + /** * # Safety * The `crash_info` can be null, but if non-null it must point to a Builder made by this module, diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 6b7523e9571..b6c283a2801 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -72,7 +72,7 @@ bool ddog_type_can_be_instrumented(const struct ddog_RemoteConfigState *remote_c bool ddog_global_log_probe_limiter_inc(const struct ddog_RemoteConfigState *remote_config); -struct ddog_Vec_CChar *ddog_CharSlice_to_owned(ddog_CharSlice str); +struct ddog_Vec_CharSlice *ddog_CharSlice_to_owned(ddog_CharSlice str); bool ddog_remote_configs_service_env_change(struct ddog_RemoteConfigState *remote_config, ddog_CharSlice service, @@ -182,6 +182,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)(struct _zend_string*), void (*addref_func)(struct _zend_string*)); diff --git a/components-rs/remote_config.rs b/components-rs/remote_config.rs index 9b2eaddb3b7..09add4b3b63 100644 --- a/components-rs/remote_config.rs +++ b/components-rs/remote_config.rs @@ -423,8 +423,9 @@ pub extern "C" fn ddog_global_log_probe_limiter_inc(remote_config: &RemoteConfig } #[no_mangle] -pub unsafe extern "C" fn ddog_CharSlice_to_owned(str: CharSlice) -> *mut Vec { - Box::into_raw(Box::new(str.as_slice().into())) +pub unsafe extern "C" fn ddog_CharSlice_to_owned(str: CharSlice) -> *mut ddcommon_ffi::Vec { + let std_vec: Vec = vec![str]; + Box::into_raw(Box::new(std_vec.into())) } #[no_mangle] diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 879f397b3f4..dcc5ca7bb4e 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -124,6 +124,17 @@ ddog_MaybeError ddog_sidecar_telemetry_enqueueConfig(struct ddog_SidecarTranspor enum ddog_ConfigurationOrigin origin, ddog_CharSlice config_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.rs b/components-rs/telemetry.rs index d86e6a26aea..3ec61e180b7 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,6 +237,7 @@ pub struct ShmCache { pub config_sent: bool, pub integrations: HashSet, pub composer_paths: HashSet, + pub last_endpoints_push: SystemTime, pub reader: OneWayShmReader, } @@ -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 1f2e65d822a..010a8c9eeeb 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -2756,6 +2756,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 5b9e8ae426d..444b3186e4a 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 6568fb09526..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: ce1dae7089d7feaf54d05157905aa9605b5f9d85 */ + * 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) 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 0b59f64c4fc..436bbdf55e8 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 0b59f64c4fc08105e5b73c5a0752ced3cf8f653e +Subproject commit 436bbdf55e8f3de2b657eacde57252dede508d32 diff --git a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php index 0a65815d21e..1251da9ee9e 100644 --- a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php +++ b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php @@ -142,6 +142,16 @@ 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 = $value->uri; + $method = $value->methods[0] ?? ''; + $resourceName = $method . ' ' . $path; + \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); + } + } } );