From f83c88392392f9ff342237301501e2e1531cdb7b Mon Sep 17 00:00:00 2001 From: bm1549 Date: Fri, 27 Mar 2026 18:25:51 -0400 Subject: [PATCH 1/2] feat: emit startup logs when DD_TRACE_STARTUP_LOGS=true without DD_TRACE_DEBUG Previously, startup logs were only emitted when DD_TRACE_DEBUG was enabled. This decouples DD_TRACE_STARTUP_LOGS from DD_TRACE_DEBUG so that setting DD_TRACE_STARTUP_LOGS=true emits startup configuration logs regardless of the debug flag or SAPI type. Also changes the default of DD_TRACE_STARTUP_LOGS from true to false. Co-Authored-By: Claude Opus 4.6 (1M context) --- ext/configuration.h | 2 +- ext/logging.c | 21 ++++++- metadata/supported-configurations.json | 2 +- tests/ext/startup_logging_without_debug.phpt | 58 ++++++++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 tests/ext/startup_logging_without_debug.phpt diff --git a/ext/configuration.h b/ext/configuration.h index 599d2ed4eb8..a9e77e8bb0a 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -206,7 +206,7 @@ enum ddtrace_sidecar_connection_mode { CONFIG(INT, DD_TELEMETRY_HEARTBEAT_INTERVAL, "60", .ini_change = zai_config_system_ini_change) \ CONFIG(INT, DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS, "0") \ CONFIG(INT, DD_TRACE_SHUTDOWN_TIMEOUT, "5000", .ini_change = zai_config_system_ini_change) \ - CONFIG(BOOL, DD_TRACE_STARTUP_LOGS, "true") \ + CONFIG(BOOL, DD_TRACE_STARTUP_LOGS, "false") \ CONFIG(BOOL, DD_TRACE_ONCE_LOGS, "true") \ CONFIG(INT, DD_TRACE_AGENT_RETRIES, "0", .ini_change = zai_config_system_ini_change) \ CONFIG(BOOL, DD_TRACE_AGENT_DEBUG_VERBOSE_CURL, "false", .ini_change = zai_config_system_ini_change) \ diff --git a/ext/logging.c b/ext/logging.c index c0e2f9fb717..e9400581732 100644 --- a/ext/logging.c +++ b/ext/logging.c @@ -7,7 +7,6 @@ #include #include "configuration.h" -#include
#ifndef _WIN32 #define atomic_compare_exchange_strong_int atomic_compare_exchange_strong @@ -16,12 +15,19 @@ static void dd_log_set_level(bool debug) { bool once = runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS(); + bool startup = runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS(); if (debug) { - if (strcmp("cli", sapi_module.name) != 0 && (runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS())) { + if (startup) { ddog_set_log_level(DDOG_CHARSLICE_C("debug"), once); } else { ddog_set_log_level(DDOG_CHARSLICE_C("debug,startup=error"), once); } + } else if (startup) { + zend_string *level = runtime_config_first_init ? get_DD_TRACE_LOG_LEVEL() : get_global_DD_TRACE_LOG_LEVEL(); + char buf[256]; + int len = snprintf(buf, sizeof(buf), "%s,startup=info", ZSTR_VAL(level)); + if (len >= (int)sizeof(buf)) len = (int)sizeof(buf) - 1; + ddog_set_log_level((ddog_CharSlice){.ptr = buf, .len = len}, once); } else if (runtime_config_first_init) { ddog_set_log_level(dd_zend_string_to_CharSlice(get_DD_TRACE_LOG_LEVEL()), once); } else if (zend_string_equals_literal_ci(Z_STR(zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_LOG_LEVEL].decoded_value), "error")) { @@ -239,7 +245,16 @@ bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value, zend_str return true; } - ddog_set_log_level(dd_zend_string_to_CharSlice(Z_STR_P(new_value)), runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS()); + bool once = runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS(); + bool startup = runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS(); + if (startup) { + char buf[256]; + int len = snprintf(buf, sizeof(buf), "%s,startup=info", Z_STRVAL_P(new_value)); + if (len >= (int)sizeof(buf)) len = (int)sizeof(buf) - 1; + ddog_set_log_level((ddog_CharSlice){.ptr = buf, .len = len}, once); + } else { + ddog_set_log_level(dd_zend_string_to_CharSlice(Z_STR_P(new_value)), once); + } return true; } diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index f4a3c4d0644..a3256a5dda6 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -2288,7 +2288,7 @@ { "implementation": "C", "type": "boolean", - "default": "true" + "default": "false" } ], "DD_TRACE_STRIPE_ANALYTICS_ENABLED": [ diff --git a/tests/ext/startup_logging_without_debug.phpt b/tests/ext/startup_logging_without_debug.phpt new file mode 100644 index 00000000000..70996c15a1a --- /dev/null +++ b/tests/ext/startup_logging_without_debug.phpt @@ -0,0 +1,58 @@ +--TEST-- +Startup logging is enabled without DD_TRACE_DEBUG +--SKIPIF-- + +--FILE-- + 1, +]); + +// Ignore any Agent connection errors for now +unset($logs['agent_error']); +// Ignore sidecar config as it depends on specific versions of PHP for now +unset($logs['sidecar_trace_sender']); + +dd_dump_startup_logs($logs); +?> +--EXPECTF-- +datadog.trace.sources_path_reachable: false +date: "%s" +os_name: "%s" +os_version: "%s" +version: "%s" +lang: "php" +lang_version: "%s" +env: null +enabled: true +service: null +enabled_cli: %s +agent_url: "%s" +debug: false +analytics_enabled: false +sample_rate: -1 +sampling_rules: [] +tags: [] +service_mapping: [] +distributed_tracing_enabled: true +dd_version: null +architecture: "%s" +instrumentation_telemetry_enabled: true +sapi: "cgi-fcgi" +datadog.trace.sources_path: null +open_basedir_configured: false +uri_fragment_regex: null +uri_mapping_incoming: null +uri_mapping_outgoing: null +auto_flush_enabled: false +generate_root_span: true +http_client_split_by_domain: false +measure_compile_time: true +report_hostname_on_root_span: false +traced_internal_functions: null +enabled_from_env: true +opcache.file_cache: null +dynamic_instrumentation_enabled: false +exception_replay_enabled: false +loaded_by_ssi: false From 493a6340a1c3679b14ff02b3b8fb395555344d2d Mon Sep 17 00:00:00 2001 From: bm1549 Date: Fri, 27 Mar 2026 18:29:14 -0400 Subject: [PATCH 2/2] feat: write startup logs directly to stderr when DD_TRACE_STARTUP_LOGS=true Instead of routing startup logs through the tracing log system (which goes to PHP's error_log), write them directly to stderr when DD_TRACE_STARTUP_LOGS is enabled. This ensures startup configuration is always visible regardless of PHP's error_log setting. The LOGEV(STARTUP, ...) path is preserved for when DD_TRACE_DEBUG enables it through the log level system. Co-Authored-By: Claude Opus 4.6 (1M context) --- ext/logging.c | 19 ++-------------- ext/startup_logging.c | 53 ++++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/ext/logging.c b/ext/logging.c index e9400581732..0f4d040a1fe 100644 --- a/ext/logging.c +++ b/ext/logging.c @@ -15,19 +15,13 @@ static void dd_log_set_level(bool debug) { bool once = runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS(); - bool startup = runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS(); if (debug) { + bool startup = runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS(); if (startup) { ddog_set_log_level(DDOG_CHARSLICE_C("debug"), once); } else { ddog_set_log_level(DDOG_CHARSLICE_C("debug,startup=error"), once); } - } else if (startup) { - zend_string *level = runtime_config_first_init ? get_DD_TRACE_LOG_LEVEL() : get_global_DD_TRACE_LOG_LEVEL(); - char buf[256]; - int len = snprintf(buf, sizeof(buf), "%s,startup=info", ZSTR_VAL(level)); - if (len >= (int)sizeof(buf)) len = (int)sizeof(buf) - 1; - ddog_set_log_level((ddog_CharSlice){.ptr = buf, .len = len}, once); } else if (runtime_config_first_init) { ddog_set_log_level(dd_zend_string_to_CharSlice(get_DD_TRACE_LOG_LEVEL()), once); } else if (zend_string_equals_literal_ci(Z_STR(zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_LOG_LEVEL].decoded_value), "error")) { @@ -245,16 +239,7 @@ bool ddtrace_alter_dd_trace_log_level(zval *old_value, zval *new_value, zend_str return true; } - bool once = runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS(); - bool startup = runtime_config_first_init ? get_DD_TRACE_STARTUP_LOGS() : get_global_DD_TRACE_STARTUP_LOGS(); - if (startup) { - char buf[256]; - int len = snprintf(buf, sizeof(buf), "%s,startup=info", Z_STRVAL_P(new_value)); - if (len >= (int)sizeof(buf)) len = (int)sizeof(buf) - 1; - ddog_set_log_level((ddog_CharSlice){.ptr = buf, .len = len}, once); - } else { - ddog_set_log_level(dd_zend_string_to_CharSlice(Z_STR_P(new_value)), once); - } + ddog_set_log_level(dd_zend_string_to_CharSlice(Z_STR_P(new_value)), runtime_config_first_init ? get_DD_TRACE_ONCE_LOGS() : get_global_DD_TRACE_ONCE_LOGS()); return true; } diff --git a/ext/startup_logging.c b/ext/startup_logging.c index 34afbbfc34b..c88df4b0d3f 100644 --- a/ext/startup_logging.c +++ b/ext/startup_logging.c @@ -363,31 +363,58 @@ static void _dd_print_values_to_log(HashTable *ht, void (*log)(const char *forma ZEND_HASH_FOREACH_END(); } +static void _dd_log_to_stderr(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + // Only show startup logs on the first request void ddtrace_startup_logging_first_rinit(void) { - LOGEV(STARTUP, { - HashTable *ht; - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, DDTRACE_STARTUP_STAT_COUNT, NULL, ZVAL_PTR_DTOR, 0); + bool to_stderr = get_DD_TRACE_STARTUP_LOGS(); + bool to_log = ddog_shall_log(DDOG_LOG_STARTUP); + if (!to_stderr && !to_log) { + return; + } - ddtrace_startup_diagnostics(ht, true); + HashTable *ht; + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, DDTRACE_STARTUP_STAT_COUNT, NULL, ZVAL_PTR_DTOR, 0); + + ddtrace_startup_diagnostics(ht, true); + LOGEV(STARTUP, { _dd_print_values_to_log(ht, log); - _dd_get_startup_config(ht); + }) + + _dd_get_startup_config(ht); + + smart_str buf = {0}; + _dd_serialize_json(ht, &buf, 0); - smart_str buf = {0}; - _dd_serialize_json(ht, &buf, 0); + if (to_stderr) { + _dd_log_to_stderr("DATADOG TRACER CONFIGURATION - %s", ZSTR_VAL(buf.s)); + _dd_log_to_stderr("For additional diagnostic checks such as Agent connectivity, see the 'ddtrace' section of a phpinfo() " + "page. Alternatively set DD_TRACE_DEBUG=Error,Startup to add diagnostic checks to the error logs on the first request " + "of a new PHP process. Set DD_TRACE_STARTUP_LOGS=0 to disable this tracer configuration message."); + if (get_DD_OPENAI_LOGS_ENABLED()) { + _dd_log_to_stderr("Note that DD_OPENAI_LOGS_ENABLED=1 may be changed or removed in any release."); + } + } + + LOGEV(STARTUP, { log("DATADOG TRACER CONFIGURATION - %s", ZSTR_VAL(buf.s)); log("For additional diagnostic checks such as Agent connectivity, see the 'ddtrace' section of a phpinfo() " "page. Alternatively set DD_TRACE_DEBUG=Error,Startup to add diagnostic checks to the error logs on the first request " "of a new PHP process. Set DD_TRACE_STARTUP_LOGS=0 to disable this tracer configuration message."); - if (get_DD_OPENAI_LOGS_ENABLED()) { log("Note that DD_OPENAI_LOGS_ENABLED=1 may be changed or removed in any release."); } + }) - smart_str_free(&buf); + smart_str_free(&buf); - zend_hash_destroy(ht); - FREE_HASHTABLE(ht); - }) + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); }