Skip to content

Commit 2f646bb

Browse files
authored
feat!: Allow overriding default tags (#183)
Changes: - Refactor how tags are stored. - BREAKING CHANGE: only value tags can contain expressions which can be evaluated during request processing. - Update directive callbacks to use NGX_CONF_OK instead of NGX_OK for better consistency. Resolves #172
1 parent f2e7b9b commit 2f646bb

13 files changed

+275
-205
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ set_target_properties(ngx_http_datadog_objs PROPERTIES POSITION_INDEPENDENT_CODE
136136
target_compile_definitions(ngx_http_datadog_objs PUBLIC DD_NGINX_FLAVOR="${NGINX_DATADOG_FLAVOR}")
137137
target_sources(ngx_http_datadog_objs
138138
PRIVATE
139+
src/common/variable.cpp
139140
src/array_util.cpp
140141
src/datadog_conf.cpp
141142
src/datadog_conf_handler.cpp

src/common/variable.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "common/variable.h"
2+
3+
#include "string_util.h"
4+
5+
namespace datadog::common {
6+
7+
ngx_http_complex_value_t *make_complex_value(ngx_conf_t *cf, ngx_str_t &expr) {
8+
auto *cv = (ngx_http_complex_value_t *)ngx_pcalloc(
9+
cf->pool, sizeof(ngx_http_complex_value_t));
10+
11+
ngx_http_compile_complex_value_t ccv;
12+
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
13+
14+
ccv.cf = cf;
15+
ccv.value = &expr;
16+
ccv.complex_value = cv;
17+
ccv.zero = 0;
18+
ccv.conf_prefix = 0;
19+
20+
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
21+
return nullptr;
22+
}
23+
24+
return cv;
25+
}
26+
27+
ngx_http_complex_value_t *make_complex_value(ngx_conf_t *cf,
28+
std::string_view expr) {
29+
auto ngx_expr = nginx::to_ngx_str(cf->pool, expr);
30+
return make_complex_value(cf, ngx_expr);
31+
}
32+
33+
std::optional<std::string> eval_complex_value(
34+
ngx_http_complex_value_t *complex_value, ngx_http_request_t *request) {
35+
if (complex_value == nullptr) return std::nullopt;
36+
37+
ngx_str_t res;
38+
if (ngx_http_complex_value(request, complex_value, &res) != NGX_OK ||
39+
res.len == 0) {
40+
return std::nullopt;
41+
}
42+
43+
return nginx::to_string(res);
44+
}
45+
46+
} // namespace datadog::common

src/common/variable.h

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
extern "C" {
4+
5+
#include <nginx.h>
6+
#include <ngx_conf_file.h>
7+
#include <ngx_config.h>
8+
#include <ngx_core.h>
9+
#include <ngx_http.h>
10+
#include <ngx_log.h>
11+
}
12+
13+
#include <optional>
14+
#include <string>
15+
16+
namespace datadog::common {
17+
18+
/// Extracted from NGINX Development Guide
19+
/// (<https://nginx.org/en/docs/dev/development_guide.html#http_complex_values>)
20+
///
21+
/// A complex value, despite its name, provides an easy way to evaluate
22+
/// expressions which can contain text, variables, and their combination.
23+
ngx_http_complex_value_t *make_complex_value(ngx_conf_t *cf,
24+
ngx_str_t &default_value);
25+
26+
ngx_http_complex_value_t *make_complex_value(ngx_conf_t *cf,
27+
std::string_view default_value);
28+
29+
/// Evaluate complex expressions. Returns the value if the evaluation is
30+
/// successful, otherwise returns nothing.
31+
std::optional<std::string> eval_complex_value(
32+
ngx_http_complex_value_t *complex_value, ngx_http_request_t *request);
33+
34+
} // namespace datadog::common

src/datadog_conf.h

+3-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern "C" {
1717
#endif
1818

1919
#include <string>
20+
#include <unordered_map>
2021
#include <vector>
2122

2223
#define DD_NGX_CONF_COMPLEX_UNSET \
@@ -25,11 +26,6 @@ extern "C" {
2526
namespace datadog {
2627
namespace nginx {
2728

28-
struct datadog_tag_t {
29-
NgxScript key_script;
30-
NgxScript value_script;
31-
};
32-
3329
struct conf_directive_source_location_t {
3430
ngx_str_t file_name; // e.g. "nginx.conf"
3531
ngx_uint_t line; // line number within the file `file_name`
@@ -61,7 +57,7 @@ struct sampling_rule_t {
6157
};
6258

6359
struct datadog_main_conf_t {
64-
ngx_array_t *tags;
60+
std::unordered_map<std::string, ngx_http_complex_value_t *> tags;
6561
// `are_propagation_styles_locked` is whether the tracer's propagation styles
6662
// have been set, either by an explicit `datadog_propagation_styles`
6763
// directive, or implicitly to a default configuration by another directive.
@@ -197,7 +193,7 @@ struct datadog_loc_conf_t {
197193
ngx_http_complex_value_t *service_env = DD_NGX_CONF_COMPLEX_UNSET;
198194
// `service_version` is set by the `datadog_version` directive.
199195
ngx_http_complex_value_t *service_version = DD_NGX_CONF_COMPLEX_UNSET;
200-
ngx_array_t *tags;
196+
std::unordered_map<std::string, ngx_http_complex_value_t *> tags;
201197
// `parent` is the parent context (e.g. the `server` to this `location`), or
202198
// `nullptr` if this context has no parent.
203199
datadog_loc_conf_t *parent;

src/datadog_directive.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace nginx {
1010
char *silently_ignore_command(ngx_conf_t *cf, ngx_command_t *command, void *) {
1111
ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Directive \"%V\" ignored",
1212
&command->name);
13-
return NGX_OK;
13+
return NGX_CONF_OK;
1414
}
1515

1616
char *alias_directive(ngx_conf_t *cf, ngx_command_t *command, void *) noexcept {
@@ -36,7 +36,7 @@ char *alias_directive(ngx_conf_t *cf, ngx_command_t *command, void *) noexcept {
3636
return static_cast<char *>(NGX_CONF_ERROR);
3737
}
3838

39-
return static_cast<char *>(NGX_CONF_OK);
39+
return NGX_CONF_OK;
4040
}
4141

4242
char *warn_deprecated_command(ngx_conf_t *cf, ngx_command_t *command,
@@ -52,7 +52,7 @@ char *warn_deprecated_command(ngx_conf_t *cf, ngx_command_t *command,
5252
}
5353

5454
ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "%s", buf);
55-
return NGX_OK;
55+
return NGX_CONF_OK;
5656
}
5757

5858
char *err_deprecated_command(ngx_conf_t *cf, ngx_command_t *command,

src/ngx_http_datadog_module.cpp

+20-74
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#if defined(WITH_RUM)
2828
#include "rum/config.h"
2929
#endif
30+
#include "common/variable.h"
3031
#include "string_util.h"
3132
#include "tracing_library.h"
3233
#include "version.h"
@@ -314,14 +315,15 @@ static ngx_int_t datadog_module_init(ngx_conf_t *cf) noexcept {
314315

315316
// Add default span tags.
316317
const auto tags = TracingLibrary::default_tags();
317-
if (tags.empty()) return NGX_OK;
318-
main_conf->tags =
319-
ngx_array_create(cf->pool, tags.size(), sizeof(datadog_tag_t));
320-
if (!main_conf->tags) return NGX_ERROR;
321-
for (const auto &tag : tags) {
322-
if (add_datadog_tag(cf, main_conf->tags, to_ngx_str(tag.first),
323-
to_ngx_str(tag.second)) != NGX_CONF_OK) {
324-
return NGX_ERROR;
318+
if (!tags.empty()) {
319+
for (const auto [key, value] : tags) {
320+
auto ngx_value = to_ngx_str(cf->pool, value);
321+
auto *complex_value = datadog::common::make_complex_value(cf, ngx_value);
322+
if (complex_value == nullptr) {
323+
return NGX_ERROR;
324+
}
325+
326+
main_conf->tags.insert_or_assign(std::string(key), complex_value);
325327
}
326328
}
327329

@@ -431,33 +433,13 @@ static void *create_datadog_loc_conf(ngx_conf_t *conf) noexcept {
431433
return loc_conf;
432434
}
433435

434-
static ngx_http_complex_value_t *make_default_complex_value(
435-
ngx_conf_t *cf, std::string_view default_value) {
436-
ngx_str_t value = to_ngx_str(default_value);
437-
auto *cv = (ngx_http_complex_value_t *)ngx_pcalloc(
438-
cf->pool, sizeof(ngx_http_complex_value_t));
439-
440-
ngx_http_compile_complex_value_t ccv;
441-
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
442-
443-
ccv.cf = cf;
444-
ccv.value = &value;
445-
ccv.complex_value = cv;
446-
ccv.zero = 0;
447-
ccv.conf_prefix = 0;
448-
449-
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
450-
return nullptr;
451-
}
452-
453-
return cv;
454-
}
455-
456436
//------------------------------------------------------------------------------
457437
// merge_datadog_loc_conf
458438
//------------------------------------------------------------------------------
459439
static char *merge_datadog_loc_conf(ngx_conf_t *cf, void *parent,
460440
void *child) noexcept {
441+
using namespace datadog::common;
442+
461443
auto prev = static_cast<datadog_loc_conf_t *>(parent);
462444
auto conf = static_cast<datadog_loc_conf_t *>(child);
463445

@@ -474,65 +456,29 @@ static char *merge_datadog_loc_conf(ngx_conf_t *cf, void *parent,
474456
nullptr);
475457
ngx_conf_merge_ptr_value(
476458
conf->operation_name_script, prev->operation_name_script,
477-
make_default_complex_value(
459+
make_complex_value(
478460
cf, TracingLibrary::default_request_operation_name_pattern()));
479461
ngx_conf_merge_ptr_value(
480462
conf->loc_operation_name_script, prev->loc_operation_name_script,
481-
make_default_complex_value(
463+
make_complex_value(
482464
cf, TracingLibrary::default_location_operation_name_pattern()));
483465
ngx_conf_merge_ptr_value(
484466
conf->resource_name_script, prev->resource_name_script,
485-
make_default_complex_value(
486-
cf, TracingLibrary::default_resource_name_pattern()));
467+
make_complex_value(cf, TracingLibrary::default_resource_name_pattern()));
487468
ngx_conf_merge_ptr_value(
488469
conf->loc_resource_name_script, prev->loc_resource_name_script,
489-
make_default_complex_value(
490-
cf, TracingLibrary::default_resource_name_pattern()));
470+
make_complex_value(cf, TracingLibrary::default_resource_name_pattern()));
491471
ngx_conf_merge_value(conf->trust_incoming_span, prev->trust_incoming_span, 1);
492472

493473
// Create a new array that joins `prev->tags` and `conf->tags`. Since tags
494474
// are set consecutively and setting a tag with the same key as a previous
495475
// one overwrites it, we need to ensure that the tags in `conf->tags` come
496476
// after `prev->tags` so as to keep the value from the most specific
497477
// configuration.
498-
if (prev->tags && !conf->tags) {
499-
conf->tags = prev->tags;
500-
} else if (prev->tags && conf->tags) {
501-
std::unordered_map<std::string, datadog_tag_t> merged_tags;
502-
503-
for (ngx_uint_t i = 0; i < prev->tags->nelts; i++) {
504-
datadog_tag_t *tag = &((datadog_tag_t *)prev->tags->elts)[i];
505-
std::string key;
506-
key.assign(reinterpret_cast<const char *>(tag->key_script.pattern_.data),
507-
tag->key_script.pattern_.len);
508-
merged_tags[key] = *tag;
509-
}
510-
511-
for (ngx_uint_t i = 0; i < conf->tags->nelts; i++) {
512-
datadog_tag_t *tag = &((datadog_tag_t *)conf->tags->elts)[i];
513-
std::string key;
514-
key.assign(reinterpret_cast<const char *>(tag->key_script.pattern_.data),
515-
tag->key_script.pattern_.len);
516-
merged_tags[key] = *tag;
517-
}
518-
519-
ngx_uint_t index = 0;
520-
for (const auto &kv : merged_tags) {
521-
if (index == conf->tags->nelts) {
522-
datadog_tag_t *tag = (datadog_tag_t *)ngx_array_push(conf->tags);
523-
524-
if (!tag) {
525-
return (char *)NGX_CONF_ERROR;
526-
}
527-
528-
*tag = kv.second;
529-
} else {
530-
datadog_tag_t *tags = (datadog_tag_t *)conf->tags->elts;
531-
tags[index] = kv.second;
532-
}
533-
534-
index++;
535-
}
478+
if (!prev->tags.empty()) {
479+
auto parent_tags =
480+
prev->tags; ///< Make a copy because merge steal the nodes.
481+
conf->tags.merge(parent_tags);
536482
}
537483

538484
#ifdef WITH_WAF

0 commit comments

Comments
 (0)