Skip to content

feat(ffe): add Feature Flags debug logging - #4327

Merged
ethan-romero-dg merged 8 commits into
masterfrom
ethan.romero/FFL-3402/ffe-debug-logging
Oct 27, 2026
Merged

ethan-romero-dg merged 8 commits into
masterfrom
ethan.romero/FFL-3402/ffe-debug-logging

Conversation

@ethan-romero-dg

@ethan-romero-dg ethan-romero-dg commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

Make PHP Feature Flags diagnostics available under DD_TRACE_DEBUG=1, including the raw inputs, values, defaults, and errors needed to explain a flag result. Previously, evaluation logs were metadata-only at TRACE, which DD_TRACE_DEBUG suppressed by forcing DEBUG.

Follows the SDK Debug Logging Checklist.

  • Log native evaluation inputs/results and PHP SDK outcomes separately: targeting key, effective scalar attributes, raw values/defaults, variant, reason, error details, metadata, and configuration version where available. OpenFeature logs provider resolution and hook failures without claiming to observe the final returned value after application hooks. Large values are not intentionally truncated; SDK formatting failures produce an explicit diagnostic.
  • Report effective settings, configuration receipt/application/removal, usable/disabled/rejected counts and rejected keys, requested versus delivered environment, creation time, version, and whether an older configuration remains active. Add request initialization and ready/unavailable/recovered diagnostics.
  • Promote exposure diagnostics to DEBUG and describe enqueueing, suppression, queue overflow, flush attempts, successful or failed sidecar handoff, and drops. Request shutdown reports pending work and explicitly states that PHP does not wait for sidecar network delivery. The Remote Config shutdown diagnostic is gated on Feature Flags being enabled, preserving unrelated tracer debug output.
  • Guard raw-value formatting when debug is off; install OpenFeature diagnostic hooks only when debug is active. Preserve evaluation behavior when application logging/formatting fails, and guard native formatting, allocation, and timestamp failures. Emit a once-per-process warning that enabled debug logs contain raw evaluation data.
  • Preserve the PR's scalar-JSON/object type-mismatch handling before metric and exposure recording, so those signals agree with the PHP SDK outcome.

Enable DD_TRACE_DEBUG=1 before PHP starts. Native tracer logs use DD_TRACE_LOG_FILE or PHP's error log; the default PHP SDK logger writes to PHP's error log. Collect both destinations if configured separately. A custom SDK logger must allow DEBUG. With DD_TRACE_DEBUG=0 and no separate debug-level override, evaluation diagnostics are suppressed.

Diagnostics deferred to keep this PHP-only

The libdatadog submodule and pin, Cargo dependencies, and general Rust logger are unchanged; this PR adds no logging bridge. We opted not to add the following shared-library diagnostics because completing them requires changes inside libdatadog:

  • Fetch/failure/retry: complete transport/HTTP failure classification, retry delays/backoff, exhaustion, and recovery diagnostics from the shared Remote Config transport.
  • Usable configuration/identity: individual native flag-parser rejection causes and complete unchanged-response/304 diagnostics at DEBUG. PHP reports rejected keys/counts and envelope errors, but the shared evaluator does not expose each compiled flag's rejection cause.
  • Readiness/recovery: fetch-failure-driven stale/recovered transitions. PHP reports configuration arrival, removal, and restoration; shared transport state is not fully exposed.
  • Exposure lifecycle: complete sidecar deduplication, batch/flush counts, network delivery outcomes, and retry/drop decisions. A successful PHP-to-sidecar handoff is not confirmation of delivery to intake.
  • Shutdown outcome: complete shared network cancellation, drain/timeout, and final drop outcomes. PHP reports its request teardown and lack of network drain without claiming downstream delivery completed.

Existing tracer and sidecar logs remain available, but the deferred diagnostics above are not completed by this PR.

