Skip to content

Commit 7b8a6ad

Browse files
authored
Add DD_RUM_* env var support for RUM configuration (#304)
* Add DD_RUM_* environment variable support for RUM configuration Add support for configuring RUM via DD_RUM_* environment variables as fallback defaults. Nginx `datadog_rum_config` directive values always take precedence over env vars on a per-field basis. When no directive exists and valid env vars are present, RUM auto-enables. Environment variables: DD_RUM_ENABLED, DD_RUM_APPLICATION_ID, DD_RUM_CLIENT_TOKEN, DD_RUM_SITE, DD_RUM_SERVICE, DD_RUM_ENV, DD_RUM_VERSION, DD_RUM_SESSION_SAMPLE_RATE, DD_RUM_SESSION_REPLAY_SAMPLE_RATE, DD_RUM_TRACK_RESOURCES, DD_RUM_TRACK_LONG_TASKS, DD_RUM_TRACK_USER_INTERACTIONS, DD_RUM_REMOTE_CONFIGURATION_ID. Precedence rules: - `datadog_rum on/off` always wins over DD_RUM_ENABLED - `datadog_rum_config` fields always win over DD_RUM_* env vars - DD_RUM_ENABLED=false explicitly disables even with config env vars - Unrecognized DD_RUM_ENABLED values log a warning and skip auto-enable Also includes: - Case-insensitive parse_bool() for boolean env var parsing - Fix std::stod crash with std::strtod for sample rate parsing - Fix snippet memory leak on config error path - Guard against rum_enable=1 with null snippet (prevents segfault) - Null-terminated error messages via ngx_pcalloc - Empty values vector guard in make_rum_json_config - Use FetchContent for inject-browser-sdk dependency - RUM integration tests and C++ unit tests - RUM CI pipeline in GitLab * Make rum_env_mappings constexpr and rename DD_RUM_VERSION to DD_RUM_MAJOR_VERSION Move rum_env_mappings definition into config_internal.h as an inline constexpr std::to_array, eliminating the separate rum_env_mappings_size variable in favor of .size(). Rename DD_RUM_VERSION to DD_RUM_MAJOR_VERSION. * Use range-based for with structured bindings for rum_env_mappings loops Replace index-based iteration with range-based for and structured bindings. Also simplify std::getenv call to use string_view::data() directly instead of constructing a temporary std::string. * Extract conf_err() helper to combine error buffer allocation and formatting Replace repeated alloc_err_buf + ngx_snprintf pairs with a single conf_err() variadic template. Also fix "Rust SDK" comment to "RUM SDK" and use static_cast for C-style casts. * Name magic number 5 as default_rum_config_version constant * Use RAII for Snippet lifetime and minor cleanups Wrap snippet_create_from_json in unique_ptr with snippet_cleanup deleter to ensure cleanup on all paths including exceptions. Also rename catch variable from 'e' to 'exception'. * Extract try_build_snippet_from_env() from merge_loc_config Reduce nesting by moving the env-based snippet construction into its own function with early returns. * Extract resolve_rum_enable_from_env() from merge_loc_config Move the DD_RUM_ENABLED resolution logic into its own function with early returns to reduce nesting. * Rename environment_variable_names to get_environment_variable_names Use a verb prefix for the rum namespace function, and simplify bool-to-ngx_flag_t conversion with static_cast. * Fix env var name mismatch in tests and UB in conf_err call Update tests and docker-compose to use DD_RUM_ENVIRONMENT and DD_RUM_MAJOR_VERSION matching the mapping table. Convert string_view to std::string before passing to variadic ngx_snprintf to avoid UB. * Improve error handling and fix silent failures in RUM config - Add NULL check in conf_err for ngx_pcalloc failure - Log warning when json.empty() after env vars were read - Rethrow std::bad_alloc instead of swallowing it; fix misleading catch message - Pass unrecognized boolean values as strings to RUM SDK instead of silently defaulting to false - Refactor resolve_rum_enable_from_env to read getenv once * Fix RUM build: reinterpret_cast for u_char* and add string_view_literals - Use reinterpret_cast instead of static_cast for char* to u_char* conversion in conf_err (clang rejects static_cast between unrelated types) - Add `using namespace std::string_view_literals` so the `sv` suffix is available for the DD_RUM_ENABLED string literal * Fix GHA build: allow overriding base image to bypass internal registry mirror The GitHub Actions runners have registry.ddbuild.io configured as a Docker mirror, but can't reach it. Parametrize the base image via ARG so GHA can use the public alpine:3.20.3 instead. * Address GitLab CI review comments: reorder and expand RUM jobs - Move .build-nginx-rum template after .build-nginx in common.yml - Group build-nginx-rum-fast with other build jobs in build-and-test-fast.yml - Expand RUM version matrix to cover all major versions (1.24, 1.25, 1.26, 1.27, 1.28, 1.29) - Move build-nginx-rum-all/test-nginx-rum-all inline with other jobs in build-and-test-all.yml - Add missing 1.25.5 and 1.27.5 to the all-versions RUM matrix * Address C++ review comments in rum/config.cpp - Rename single-letter loop variable `e` to `entry` - Extract duplicated tag-setting logic into apply_rum_config_tags() helper * Address review comments: nits and consistency fixes - Makefile: add space before $(if for readability - config_internal.h: add rum_config_map type alias; use in signatures - config.cpp: use values.front() instead of values[0] throughout - config.cpp: fix misleading version error message (vN not v5) - test_rum_config.cpp: group rapidjson include with other third-party includes - test_injection.py: replace bare assert with self.assertXxx() for consistency * Fix: restore arg_values[0] for ngx_str_t* pointer (not a vector)
1 parent 697ea42 commit 7b8a6ad

25 files changed

Lines changed: 1374 additions & 152 deletions

.github/workflows/system-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
# - https://github.com/DataDog/system-tests/blob/main/utils/scripts/load-binary.sh
2020
NGINX_VERSION: 1.29.5
2121
WAF: ON
22+
BASE_IMAGE: alpine:3.20.3
2223
steps:
2324
- name: checkout
2425
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0

.gitlab/build-and-test-all.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ build-nginx-all:
5454
- "1.29.5"
5555
WAF: ["ON", "OFF"]
5656

57+
build-nginx-rum-all:
58+
extends:
59+
- .build-all
60+
- .build-nginx-rum
61+
parallel:
62+
matrix:
63+
- ARCH: ["amd64", "arm64"]
64+
NGINX_VERSION:
65+
- "1.24.0"
66+
- "1.25.5"
67+
- "1.26.3"
68+
- "1.27.5"
69+
- "1.28.2"
70+
- "1.29.5"
71+
WAF: ["OFF"]
72+
5773
build-ingress-nginx-all:
5874
extends:
5975
- .build-all
@@ -237,6 +253,37 @@ test-nginx-all-bis:
237253
NGINX_VERSION: ["1.24.0"]
238254
WAF: ["ON", "OFF"]
239255

256+
test-nginx-rum-all:
257+
extends:
258+
- .test-all
259+
- .test-nginx-rum
260+
parallel:
261+
matrix:
262+
- ARCH: ["amd64", "arm64"]
263+
BASE_IMAGE: ["nginx:1.24.0"]
264+
NGINX_VERSION: ["1.24.0"]
265+
WAF: ["OFF"]
266+
- ARCH: ["amd64", "arm64"]
267+
BASE_IMAGE: ["nginx:1.25.5"]
268+
NGINX_VERSION: ["1.25.5"]
269+
WAF: ["OFF"]
270+
- ARCH: ["amd64", "arm64"]
271+
BASE_IMAGE: ["nginx:1.26.3"]
272+
NGINX_VERSION: ["1.26.3"]
273+
WAF: ["OFF"]
274+
- ARCH: ["amd64", "arm64"]
275+
BASE_IMAGE: ["nginx:1.27.5"]
276+
NGINX_VERSION: ["1.27.5"]
277+
WAF: ["OFF"]
278+
- ARCH: ["amd64", "arm64"]
279+
BASE_IMAGE: ["nginx:1.28.2"]
280+
NGINX_VERSION: ["1.28.2"]
281+
WAF: ["OFF"]
282+
- ARCH: ["amd64", "arm64"]
283+
BASE_IMAGE: ["nginx:1.29.5"]
284+
NGINX_VERSION: ["1.29.5"]
285+
WAF: ["OFF"]
286+
240287
test-ingress-nginx-all:
241288
extends:
242289
- .test-all
@@ -296,3 +343,4 @@ test-openresty-all:
296343
- "1.27.1.1"
297344
- "1.27.1.2"
298345
WAF: ["ON", "OFF"]
346+

.gitlab/build-and-test-fast.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ build-openresty-fast:
6262
RESTY_VERSION: ["1.27.1.2"]
6363
WAF: ["ON", "OFF"]
6464

65+
build-nginx-rum-fast:
66+
extends:
67+
- .build-and-test-fast
68+
- .build-nginx-rum
69+
parallel:
70+
matrix:
71+
- ARCH: ["amd64", "arm64"]
72+
NGINX_VERSION:
73+
- "1.24.0"
74+
- "1.25.5"
75+
- "1.26.3"
76+
- "1.27.5"
77+
- "1.28.2"
78+
- "1.29.5"
79+
WAF: ["OFF"]
80+
6581
test-nginx-fast:
6682
extends:
6783
- .build-and-test-fast
@@ -121,6 +137,38 @@ test-openresty-fast:
121137
RESTY_VERSION: ["1.27.1.2"]
122138
WAF: ["ON", "OFF"]
123139

140+
test-nginx-rum-fast:
141+
extends:
142+
- .build-and-test-fast
143+
- .test-nginx-rum
144+
needs: [build-nginx-rum-fast]
145+
parallel:
146+
matrix:
147+
- ARCH: ["amd64", "arm64"]
148+
BASE_IMAGE: ["nginx:1.24.0"]
149+
NGINX_VERSION: ["1.24.0"]
150+
WAF: ["OFF"]
151+
- ARCH: ["amd64", "arm64"]
152+
BASE_IMAGE: ["nginx:1.25.5"]
153+
NGINX_VERSION: ["1.25.5"]
154+
WAF: ["OFF"]
155+
- ARCH: ["amd64", "arm64"]
156+
BASE_IMAGE: ["nginx:1.26.3"]
157+
NGINX_VERSION: ["1.26.3"]
158+
WAF: ["OFF"]
159+
- ARCH: ["amd64", "arm64"]
160+
BASE_IMAGE: ["nginx:1.27.5"]
161+
NGINX_VERSION: ["1.27.5"]
162+
WAF: ["OFF"]
163+
- ARCH: ["amd64", "arm64"]
164+
BASE_IMAGE: ["nginx:1.28.2"]
165+
NGINX_VERSION: ["1.28.2"]
166+
WAF: ["OFF"]
167+
- ARCH: ["amd64", "arm64"]
168+
BASE_IMAGE: ["nginx:1.29.5"]
169+
NGINX_VERSION: ["1.29.5"]
170+
WAF: ["OFF"]
171+
124172
coverage:
125173
extends:
126174
- .build-and-test-fast

.gitlab/common.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717
paths:
1818
- artifacts/$ARCH/$NGINX_VERSION/$WAF/
1919

20+
.build-nginx-rum:
21+
extends: .build
22+
variables:
23+
RUM: "ON"
24+
script:
25+
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ddbuild.io/DataDog/".insteadOf "https://github.com/DataDog/"
26+
- make build-musl-aux
27+
- mkdir -p artifacts-rum/$ARCH/$NGINX_VERSION/$WAF
28+
- cp .musl-build/ngx_http_datadog_module.so* artifacts-rum/$ARCH/$NGINX_VERSION/$WAF/
29+
artifacts:
30+
paths:
31+
- artifacts-rum/$ARCH/$NGINX_VERSION/$WAF/
32+
2033
.build-openresty:
2134
extends: .build
2235
script:
@@ -51,6 +64,16 @@
5164
paths:
5265
- test/logs/test.log
5366

67+
.test-nginx-rum:
68+
extends: .test
69+
variables:
70+
RUM: "ON"
71+
script:
72+
- python3 test/bin/run.py --image $BASE_IMAGE --module-path artifacts-rum/$ARCH/$NGINX_VERSION/$WAF/ngx_http_datadog_module.so -- --verbose --failfast
73+
artifacts:
74+
paths:
75+
- test/logs/test.log
76+
5477
.test-ingress-nginx:
5578
extends: .test
5679
variables:

CMakeLists.txt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,26 @@ if(NGINX_DATADOG_ASM_ENABLED)
105105
endif()
106106

107107
if(NGINX_DATADOG_RUM_ENABLED)
108-
execute_process (
109-
COMMAND cargo pkgid --package inject-browser-sdk --manifest-path "${CMAKE_CURRENT_SOURCE_DIR}/deps/inject-browser-sdk/Cargo.toml"
110-
OUTPUT_VARIABLE RUM_SDK_INJECTOR_PKGID
111-
)
112-
string(REGEX REPLACE ".*@(.*)" "\\1" RUM_SDK_INJECTOR_VERSION ${RUM_SDK_INJECTOR_PKGID})
113-
string(STRIP "${RUM_SDK_INJECTOR_VERSION}" RUM_SDK_INJECTOR_VERSION)
114-
115-
unset(RUM_SDK_INJECTOR_PKGID)
116-
117108
add_subdirectory(deps)
109+
110+
# Determine the inject-browser-sdk version for the build identifier
111+
find_file(_inject_sdk_cargo_toml "Cargo.toml"
112+
PATHS
113+
"${CMAKE_CURRENT_SOURCE_DIR}/deps/inject-browser-sdk"
114+
"${injectbrowsersdk_SOURCE_DIR}"
115+
NO_DEFAULT_PATH)
116+
if(_inject_sdk_cargo_toml)
117+
execute_process(
118+
COMMAND cargo pkgid --package inject-browser-sdk --manifest-path "${_inject_sdk_cargo_toml}"
119+
OUTPUT_VARIABLE RUM_SDK_INJECTOR_PKGID
120+
)
121+
string(REGEX REPLACE ".*@(.*)" "\\1" RUM_SDK_INJECTOR_VERSION ${RUM_SDK_INJECTOR_PKGID})
122+
string(STRIP "${RUM_SDK_INJECTOR_VERSION}" RUM_SDK_INJECTOR_VERSION)
123+
unset(RUM_SDK_INJECTOR_PKGID)
124+
unset(_inject_sdk_cargo_toml CACHE)
125+
else()
126+
set(RUM_SDK_INJECTOR_VERSION "unknown")
127+
endif()
118128
endif()
119129

120130
include(./cmake/generate_build_id.cmake)

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ build-push-musl-toolchain:
5959

6060
.PHONY: build-local-musl-toolchain
6161
build-local-musl-toolchain:
62-
docker build --progress=plain --platform $(DOCKER_PLATFORM) --build-arg ARCH=$(ARCH) -t $(BUILD_IMAGE) build_env
62+
docker build --progress=plain --platform $(DOCKER_PLATFORM) --build-arg ARCH=$(ARCH) $(if $(BASE_IMAGE), --build-arg BASE_IMAGE=$(BASE_IMAGE),) -t $(BUILD_IMAGE) build_env
6363

6464
.PHONY: build-push-test-image
6565
build-push-test-image:

build_env/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Dockerfile for registry.ddbuild.io/ci/nginx-datadog/nginx_musl_toolchain
22

3-
FROM registry.ddbuild.io/images/mirror/library/alpine:3.20.3 AS sysroot
3+
ARG BASE_IMAGE=registry.ddbuild.io/images/mirror/library/alpine:3.20.3
4+
FROM ${BASE_IMAGE} AS sysroot
45

56
ARG LLVM_VERSION=17.0.6
67
ARG ARCH

deps/CMakeLists.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
include(corrosion.cmake)
1+
include(FetchContent)
22

3-
corrosion_import_crate(MANIFEST_PATH inject-browser-sdk/lib/Cargo.toml)
3+
set(INJECT_BROWSER_SDK_GIT_REF "49245e1c6ed8275990bb910125187e0aaad09e3d" CACHE STRING
4+
"Git tag/branch for inject-browser-sdk")
45

5-
corrosion_experimental_cbindgen(
6-
TARGET inject_browser_sdk
7-
HEADER_NAME injectbrowsersdk.h
8-
FLAGS --config ${CMAKE_SOURCE_DIR}/deps/cbindgen.toml
6+
FetchContent_Declare(
7+
InjectBrowserSDK
8+
GIT_REPOSITORY https://github.com/DataDog/inject-browser-sdk.git
9+
GIT_TAG ${INJECT_BROWSER_SDK_GIT_REF}
910
)
11+
FetchContent_MakeAvailable(InjectBrowserSDK)
1012

13+
if(NOT TARGET inject_browser_sdk)
14+
add_library(inject_browser_sdk ALIAS inject_browser_sdk_ffi)
15+
endif()

deps/corrosion.cmake

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/datadog_context.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ ngx_int_t DatadogContext::on_header_filter(ngx_http_request_t *request) {
113113
rum_span.set_error(true);
114114
}
115115
} else {
116+
// No trace/span found for this request (e.g. tracing is disabled via
117+
// `datadog_tracing off`, or this is an untracked subrequest).
118+
// Proceed with RUM injection without instrumentation.
116119
rum_ctx_.on_header_filter(request, loc_conf, ngx_http_next_header_filter);
117120
}
118121
}
@@ -340,6 +343,13 @@ void destroy_datadog_context(ngx_http_request_t *request) noexcept {
340343
}
341344

342345
ngx_int_t DatadogContext::on_precontent_phase(ngx_http_request_t *request) {
346+
// When tracing is disabled (e.g. `datadog_tracing off`), no traces are
347+
// created. Skip header injection to avoid undefined behavior from
348+
// calling traces_.front() on an empty vector.
349+
if (traces_.empty()) {
350+
return NGX_DECLINED;
351+
}
352+
343353
// inject headers in the precontent phase into the request headers
344354
// These headers will be copied by ngx_http_proxy_create_request on the
345355
// content phase into the outgoing request headers (probably)

0 commit comments

Comments
 (0)