Skip to content

Commit

Permalink
Merge pull request #2922 from DataDog/maximo/consistent-header-tags
Browse files Browse the repository at this point in the history
Support Tag Keys for DD_TRACE_HEADER_TAGS
  • Loading branch information
link04 authored Nov 6, 2024
2 parents b3fd817 + 2f104b2 commit db89e26
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 29 deletions.
4 changes: 3 additions & 1 deletion ext/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ enum ddtrace_sampling_rules_format {
CONFIG(CUSTOM(INT), DD_TRACE_SAMPLING_RULES_FORMAT, "glob", .parser = dd_parse_sampling_rules_format) \
CONFIG(JSON, DD_SPAN_SAMPLING_RULES, "[]") \
CONFIG(STRING, DD_SPAN_SAMPLING_RULES_FILE, "", .ini_change = ddtrace_alter_sampling_rules_file_config) \
CONFIG(SET_LOWERCASE, DD_TRACE_HEADER_TAGS, "", .ini_change = ddtrace_alter_DD_TRACE_HEADER_TAGS) \
CONFIG(SET_OR_MAP_LOWERCASE, DD_TRACE_HEADER_TAGS, "", .ini_change = ddtrace_alter_DD_TRACE_HEADER_TAGS) \
CONFIG(INT, DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH, "512") \
CONFIG(MAP, DD_TRACE_PEER_SERVICE_MAPPING, "") \
CONFIG(BOOL, DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED, "false") \
Expand Down Expand Up @@ -274,6 +274,7 @@ typedef enum { DD_CONFIGURATION } ddtrace_config_id;
}
#define SET MAP
#define SET_LOWERCASE MAP
#define SET_OR_MAP_LOWERCASE MAP
#define JSON MAP
#define MAP(id) \
static inline zend_array *get_##id(void) { return Z_ARR_P(zai_config_get_value(DDTRACE_CONFIG_##id)); } \
Expand All @@ -294,6 +295,7 @@ static inline bool get_global_DD_TRACE_SIDECAR_TRACE_SENDER(void) { return true;
#undef MAP
#undef SET
#undef SET_LOWERCASE
#undef SET_OR_MAP_LOWERCASE
#undef JSON
#undef BOOL
#undef INT
Expand Down
18 changes: 12 additions & 6 deletions ext/serializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,20 @@ static zend_result dd_add_meta_array(void *context, ddtrace_string key, ddtrace_

static void dd_add_header_to_meta(zend_array *meta, const char *type, zend_string *lowerheader,
zend_string *headerval) {
if (zend_hash_exists(get_DD_TRACE_HEADER_TAGS(), lowerheader)) {
for (char *ptr = ZSTR_VAL(lowerheader); *ptr; ++ptr) {
if ((*ptr < 'a' || *ptr > 'z') && *ptr != '-' && (*ptr < '0' || *ptr > '9')) {
*ptr = '_';
zval *header_config = zend_hash_find(get_DD_TRACE_HEADER_TAGS(), lowerheader);
if (header_config != NULL && Z_TYPE_P(header_config) == IS_STRING) {
zend_string *header_config_str = Z_STR_P(header_config);
zend_string *headertag;
if (ZSTR_LEN(header_config_str) == 0) {
for (char *ptr = ZSTR_VAL(lowerheader); *ptr; ++ptr) {
if ((*ptr < 'a' || *ptr > 'z') && *ptr != '-' && (*ptr < '0' || *ptr > '9')) {
*ptr = '_';
}
}
headertag = zend_strpprintf(0, "http.%s.headers.%s", type, ZSTR_VAL(lowerheader));
} else {
headertag = zend_string_copy(header_config_str);
}

zend_string *headertag = zend_strpprintf(0, "http.%s.headers.%s", type, ZSTR_VAL(lowerheader));
zval headerzv;
ZVAL_STR_COPY(&headerzv, headerval);
zend_hash_update(meta, headertag, &headerzv);
Expand Down
7 changes: 3 additions & 4 deletions tests/Unit/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,9 @@ public function testHttpHeadersCanSetOne()
public function testHttpHeadersCanSetMultiple()
{
$this->putEnvAndReloadConfig([
'DD_TRACE_HEADER_TAGS=A-Header ,Any-Name , cOn7aining-!spe_cial?:ch/ars ',
'DD_TRACE_HEADER_TAGS=A-Header ,Any-Name , cOn7aining-!spe_cial?:ch/ars , valueless:, Some-Header:with-colon-Key',
]);
// Same behavior as python tracer:
// https://github.com/DataDog/dd-trace-py/blob/f1298cb8100f146059f978b58c88641bd7424af8/ddtrace/http/headers.py
$this->assertSame(['a-header', 'any-name', 'con7aining-!spe_cial?:ch/ars'], array_keys(\dd_trace_env_config("DD_TRACE_HEADER_TAGS")));
$this->assertSame(['a-header', 'any-name', 'con7aining-!spe_cial?', 'valueless', 'some-header'], array_keys(\dd_trace_env_config("DD_TRACE_HEADER_TAGS")));
$this->assertEquals(['a-header' => '', 'any-name' => '', 'con7aining-!spe_cial?' => 'ch/ars', 'valueless' => '', 'some-header' => 'with-colon-Key'], \dd_trace_env_config("DD_TRACE_HEADER_TAGS"));
}
}
26 changes: 26 additions & 0 deletions tests/ext/dd_trace_serialize_header_to_meta.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Headers values are mapped to expected tag key
--ENV--
HTTP_CONTENT_TYPE=text/plain
HTTP_CUSTOM_HEADER=custom-header-value
HTTP_HEADER1=val
HTTP_HEADER2=v a l
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_TRACE_HEADER_TAGS=Content-Type,Custom-Header:custom-HeaderKey,header1: t a g ,header2:tag
--GET--
application_key=123
--FILE--
<?php
DDTrace\start_span();
DDTrace\close_span();
$spans = dd_trace_serialize_closed_spans();
var_dump($spans[0]['meta']['http.request.headers.content-type']);
var_dump($spans[0]['meta']['custom-HeaderKey']);
var_dump($spans[0]['meta']['t a g']);
var_dump($spans[0]['meta']['tag']);
?>
--EXPECT--
string(10) "text/plain"
string(19) "custom-header-value"
string(3) "val"
string(5) "v a l"
87 changes: 69 additions & 18 deletions zend_abstract_interface/config/config_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,49 +105,98 @@ static bool zai_config_decode_int(zai_str value, zval *decoded_value) {
return true;
}

static bool zai_config_decode_map(zai_str value, zval *decoded_value, bool persistent) {
static bool zai_config_decode_map(zai_str value, zval *decoded_value, bool persistent, bool lowercase, bool map_keyless) {
zval tmp;
ZVAL_ARR(&tmp, pemalloc(sizeof(HashTable), persistent));
zend_hash_init(Z_ARRVAL(tmp), 8, NULL, persistent ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR, persistent);

char *data = (char *)value.ptr;
if (data && *data) { // non-empty
const char *key_start, *key_end, *value_start, *value_end;

do {
if (*data != ',' && *data != ' ' && *data != '\t' && *data != '\n') {
key_start = key_end = data;
while (*++data) {
while (*data == ',' || *data == ' ' || *data == '\t' || *data == '\n') {
data++;
}

if (*data) {
key_start = data;
key_end = NULL;
bool has_colon = false;

while (*data && *data != ',') {
if (*data == ':') {
while (*++data && (*data == ' ' || *data == '\t' || *data == '\n'))
;
has_colon = true;
data++;

while (*data == ' ' || *data == '\t' || *data == '\n') data++;

value_start = value_end = data;
if (!*data || *data == ',') {
--value_end; // empty string instead of single char
if (*data == ',' || !*data) {
value_end = NULL;
} else {
while (*++data && *data != ',') {
value_start = value_end = data;
do {
if (*data != ' ' && *data != '\t' && *data != '\n') {
value_end = data;
}
data++;
} while (*data && *data != ',');
}

if (key_end && key_start) {
size_t key_len = key_end - key_start + 1;

zend_string *key = zend_string_init(key_start, key_len, persistent);
if (lowercase) {
zend_str_tolower(ZSTR_VAL(key), ZSTR_LEN(key));
}

zval val;
if (value_end) {
size_t value_len = value_end - value_start + 1;
ZVAL_NEW_STR(&val, zend_string_init(value_start, value_len, persistent));
} else {
if (persistent) {
ZVAL_EMPTY_PSTRING(&val);
} else {
ZVAL_EMPTY_STRING(&val);
}
}
zend_hash_update(Z_ARRVAL(tmp), key, &val);
zend_string_release(key);
}

size_t key_len = key_end - key_start + 1;
size_t value_len = value_end - value_start + 1;
zval val;
ZVAL_NEW_STR(&val, zend_string_init(value_start, value_len, persistent));
zend_hash_str_update(Z_ARRVAL(tmp), key_start, key_len, &val);
break;
}

// Set key_end to the last valid non-whitespace character of the key
if (*data != ' ' && *data != '\t' && *data != '\n') {
key_end = data;
}
data++;
}

// Handle standalone keys (without a colon) if map_keyless is enabled
if (map_keyless && !has_colon && key_end) {
size_t key_len = key_end - key_start + 1;
zend_string *key = zend_string_init(key_start, key_len, persistent);
if (lowercase) {
zend_str_tolower(ZSTR_VAL(key), ZSTR_LEN(key));
}

zval val;
if (persistent) {
ZVAL_EMPTY_PSTRING(&val);
} else {
ZVAL_EMPTY_STRING(&val);
}
zend_hash_update(Z_ARRVAL(tmp), key, &val);
zend_string_release(key);
}
} else {
++data;
}
} while (*data);

// Check if the array has any elements; if not, cleanup
if (zend_hash_num_elements(Z_ARRVAL(tmp)) == 0) {
zend_hash_destroy(Z_ARRVAL(tmp));
pefree(Z_ARRVAL(tmp), persistent);
Expand Down Expand Up @@ -232,7 +281,9 @@ bool zai_config_decode_value(zai_str value, zai_config_type type, zai_custom_par
case ZAI_CONFIG_TYPE_INT:
return zai_config_decode_int(value, decoded_value);
case ZAI_CONFIG_TYPE_MAP:
return zai_config_decode_map(value, decoded_value, persistent);
return zai_config_decode_map(value, decoded_value, persistent, false, false);
case ZAI_CONFIG_TYPE_SET_OR_MAP_LOWERCASE:
return zai_config_decode_map(value, decoded_value, persistent, true, true);
case ZAI_CONFIG_TYPE_SET:
return zai_config_decode_set(value, decoded_value, persistent, false);
case ZAI_CONFIG_TYPE_SET_LOWERCASE:
Expand Down
1 change: 1 addition & 0 deletions zend_abstract_interface/config/config_decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ typedef enum {
ZAI_CONFIG_TYPE_MAP,
ZAI_CONFIG_TYPE_SET,
ZAI_CONFIG_TYPE_SET_LOWERCASE,
ZAI_CONFIG_TYPE_SET_OR_MAP_LOWERCASE,
ZAI_CONFIG_TYPE_JSON,
ZAI_CONFIG_TYPE_STRING,
ZAI_CONFIG_TYPE_CUSTOM,
Expand Down

0 comments on commit db89e26

Please sign in to comment.