Validation

  • Shutdown-log gate: the three reported regressions (die_in_sandbox, startup_logging_json_config, span_on_close) now pass with multiple observers enabled. Combined with the FFE PHPTs: 18 passed, 2 environment-dependent skips. Dogfooding debug-on/off checks also passed and confirmed the shutdown log is retained when flagging/debug are enabled. These local checks used a non-ASAN build; ASAN CI remains to be verified.

  • Rebuilt the modified extension against the unchanged pinned libdatadog.

  • Focused PHP suites: 39 Feature Flags tests / 110 assertions and 47 OpenFeature tests / 104 assertions, including throwing loggers, formatting failures, hook errors, and debug-off formatting guards.

  • Latest native FFE PHPT run: 15 passed, 2 environment-dependent tests skipped. Coverage includes raw evaluation data, DEBUG gating, rejected configurations, object mismatches, exposure overflow/handoff, the raw-data warning, and a failing /dev/full log sink. Agent/RC lifecycle and environment-mismatch scenarios passed separately in the earlier focused run; the Unix-Agent metrics fixture was not exercised.

  • 10 focused Rust tests passed: five PHP configuration/evaluation tests and five logger tests.

  • Ran the ffe-dogfooding PHP/OpenFeature app on PHP 8.3.33 with OpenFeature SDK 2.3.0 and local Agent/Remote Config test fixtures. Seven cases passed with debug enabled and again with debug disabled: boolean, integer, float, string, nested object, missing flag, and wrong type. Debug-on logs retained a complete 4 KB value and targeting context; debug-off produced zero debug lines with the expected evaluation results. An evaluation before configuration arrived returned its expected default.

Validation is focused, not a full SDK/platform or managed-backend certification. The app runs did not inject malloc/vsnprintf failures, measure debug-off allocations, or certify downstream sidecar delivery. PHP 7 was not rerun for these latest changes.

Reviewer checklist

  • Test coverage seems ok.
  • Appropriate labels assigned.

FFE reviewer selection

Reviewers: @btthomas, @typotter

Why: Guided and observable onboarding — This PR adds local Feature Flags debug logging to the PHP SDK, shown when DD_TRACE_DEBUG=1 is set. Reviewers come from the matched pod.

🤖 Automated selection.

Selection details

Initiative: project / guided-onboarding

Why this match: This PR adds local Feature Flags debug logging to the PHP SDK, shown when DD_TRACE_DEBUG=1 is set. The logs cover evaluation inputs and results, configuration receipt, application, removal and rejection, provider readiness (ready, unavailable, recovered), OpenFeature resolution diagnostics, exposure handoff and shutdown state. The point is to let customers check their SDK setup and see why a flag returned the value it did. That is SDK lifecycle and local debug-logging work under guided-onboarding. It is a concrete diagnostic capability, so it goes to that feature project rather than customer-onboarding-support. The catalog has no fallback entries with supplemental_review enabled, so no additional review areas apply.

  • @btthomas — primary (sdk-onboarding): Matched pod member. Existing review coverage preserved.
  • @typotter — floating (sdk-onboarding): Coverage and knowledge sharing. Existing review coverage preserved.

The PR author was excluded. Existing reviewers and teams were preserved.

Debug or fix a problem

Workflow run · Bits Code session

  • Wrong project or pod? Clarify this PR’s purpose, or correct the reviewer registry and publish the change. Sheet edits take effect after publication.
  • Need another reviewer now? Request them in GitHub. This workflow only adds requests and keeps existing reviewers.
  • Stuck beyond five minutes or failed? Inspect the run’s failed step and available Bits session. After fixing the cause, rerun the workflow with this PR URL and dry_run=false. Unchanged completed runs are skipped. If the receipt says needs_reconciliation or a write outcome is uncertain, ask the workflow owner to reconcile GitHub and the receipt before retrying; do not delete the lock blindly.
  • Report a problem: Comment on the reviewer-routing skill PR with this PR URL, the workflow run link, and the expected initiative or reviewer correction.
Evidence and run details

✅ Complete — reviewer coverage verified. The workflow checked pending requests or submitted reviews on the evaluated head.

Generated asynchronously by the FFE reviewer-routing workflow.

Changed paths: components-rs/ffe.rs, components-rs/remote_config.rs, tracer/ffe.c, tracer/functions.c, src/api/FeatureFlags/Internal/EvaluationLog.php, src/api/FeatureFlags/Client.php, src/DDTrace/OpenFeature/DataDogProvider.php, src/api/FeatureFlags/README.md

Evaluated head: 5c61f9d5ddc30b2fe13d11df0872f882e43b8798

Registry revision: revision:1946aa2a-530e-487a-84a7-83aff44a1089

Pinned skill source: d214cfc65ae154b97d39af5cf32caf48aaec9bf0

Instructions: workflow-injected snapshot (no plugin loaded)

