Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 38 additions & 38 deletions ext/otel_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ static void report_otel_cfg_telemetry_invalid(const char *otel_cfg, const char *
}
}

static bool get_otel_value(zai_str str, zai_env_buffer buf, bool pre_rinit) {
if (zai_getenv_ex(str, buf, pre_rinit) == ZAI_ENV_SUCCESS) {
static bool get_otel_value(zai_str str, zai_env_buffer *buf, bool pre_rinit) {
if (zai_getenv_ex(str, buf, pre_rinit, true) == ZAI_ENV_SUCCESS) {
return true;
}

zval *cfg = cfg_get_entry(str.ptr, str.len);
if (cfg) {
if (Z_TYPE_P(cfg) == IS_ARRAY) {
zval *val;
char *off = buf.ptr;
char *off = buf->ptr;
ZEND_HASH_FOREACH_VAL(Z_ARR_P(cfg), val) {
if (Z_TYPE_P(val) == IS_STRING) {
if (off - buf.ptr + Z_STRLEN_P(val) + 2 >= ZAI_ENV_MAX_BUFSIZ) {
if (off - buf->ptr + Z_STRLEN_P(val) + 2 >= ZAI_ENV_MAX_BUFSIZ) {
return false;
}
if (off != buf.ptr) {
if (off != buf->ptr) {
*off++ = ',';
}
memcpy(off, Z_STRVAL_P(val), Z_STRLEN_P(val));
Expand All @@ -46,29 +46,29 @@ static bool get_otel_value(zai_str str, zai_env_buffer buf, bool pre_rinit) {
} else if (Z_STRLEN_P(cfg) == 0 || Z_STRLEN_P(cfg) + 1 >= ZAI_ENV_MAX_BUFSIZ) {
return false;
} else {
memcpy(buf.ptr, Z_STRVAL_P(cfg), Z_STRLEN_P(cfg) + 1);
memcpy(buf->ptr, Z_STRVAL_P(cfg), Z_STRLEN_P(cfg) + 1);
}
return true;
}

return false;
}

static bool ddtrace_conf_otel_resource_attributes_special(const char *tag, int len, zai_env_buffer buf, bool pre_rinit) {
static bool ddtrace_conf_otel_resource_attributes_special(const char *tag, int len, zai_env_buffer *buf, bool pre_rinit) {
if (!get_otel_value((zai_str)ZAI_STRL("OTEL_RESOURCE_ATTRIBUTES"), buf, pre_rinit)) {
return false;
}

for (char *cur = buf.ptr, *key_start = cur; *cur; ++cur) {
for (char *cur = buf->ptr, *key_start = cur; *cur; ++cur) {
if (*cur == '=') {
char *key_end = cur++;
while (*cur && *cur != ',') {
++cur;
}
if (key_end - key_start == len && memcmp(key_start, tag, len) == 0 && key_end[1]) {
size_t vallen = cur - (key_end + 1);
memcpy(buf.ptr, key_end + 1, vallen);
buf.ptr[vallen] = 0;
memcpy(buf->ptr, key_end + 1, vallen);
buf->ptr[vallen] = 0;
return true;
}
key_start = cur-- + 1;
Expand All @@ -78,92 +78,92 @@ static bool ddtrace_conf_otel_resource_attributes_special(const char *tag, int l
return false;
}

bool ddtrace_conf_otel_resource_attributes_env(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_resource_attributes_env(zai_env_buffer *buf, bool pre_rinit) {
return ddtrace_conf_otel_resource_attributes_special(ZEND_STRL("deployment.environment"), buf, pre_rinit);
}

bool ddtrace_conf_otel_resource_attributes_version(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_resource_attributes_version(zai_env_buffer *buf, bool pre_rinit) {
return ddtrace_conf_otel_resource_attributes_special(ZEND_STRL("service.version"), buf, pre_rinit);
}

bool ddtrace_conf_otel_service_name(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_service_name(zai_env_buffer *buf, bool pre_rinit) {
return get_otel_value((zai_str)ZAI_STRL("OTEL_SERVICE_NAME"), buf, pre_rinit)
|| ddtrace_conf_otel_resource_attributes_special(ZEND_STRL("service.name"), buf, pre_rinit);
}

bool ddtrace_conf_otel_log_level(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_log_level(zai_env_buffer *buf, bool pre_rinit) {
return get_otel_value((zai_str)ZAI_STRL("OTEL_LOG_LEVEL"), buf, pre_rinit);
}

bool ddtrace_conf_otel_propagators(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_propagators(zai_env_buffer *buf, bool pre_rinit) {
if (!get_otel_value((zai_str)ZAI_STRL("OTEL_PROPAGATORS"), buf, pre_rinit)) {
return false;
}
char *off = (char *)zend_memnstr(buf.ptr, ZEND_STRL("b3"), buf.ptr + strlen(buf.ptr));
if (off && (!off[strlen("b3")] || off[strlen("b3")] == ',') && strlen(buf.ptr) < buf.len - 100) {
memmove(off + strlen("b3 single header"), off + strlen("b3"), buf.ptr + strlen(buf.ptr) - (off + strlen("b3")) + 1);
char *off = (char *)zend_memnstr(buf->ptr, ZEND_STRL("b3"), buf->ptr + strlen(buf->ptr));
if (off && (!off[strlen("b3")] || off[strlen("b3")] == ',') && strlen(buf->ptr) < buf->len - 100) {
memmove(off + strlen("b3 single header"), off + strlen("b3"), buf->ptr + strlen(buf->ptr) - (off + strlen("b3")) + 1);
memcpy(off, "b3 single header", strlen("b3 single header"));
}
return true;
}

bool ddtrace_conf_otel_sample_rate(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_sample_rate(zai_env_buffer *buf, bool pre_rinit) {
if (!get_otel_value((zai_str)ZAI_STRL("OTEL_TRACES_SAMPLER"), buf, pre_rinit)) {
return false;
}

if (strcmp(buf.ptr, "always_on") == 0 || strcmp(buf.ptr, "parentbased_always_on") == 0) {
memcpy(buf.ptr, ZEND_STRS("1"));
if (strcmp(buf->ptr, "always_on") == 0 || strcmp(buf->ptr, "parentbased_always_on") == 0) {
memcpy(buf->ptr, ZEND_STRS("1"));
return true;
}
if (strcmp(buf.ptr, "always_off") == 0 || strcmp(buf.ptr, "parentbased_always_off") == 0) {
memcpy(buf.ptr, ZEND_STRS("0"));
if (strcmp(buf->ptr, "always_off") == 0 || strcmp(buf->ptr, "parentbased_always_off") == 0) {
memcpy(buf->ptr, ZEND_STRS("0"));
return true;
}
if (strcmp(buf.ptr, "traceidratio") == 0 || strcmp(buf.ptr, "parentbased_traceidratio") == 0) {
if (strcmp(buf->ptr, "traceidratio") == 0 || strcmp(buf->ptr, "parentbased_traceidratio") == 0) {
if (get_otel_value((zai_str)ZAI_STRL("OTEL_TRACES_SAMPLER_ARG"), buf, pre_rinit)) {
return true;
}
LOG_ONCE(WARN, "OTEL_TRACES_SAMPLER is %s, but is missing OTEL_TRACES_SAMPLER_ARG", buf.ptr);
LOG_ONCE(WARN, "OTEL_TRACES_SAMPLER is %s, but is missing OTEL_TRACES_SAMPLER_ARG", buf->ptr);
} else {
LOG_ONCE(WARN, "OTEL_TRACES_SAMPLER has invalid value: %s", buf.ptr);
LOG_ONCE(WARN, "OTEL_TRACES_SAMPLER has invalid value: %s", buf->ptr);
}
report_otel_cfg_telemetry_invalid("otel_traces_sampler", "dd_trace_sample_rate", pre_rinit);
return false;
}

bool ddtrace_conf_otel_traces_exporter(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_traces_exporter(zai_env_buffer *buf, bool pre_rinit) {
if (get_otel_value((zai_str)ZAI_STRL("OTEL_TRACES_EXPORTER"), buf, pre_rinit)) {
if (strcmp(buf.ptr, "none") == 0) {
memcpy(buf.ptr, ZEND_STRS("0"));
if (strcmp(buf->ptr, "none") == 0) {
memcpy(buf->ptr, ZEND_STRS("0"));
return true;
}
LOG_ONCE(WARN, "OTEL_TRACES_EXPORTER has invalid value: %s", buf.ptr);
LOG_ONCE(WARN, "OTEL_TRACES_EXPORTER has invalid value: %s", buf->ptr);
report_otel_cfg_telemetry_invalid("otel_traces_exporter", "dd_trace_enabled", pre_rinit);
}
return false;
}

bool ddtrace_conf_otel_metrics_exporter(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_metrics_exporter(zai_env_buffer *buf, bool pre_rinit) {
if (get_otel_value((zai_str)ZAI_STRL("OTEL_METRICS_EXPORTER"), buf, pre_rinit)) {
if (strcmp(buf.ptr, "none") == 0) {
memcpy(buf.ptr, ZEND_STRS("0"));
if (strcmp(buf->ptr, "none") == 0) {
memcpy(buf->ptr, ZEND_STRS("0"));
return true;
}
LOG_ONCE(WARN, "OTEL_METRICS_EXPORTER has invalid value: %s", buf.ptr);
LOG_ONCE(WARN, "OTEL_METRICS_EXPORTER has invalid value: %s", buf->ptr);
report_otel_cfg_telemetry_invalid("otel_metrics_exporter", "dd_integration_metrics_enabled", pre_rinit);
}
return false;
}

bool ddtrace_conf_otel_resource_attributes_tags(zai_env_buffer buf, bool pre_rinit) {
bool ddtrace_conf_otel_resource_attributes_tags(zai_env_buffer *buf, bool pre_rinit) {
if (!get_otel_value((zai_str)ZAI_STRL("OTEL_RESOURCE_ATTRIBUTES"), buf, pre_rinit)) {
return false;
}

char *out = buf.ptr;
char *out = buf->ptr;
int tags = 0;
for (char *cur = buf.ptr, *key_start = cur; *cur; ++cur) {
for (char *cur = buf->ptr, *key_start = cur; *cur; ++cur) {
if (*cur == '=') {
char *key = key_start, *key_end = cur++;
while (*cur && *cur != ',') {
Expand All @@ -190,7 +190,7 @@ bool ddtrace_conf_otel_resource_attributes_tags(zai_env_buffer buf, bool pre_rin
--cur;
}
}
if (out != buf.ptr) {
if (out != buf->ptr) {
--out;
}
*out = 0;
Expand Down
18 changes: 9 additions & 9 deletions ext/otel_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

#include <env/env.h>

bool ddtrace_conf_otel_resource_attributes_env(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_resource_attributes_version(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_service_name(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_log_level(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_propagators(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_sample_rate(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_traces_exporter(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_metrics_exporter(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_resource_attributes_tags(zai_env_buffer buf, bool pre_rinit);
bool ddtrace_conf_otel_resource_attributes_env(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_resource_attributes_version(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_service_name(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_log_level(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_propagators(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_sample_rate(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_traces_exporter(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_metrics_exporter(zai_env_buffer *buf, bool pre_rinit);
bool ddtrace_conf_otel_resource_attributes_tags(zai_env_buffer *buf, bool pre_rinit);

#endif // DD_OTEL_CONFIG_H
83 changes: 77 additions & 6 deletions zend_abstract_interface/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,68 @@ HashTable zai_config_name_map = {0};
uint16_t zai_config_memoized_entries_count = 0;
zai_config_memoized_entry zai_config_memoized_entries[ZAI_CONFIG_ENTRIES_COUNT_MAX];

static bool zai_config_get_env_value(zai_str name, zai_env_buffer buf) {
/*
* File-local owning zai_option_str cache:
* - None (ptr == NULL): env var was unset.
* - Some with len == 0: env var was explicitly set to empty.
* - Some with len > 0: env var has a non-empty value.
*
* Each Some entry owns pemalloc(..., 1) memory and is freed in
* zai_config_clear_cached_env_values().
*/
static zai_option_str zai_config_cached_env_values[ZAI_CONFIG_ENTRIES_COUNT_MAX][ZAI_CONFIG_NAMES_COUNT_MAX];

bool zai_config_get_cached_env_value(zai_config_id id, uint8_t name_index, zai_env_buffer *buf) {
ZEND_ASSERT(buf != NULL);
ZEND_ASSERT(buf->ptr != NULL);
ZEND_ASSERT(buf->len > 0);
ZEND_ASSERT(id < zai_config_memoized_entries_count);
ZEND_ASSERT(name_index < zai_config_memoized_entries[id].names_count);
zai_option_str cached = zai_config_cached_env_values[id][name_index];
if (zai_option_str_is_some(cached)) {
buf->ptr = (char *)cached.ptr;
buf->len = cached.len;
return true;
}
return false;
}

static void zai_config_cache_env_values(void) {
for (zai_config_id i = 0; i < zai_config_memoized_entries_count; i++) {
zai_config_memoized_entry *memoized = &zai_config_memoized_entries[i];
for (uint8_t n = 0; n < memoized->names_count; n++) {
zai_str name = {.len = memoized->names[n].len, .ptr = memoized->names[n].ptr};
char *value = getenv(name.ptr);
if (!value) {
continue;
}
size_t len = strlen(value);
char *copy = pemalloc(len + 1, 1);
memcpy(copy, value, len + 1);
zai_config_cached_env_values[i][n] = zai_option_str_from_raw_parts(copy, len);
}
}
}

static void zai_config_clear_cached_env_values(void) {
for (zai_config_id i = 0; i < zai_config_memoized_entries_count; i++) {
zai_config_memoized_entry *memoized = &zai_config_memoized_entries[i];
for (uint8_t n = 0; n < memoized->names_count; n++) {
zai_option_str *cached = &zai_config_cached_env_values[i][n];
if (zai_option_str_is_some(*cached)) {
pefree((char *)cached->ptr, 1);
}
*cached = ZAI_OPTION_STR_NONE;
}
}
}

static bool zai_config_get_env_value(zai_str name, zai_config_id id, uint8_t name_index, zai_env_buffer *buf) {
// TODO Handle other return codes
// We want to explicitly allow pre-RINIT access to env vars here. So that callers can have an early view at config.
// But in general allmost all configurations shall only be accessed after first RINIT. (the trivial getter will
return zai_getenv_ex(name, buf, true) == ZAI_ENV_SUCCESS;
if (zai_getenv(name, buf) == ZAI_ENV_SUCCESS) {
return true;
}
return zai_config_get_cached_env_value(id, name_index, buf);
}

static inline void zai_config_process_env(zai_config_memoized_entry *memoized, zai_env_buffer buf, zai_option_str *value) {
Expand All @@ -40,6 +97,8 @@ static void zai_config_find_and_set_value(zai_config_memoized_entry *memoized, z

int16_t name_index = 0;
for (; name_index < memoized->names_count; name_index++) {
buf.ptr = buf_storage;
buf.len = sizeof(buf_storage);
zai_str name = {.len = memoized->names[name_index].len, .ptr = memoized->names[name_index].ptr};
zai_config_stable_file_entry *entry = zai_config_stable_file_get_value(name);
if (entry && entry->source == DDOG_LIBRARY_CONFIG_SOURCE_FLEET_STABLE_CONFIG) {
Expand All @@ -48,7 +107,7 @@ static void zai_config_find_and_set_value(zai_config_memoized_entry *memoized, z
name_index = ZAI_CONFIG_ORIGIN_FLEET_STABLE;
memoized->config_id = (zai_str) ZAI_STR_FROM_ZSTR(entry->config_id);
break;
} else if (zai_config_get_env_value(name, buf)) {
} else if (zai_config_get_env_value(name, id, (uint8_t)name_index, &buf)) {
zai_config_process_env(memoized, buf, &value);
break;
} else if (entry && entry->source == DDOG_LIBRARY_CONFIG_SOURCE_LOCAL_STABLE_CONFIG) {
Expand All @@ -59,7 +118,10 @@ static void zai_config_find_and_set_value(zai_config_memoized_entry *memoized, z
break;
}
}
if (!value.len && memoized->env_config_fallback && memoized->env_config_fallback(buf, true)) {

buf.ptr = buf_storage;
buf.len = sizeof(buf_storage);
if (!value.len && memoized->env_config_fallback && memoized->env_config_fallback(&buf, true)) {
zai_config_process_env(memoized, buf, &value);
name_index = ZAI_CONFIG_ORIGIN_MODIFIED;
}
Expand Down Expand Up @@ -148,6 +210,7 @@ bool zai_config_minit(zai_config_entry entries[], size_t entries_count, zai_conf
if (!entries || !entries_count) return false;
if (!zai_json_setup_bindings()) return false;
zai_config_entries_init(entries, entries_count);
zai_config_cache_env_values();
zai_config_ini_minit(env_to_ini, module_number);
zai_config_stable_file_minit();
#if PHP_VERSION_ID >= 70300 && PHP_VERSION_ID < 70400
Expand All @@ -164,6 +227,7 @@ static void zai_config_dtor_memoized_zvals(void) {

void zai_config_mshutdown(void) {
zai_config_dtor_memoized_zvals();
zai_config_clear_cached_env_values();
if (zai_config_name_map.nTableSize) {
zend_hash_destroy(&zai_config_name_map);
}
Expand Down Expand Up @@ -237,6 +301,13 @@ void zai_config_first_time_rinit(bool in_request) {
(void)in_request;
#endif

if (in_request) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only need it for FPM, right? So let's add a sapi_module check here?
Saving some minor time on CLI scripts.

Copy link
Collaborator Author

@morrisonlevi morrisonlevi Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what litespeed does, and IIRC we did have a customer using it. How about a not-CLI SAPI check instead? That way web SAPIs stay consistent, but it's skipped on CLI where it's unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's reasonably clearer now. Patch is smaller, at least xD

// Refresh process env snapshot for SAPIs like FPM that materialize
// pool env values just before the first request.
zai_config_clear_cached_env_values();
zai_config_cache_env_values();
}

for (uint16_t i = 0; i < zai_config_memoized_entries_count; i++) {
zai_config_memoized_entry *memoized = &zai_config_memoized_entries[i];
zai_config_find_and_set_value(memoized, i);
Expand Down
2 changes: 2 additions & 0 deletions zend_abstract_interface/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stddef.h>
#include <stdint.h>

#include "../env/env.h"
#include "../zai_string/string.h"
#include "config_decode.h"

Expand Down Expand Up @@ -114,5 +115,6 @@ bool zai_config_get_id_by_name(zai_str name, zai_config_id *id);
void zai_config_register_config_id(zai_config_name *name, zai_config_id id);

bool zai_config_is_initialized(void);
bool zai_config_get_cached_env_value(zai_config_id id, uint8_t name_index, zai_env_buffer *buf);

#endif // ZAI_CONFIG_H
9 changes: 7 additions & 2 deletions zend_abstract_interface/config/config_ini.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ void zai_config_ini_rinit(void) {
// makes only sense to update INIs once, avoid rereading env unnecessarily
if (!env_to_ini_name || !memoized->original_on_modify) {
for (uint8_t name_index = 0; name_index < memoized->names_count; name_index++) {
buf.ptr = buf_storage;
buf.len = sizeof(buf_storage);
zai_str name = ZAI_STR_NEW(memoized->names[name_index].ptr, memoized->names[name_index].len);
zai_config_stable_file_entry *entry = zai_config_stable_file_get_value(name);
if (entry && entry->source == DDOG_LIBRARY_CONFIG_SOURCE_FLEET_STABLE_CONFIG
Expand All @@ -446,7 +448,8 @@ void zai_config_ini_rinit(void) {
memoized->name_index = ZAI_CONFIG_ORIGIN_FLEET_STABLE;
memoized->config_id = (zai_str) ZAI_STR_FROM_ZSTR(entry->config_id);
goto next_entry;
} else if (zai_getenv_ex(name, buf, false) == ZAI_ENV_SUCCESS
} else if ((zai_getenv(name, &buf) == ZAI_ENV_SUCCESS
|| zai_config_get_cached_env_value(i, name_index, &buf))
&& zai_config_process_runtime_env(memoized, buf, in_startup, i, name_index)) {
goto next_entry;
} else if (entry && entry->source == DDOG_LIBRARY_CONFIG_SOURCE_LOCAL_STABLE_CONFIG
Expand All @@ -458,7 +461,9 @@ void zai_config_ini_rinit(void) {
}
}

if (memoized->env_config_fallback && memoized->env_config_fallback(buf, false) && zai_config_process_runtime_env(memoized, buf, in_startup, i, 0)) {
buf.ptr = buf_storage;
buf.len = sizeof(buf_storage);
if (memoized->env_config_fallback && memoized->env_config_fallback(&buf, false) && zai_config_process_runtime_env(memoized, buf, in_startup, i, 0)) {
goto next_entry;
}
}
Expand Down
Loading
Loading