From 8ef525cd9b2b58f0805b74137bcb78a1969ceaf7 Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 11:55:09 +0200 Subject: [PATCH 1/7] Implement Go thread context sharing endpoint --- manifests/golang.yml | 5 ++- .../build/docker/golang/app/net-http/main.go | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/manifests/golang.yml b/manifests/golang.yml index 8dc6af08cc7..4c598060fb2 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1262,7 +1262,10 @@ manifest: tests/auto_inject/test_auto_inject_install.py::TestContainerAutoInjectInstallScriptAppsec: v2.0.0 tests/auto_inject/test_auto_inject_install.py::TestHostAutoInjectInstallScriptAppsec: v2.0.0 tests/auto_inject/test_auto_inject_install.py::TestSimpleInstallerAutoInjectManualAppsec: v2.0.0 - tests/cws/test_thread_context_sharing.py::Test_ThreadContextSharing: missing_feature (missing /security/thread_context_sharing endpoint on weblog) + tests/cws/test_thread_context_sharing.py::Test_ThreadContextSharing: + - weblog_declaration: + "*": missing_feature (missing /security/thread_context_sharing endpoint on weblog) + net-http: v2.11.0-dev tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Line_Capture_Expressions: v2.2.3 tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Method_Capture_Expressions: v2.2.3 tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Method_Capture_Expressions::test_complex_capture_expressions: missing_feature (index expression not yet supported in Go system-probe) diff --git a/utils/build/docker/golang/app/net-http/main.go b/utils/build/docker/golang/app/net-http/main.go index 343fce50cd9..809c09bb3d8 100644 --- a/utils/build/docker/golang/app/net-http/main.go +++ b/utils/build/docker/golang/app/net-http/main.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "math/big" "math/rand" "net/http" "net/http/httptest" @@ -195,6 +196,46 @@ func main() { mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Query().Get("path") + + span, ok := tracer.SpanFromContext(r.Context()) + if !ok { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if err := os.WriteFile(path, []byte("system-tests thread context sharing"), 0o644); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + // SpanContext.TraceID() returns the full 128-bit trace id as a hex string; + // convert it to decimal to match the other weblogs' response format. + traceID, ok := new(big.Int).SetString(span.Context().TraceID(), 16) + if !ok { + w.WriteHeader(http.StatusInternalServerError) + return + } + + jsonResponse, err := json.Marshal(struct { + TraceID string `json:"trace_id"` + SpanID string `json:"span_id"` + }{ + TraceID: traceID.String(), + SpanID: strconv.FormatUint(span.Context().SpanID(), 10), + }) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(jsonResponse); err != nil { + return + } + }) + mux.HandleFunc("/make_distant_call", func(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") if url == "" { From b599ebe19159c415c3eb4d67bef85b0c0105ce66 Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 13:43:18 +0200 Subject: [PATCH 2/7] Share Go thread context handler --- .../_shared/common/thread_context_sharing.go | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 utils/build/docker/golang/app/_shared/common/thread_context_sharing.go diff --git a/utils/build/docker/golang/app/_shared/common/thread_context_sharing.go b/utils/build/docker/golang/app/_shared/common/thread_context_sharing.go new file mode 100644 index 00000000000..31b22f39105 --- /dev/null +++ b/utils/build/docker/golang/app/_shared/common/thread_context_sharing.go @@ -0,0 +1,50 @@ +package common + +import ( + "encoding/json" + "math/big" + "net/http" + "os" + "strconv" + + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" +) + +func ThreadContextSharing(w http.ResponseWriter, r *http.Request) { + span, ok := tracer.SpanFromContext(r.Context()) + if !ok { + w.WriteHeader(http.StatusInternalServerError) + return + } + + path := r.URL.Query().Get("path") + if err := os.WriteFile(path, []byte("system-tests thread context sharing"), 0o644); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + // SpanContext.TraceID() returns the full 128-bit trace id as a hex string; + // convert it to decimal to match the other weblogs' response format. + traceID, ok := new(big.Int).SetString(span.Context().TraceID(), 16) + if !ok { + w.WriteHeader(http.StatusInternalServerError) + return + } + + jsonResponse, err := json.Marshal(struct { + TraceID string `json:"trace_id"` + SpanID string `json:"span_id"` + }{ + TraceID: traceID.String(), + SpanID: strconv.FormatUint(span.Context().SpanID(), 10), + }) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(jsonResponse); err != nil { + return + } +} From c11b196560d6e258eec36f30f99ffb651d3fa72d Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 13:43:18 +0200 Subject: [PATCH 3/7] Register thread context handler in HTTP weblogs --- .../golang/app/net-http-orchestrion/main.go | 1 + .../golang/app/net-http-span-pool/main.go | 1 + .../build/docker/golang/app/net-http/main.go | 42 +------------------ 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/utils/build/docker/golang/app/net-http-orchestrion/main.go b/utils/build/docker/golang/app/net-http-orchestrion/main.go index e433e59a347..b9cda1a1f41 100644 --- a/utils/build/docker/golang/app/net-http-orchestrion/main.go +++ b/utils/build/docker/golang/app/net-http-orchestrion/main.go @@ -146,6 +146,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/make_distant_call", func(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") diff --git a/utils/build/docker/golang/app/net-http-span-pool/main.go b/utils/build/docker/golang/app/net-http-span-pool/main.go index f4ec9ff4835..f99de5abc05 100644 --- a/utils/build/docker/golang/app/net-http-span-pool/main.go +++ b/utils/build/docker/golang/app/net-http-span-pool/main.go @@ -189,6 +189,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/make_distant_call", func(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") diff --git a/utils/build/docker/golang/app/net-http/main.go b/utils/build/docker/golang/app/net-http/main.go index 809c09bb3d8..a802fe3ccfb 100644 --- a/utils/build/docker/golang/app/net-http/main.go +++ b/utils/build/docker/golang/app/net-http/main.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "math/big" "math/rand" "net/http" "net/http/httptest" @@ -195,46 +194,7 @@ func main() { mux.HandleFunc("/spawn_child", common.SpawnChild) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) - - mux.HandleFunc("/security/thread_context_sharing", func(w http.ResponseWriter, r *http.Request) { - path := r.URL.Query().Get("path") - - span, ok := tracer.SpanFromContext(r.Context()) - if !ok { - w.WriteHeader(http.StatusInternalServerError) - return - } - - if err := os.WriteFile(path, []byte("system-tests thread context sharing"), 0o644); err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - // SpanContext.TraceID() returns the full 128-bit trace id as a hex string; - // convert it to decimal to match the other weblogs' response format. - traceID, ok := new(big.Int).SetString(span.Context().TraceID(), 16) - if !ok { - w.WriteHeader(http.StatusInternalServerError) - return - } - - jsonResponse, err := json.Marshal(struct { - TraceID string `json:"trace_id"` - SpanID string `json:"span_id"` - }{ - TraceID: traceID.String(), - SpanID: strconv.FormatUint(span.Context().SpanID(), 10), - }) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(jsonResponse); err != nil { - return - } - }) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/make_distant_call", func(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") From 7a72b3c4e0d3040214f942fdd5a173b71be20dda Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 13:43:18 +0200 Subject: [PATCH 4/7] Register thread context handler in framework weblogs --- utils/build/docker/golang/app/chi/main.go | 1 + utils/build/docker/golang/app/echo/main.go | 1 + utils/build/docker/golang/app/gin/main.go | 1 + 3 files changed, 3 insertions(+) diff --git a/utils/build/docker/golang/app/chi/main.go b/utils/build/docker/golang/app/chi/main.go index 052b190b3a5..b85532021b5 100644 --- a/utils/build/docker/golang/app/chi/main.go +++ b/utils/build/docker/golang/app/chi/main.go @@ -156,6 +156,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/make_distant_call", func(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") diff --git a/utils/build/docker/golang/app/echo/main.go b/utils/build/docker/golang/app/echo/main.go index e7db727b2fc..631076f9a44 100644 --- a/utils/build/docker/golang/app/echo/main.go +++ b/utils/build/docker/golang/app/echo/main.go @@ -168,6 +168,7 @@ func main() { }) r.Any("/trace/manual_keep_drop", echoHandleFunc(common.ManualKeepDrop)) + r.Any("/security/thread_context_sharing", echoHandleFunc(common.ThreadContextSharing)) r.Any("/make_distant_call", func(c echo.Context) error { url := c.Request().URL.Query().Get("url") diff --git a/utils/build/docker/golang/app/gin/main.go b/utils/build/docker/golang/app/gin/main.go index 01c669a1aee..1b8b3e4a913 100644 --- a/utils/build/docker/golang/app/gin/main.go +++ b/utils/build/docker/golang/app/gin/main.go @@ -158,6 +158,7 @@ func main() { }) r.Any("/trace/manual_keep_drop", ginHandleFunc(common.ManualKeepDrop)) + r.Any("/security/thread_context_sharing", ginHandleFunc(common.ThreadContextSharing)) r.Any("/make_distant_call", func(ctx *gin.Context) { url := ctx.Request.URL.Query().Get("url") From 100fae4a4cddc76a7ff06cfad3ac933ffe0c0486 Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 13:43:19 +0200 Subject: [PATCH 5/7] Register thread context handler in GraphQL weblogs --- utils/build/docker/golang/app/gqlgen/server.go | 1 + utils/build/docker/golang/app/graph-gophers/main.go | 1 + utils/build/docker/golang/app/graphql-go/main.go | 1 + 3 files changed, 3 insertions(+) diff --git a/utils/build/docker/golang/app/gqlgen/server.go b/utils/build/docker/golang/app/gqlgen/server.go index 2ceed64f89f..45692694115 100644 --- a/utils/build/docker/golang/app/gqlgen/server.go +++ b/utils/build/docker/golang/app/gqlgen/server.go @@ -49,6 +49,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) { diff --git a/utils/build/docker/golang/app/graph-gophers/main.go b/utils/build/docker/golang/app/graph-gophers/main.go index e701167648a..7f50580cde2 100644 --- a/utils/build/docker/golang/app/graph-gophers/main.go +++ b/utils/build/docker/golang/app/graph-gophers/main.go @@ -62,6 +62,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) { diff --git a/utils/build/docker/golang/app/graphql-go/main.go b/utils/build/docker/golang/app/graphql-go/main.go index 55a62a7e2fc..7f1ff17fe92 100644 --- a/utils/build/docker/golang/app/graphql-go/main.go +++ b/utils/build/docker/golang/app/graphql-go/main.go @@ -90,6 +90,7 @@ func main() { }) mux.HandleFunc("/trace/manual_keep_drop", common.ManualKeepDrop) + mux.HandleFunc("/security/thread_context_sharing", common.ThreadContextSharing) mux.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) { From 07df5d2d4c7794781caffb7ac8b907f826c180af Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 13:43:19 +0200 Subject: [PATCH 6/7] Enable Go thread context sharing across weblogs --- manifests/golang.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/manifests/golang.yml b/manifests/golang.yml index 4c598060fb2..41b9806a673 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1262,10 +1262,7 @@ manifest: tests/auto_inject/test_auto_inject_install.py::TestContainerAutoInjectInstallScriptAppsec: v2.0.0 tests/auto_inject/test_auto_inject_install.py::TestHostAutoInjectInstallScriptAppsec: v2.0.0 tests/auto_inject/test_auto_inject_install.py::TestSimpleInstallerAutoInjectManualAppsec: v2.0.0 - tests/cws/test_thread_context_sharing.py::Test_ThreadContextSharing: - - weblog_declaration: - "*": missing_feature (missing /security/thread_context_sharing endpoint on weblog) - net-http: v2.11.0-dev + tests/cws/test_thread_context_sharing.py::Test_ThreadContextSharing: v2.11.0-dev tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Line_Capture_Expressions: v2.2.3 tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Method_Capture_Expressions: v2.2.3 tests/debugger/test_debugger_capture_expressions.py::Test_Debugger_Method_Capture_Expressions::test_complex_capture_expressions: missing_feature (index expression not yet supported in Go system-probe) From 7041f306bfb8e3e28bf9cfca247c1971b209cadf Mon Sep 17 00:00:00 2001 From: Eliott B Date: Mon, 14 Sep 2026 14:03:07 +0200 Subject: [PATCH 7/7] Select GraphQL weblogs for thread context sharing --- utils/build/docker/golang/weblog_metadata.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/build/docker/golang/weblog_metadata.yml b/utils/build/docker/golang/weblog_metadata.yml index 51f16fe6e1d..3538e596a7b 100644 --- a/utils/build/docker/golang/weblog_metadata.yml +++ b/utils/build/docker/golang/weblog_metadata.yml @@ -1,3 +1,4 @@ +--- # docs/understand/weblogs/weblog-metadata.md haproxy: @@ -11,12 +12,15 @@ apim: supported_scenarios: [APPSEC_BLOCKING, DEFAULT] graphql-go: categories: [dd_trace_graphql] + supported_scenarios: [THREAD_CONTEXT_SHARING] excluded_scenarios: [GRAPHQL_ERROR_TRACKING] # why ??? gqlgen: categories: [dd_trace_graphql] + supported_scenarios: [THREAD_CONTEXT_SHARING] excluded_scenarios: [GRAPHQL_ERROR_TRACKING] # why ??? graph-gophers: categories: [dd_trace_graphql] + supported_scenarios: [THREAD_CONTEXT_SHARING] excluded_scenarios: [GRAPHQL_ERROR_TRACKING] # why ??? echo: categories: [dd_trace]