Instruction snapshot SHA-256: f23b2a88c13c0da90e2dd58246a14f257af91e2195c555039bd555b52e6f3516

@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Tests

⚠️ Warnings

Your PR has warnings. Please review the issues below.

❄️ 11 New flaky tests detected

tmp/build_extension/tests/ext/ffe/debug_logging.phpt (FFE debug logs describe configuration, raw evaluation and exposure outcomes) from php.tmp.build_extension.tests.ext.ffe
001+ AddressSanitizer:DEADLYSIGNAL
002+ =================================================================
003+ ==3971==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000000000 bp 0x7fff038ed2b0 sp 0x7fff038ed258 T0)
004+ ==3971==Hint: pc points to the zero page.
005+ ==3971==The signal is caused by a READ memory access.
006+ ==3971==Hint: address points to the zero page.
007+     #0 0x000000000000  (<unknown module>)
011+     #4 0x6ca5eca5de7f in serialize_process_tags tmp/build_extension/ext/process_tags.c:250:9
012+     #5 0x6ca5eca5cc76 in init_process_tags tmp/build_extension/ext/process_tags.c:290:5
013+     #6 0x6ca5eca5cc28 in datadog_process_tags_first_rinit tmp/build_extension/ext/process_tags.c:332:5
...
tmp/build_extension/tests/ext/ffe/debug_logging_agentless.phpt (FFE startup diagnostics report that agentless tracing disables Agent Remote Configuration) from php.tmp.build_extension.tests.ext.ffe
001+ AddressSanitizer:DEADLYSIGNAL
002+ =================================================================
001- bool(true)
002- bool(true)
003+ ==3849==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000000000 bp 0x7ffc66c73450 sp 0x7ffc66c733f8 T0)
004+ ==3849==Hint: pc points to the zero page.
005+ ==3849==The signal is caused by a READ memory access.
006+ ==3849==Hint: address points to the zero page.
007+     #0 0x000000000000  (<unknown module>)
011+     #4 0x70535ba5de7f in serialize_process_tags tmp/build_extension/ext/process_tags.c:250:9
...

View in Flaky Test Management

ℹ️ Info

No other issues found (see more)

🧪 All tests passed

🎯 Code Coverage (details)
• Patch Coverage: 100.00%
• Overall Coverage: 74.36% (+0.00%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 5c61f9d | Docs | View more details | Give us feedback!

@pr-commenter

pr-commenter Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Benchmarks [ tracer ]

Benchmark execution time: 2026-10-23 19:42:38

Comparing candidate commit 5c61f9d in PR branch ethan.romero/FFL-3402/ffe-debug-logging with baseline commit 2e9a4c5 in branch master.

📊 Benchmarking dashboard

Found 1 performance improvements and 4 performance regressions! Performance is the same for 187 metrics, 2 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:ContextPropagationBench/benchInject64Bit

  • 🟩 execution_time [-283.077ns; -140.923ns] or [-11.269%; -5.610%]

scenario:MessagePackSerializationBench/benchMessagePackSerialization

  • 🟥 execution_time [+3.968µs; +5.312µs] or [+3.616%; +4.841%]

scenario:SamplingRuleMatchingBench/benchRegexMatching1

  • 🟥 execution_time [+85.093ns; +178.907ns] or [+5.751%; +12.091%]

scenario:SamplingRuleMatchingBench/benchRegexMatching2

  • 🟥 execution_time [+42.367ns; +119.033ns] or [+2.820%; +7.922%]

scenario:SamplingRuleMatchingBench/benchRegexMatching4

  • 🟥 execution_time [+128.224ns; +200.376ns] or [+8.749%; +13.673%]

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:ComposerTelemetryBench/benchTelemetryParsing

  • unstable execution_time [-544.901ns; +944.901ns] or [-3.865%; +6.701%]

scenario:LaravelBench/benchLaravelDdprof-opcache

  • unstable execution_time [-654.213µs; +1353.253µs] or [-4.887%; +10.108%]

@bwoebi

bwoebi commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

DD_TRACE_DEBUG in dd-trace-php (non-sidecar) is deliberately relatively sparse (mostly to avoid a firehose of logging whenever anyone turns this on on a prod webserver). A lot of the details added here belong on the DD_TRACE_LOG_LEVEL=trace level.

@ethan-romero-dg

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T20:56:42.510985Z 53b4cc1 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 53b4cc1d7f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ethan-romero-dg
ethan-romero-dg marked this pull request as ready for review October 16, 2026 20:52
@ethan-romero-dg
ethan-romero-dg requested review from a team as code owners October 16, 2026 20:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53b4cc1d7f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@datadog-prod-us1-3 datadog-prod-us1-3 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bits Code Review: PASS

More details

The scalar-JSON object check returns TYPE_MISMATCH before metrics and exposures are recorded, keeping native evaluation results consistent with the PHP SDK. The completed static review produced no reportable findings.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit 53b4cc1

@ethan-romero-dg
ethan-romero-dg force-pushed the ethan.romero/FFL-3402/ffe-debug-logging branch 2 times, most recently from 1ea36c1 to 2d45605 Compare October 22, 2026 17:25
@bwoebi

bwoebi commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

As noted here: #4288 (comment) - please split this accordingly.

ethan-romero-dg and others added 8 commits October 23, 2026 18:19
Port the dd-trace-js Feature Flags debug logging (DataDog/dd-trace-js#10468)
to PHP. When debug logging is enabled, log provider setup, the Remote Config
subscription state at startup, configuration received/applied/removed,
per-evaluation details, and the exposure lifecycle. Logs carry identifiers,
counts and settings only, never flag values, targeting keys or attributes.

The "flagging provider is disabled" hint is logged on first evaluation
rather than at startup, so applications that never use feature flags get
no FFE debug output.

Environment: Datadog workspace

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
DD_TRACE_DEBUG is deliberately sparse, so per-evaluation and per-exposure
details move to DD_TRACE_LOG_LEVEL=trace (or debug,ddtrace=trace):

- DDTrace\ffe_evaluate logs one "evaluated {...}" line at trace level. It
  replaces the userland DebugLogger, so gating and destination follow the
  native logger like the rest of the tracer.
- Exposure loss stays visible at debug: buffer overflows are reported once
  per flush ("dropped N exposure(s)"), as are flushes without a sidecar.
- Successful flushes log one trace line; datadog_ffi_try already reports
  failures.

ddog_ffe_evaluate now rejects scalar JSON values for object flags with
TYPE_MISMATCH, as the PHP SDK already did. The native log line, evaluation
metric and exposure recording now match the SDK result, and such values
are no longer exposed.

Environment: Datadog workspace

Co-authored-by: ethan.romero <ethan.romero@datadoghq.com>
This PR adds Feature Flags logs at three layers with different switches
and destinations, but nothing told customers how to collect them. The
PHP SDK layer writes through error_log, while the extension and sidecar
write to DD_TRACE_LOG_FILE, and the sidecar writes nowhere else. The
usual DD_TRACE_DEBUG plus DD_TRACE_LOG_FILE setup therefore misses every
SDK evaluation line.

Document the switches, startup order, what each layer logs and where,
and how to filter. Every destination, format, and level was checked
against a fresh app built from this branch.

Environment: Datadog workspace

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Co-authored-by: Claude Code Claude Opus 5.5 <noreply@anthropic.com>
@ethan-romero-dg
ethan-romero-dg force-pushed the ethan.romero/FFL-3402/ffe-debug-logging branch from bde3395 to 5c61f9d Compare October 23, 2026 18:35
@ethan-romero-dg

Copy link
Copy Markdown
Contributor Author

As noted here: #4288 (comment) - please split this accordingly.

@bwoebi

Addressed: moved sdk_debug_logging.phpt into tests/api/Unit/FeatureFlags, using the existing PHPUnit bootstrap. Split object_type_mismatch.phpt: SDK returned-value/default assertions now live in NativeClientTest.php; the extension test calls DDTrace\ffe_evaluate() directly and checks native results and logging.

The other tests remain in tests/ext/ffe because they exercise extension configuration, evaluation, exposure handling, and logging behavior through native APIs. They no longer load SDK source files. I verified the extension suite runs without the repository’s src/ tree available.

@bwoebi bwoebi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me now, thanks!

@ethan-romero-dg
ethan-romero-dg merged commit 237bc6a into master Oct 27, 2026
2234 checks passed
@ethan-romero-dg
ethan-romero-dg deleted the ethan.romero/FFL-3402/ffe-debug-logging branch October 27, 2026 14:44
@github-actions github-actions Bot added this to the 1.27.0 milestone Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants