Skip to content

Make CCF stderr fatal-only and consistent - #8690

Merged
Amaury Chamayou (achamayou) merged 10 commits into
microsoft:mainfrom
achamayou:achamayou-issue-8623-make-stderr-output-exclusive-to-fatal-pa-257f5d
Nov 9, 2026
Merged

Amaury Chamayou (achamayou) merged 10 commits into
microsoft:mainfrom
achamayou:achamayou-issue-8623-make-stderr-output-exclusive-to-fatal-pa-257f5d

Conversation

@achamayou

Copy link
Copy Markdown
Member

Motivation

CCF's stderr does not consistently indicate fatal node failures, making fatal diagnostics difficult to capture separately from ordinary logs.
Closes #8623

Implementation summary

Adds a shared, flushed fatal-diagnostic writer for CCF-controlled fatal paths across startup, join, and steady-state operation.
Recoverable logs and fatal-severity-only entries remain on stdout, and stderr diagnostics for join failures exclude response bodies and snapshot exception details.
Documents that direct application/dependency writes and runtime-generated diagnostics are outside the contract.

Safety and compatibility

No consensus, KV, wire-format, or application API changes.
The stderr diagnostics avoid join response bodies and snapshot exception details; existing detailed stdout logs are preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
@achamayou
Amaury Chamayou (achamayou) requested a review from a team as a code owner November 5, 2026 22:41
Copilot AI balanced review requested due to automatic review settings November 5, 2026 22:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The tests bypass the production console sink and omit aborting worker exceptions, leaving stream routing, stderr diagnostics, and SIGABRT termination unprotected against regressions.

2 open issues
What changed in this PR

This PR addresses #8623 by making CCF-controlled fatal paths emit flushed stderr diagnostics while keeping ordinary logging on stdout.

Changes:

  • Adds a shared fatal-diagnostic writer across startup, join, and steady-state failure paths.
  • Excludes join response bodies and snapshot exception details from stderr.
  • Adds logging/runtime-control assertions and documents the stderr contract and its exclusions.

Custom instructions used

  • .github/copilot-instructions.md
  • .github/instructions/reviewing.instructions.md
File Description
src/​tasks/​worker.h Emits diagnostics before aborting on task exceptions.
src/​tasks/​worker.cpp Diagnoses exception-runtime handler resolution failure.
src/​node/​rpc/​frontend.h Diagnoses fatal transaction serialisation failure.
src/​node/​node_state.h Removes sensitive details from join stderr diagnostics.
src/​host/​test/​runtime_control.cpp Checks fatal and nonfatal stderr behaviour.
src/​host/​socket.h Emits diagnostics before fatal socket aborts.
src/​host/​runtime_control.h Uses the shared fatal-diagnostic writer.
src/​host/​run.cpp Adds startup, shutdown, and exception diagnostics.
src/​ds/​test/​logger.cpp Adds stderr-routing assertions.
src/​ds/​internal_logger.h Defines the shared, flushed diagnostic writer.
src/​ds/​files.h Standardises fatal file-read diagnostics.
doc/​build_apps/​logging.rst Defines stdout/stderr behaviour and scope.
CHANGELOG.md Announces the fatal-diagnostic contract.

Review effort: Balanced

Comment thread src/ds/test/logger.cpp Resolved
Comment thread src/tasks/worker.h Resolved
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Comment thread CHANGELOG.md Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Comment thread src/ds/internal_logger.h Resolved
Comment thread src/ds/files.h Resolved
Comment thread src/ds/files.h Resolved
Comment thread src/ds/test/logger.cpp Resolved
Comment thread src/tasks/test/basic_tasks.cpp Resolved
Comment thread CHANGELOG.md Resolved
Every report_fatal_error() call is already serialised by NodeState::lock and RuntimeControlImpl's mutex. Other write_fatal_diagnostic() callers can run concurrently, so each diagnostic is now emitted as a single stream insertion, which stdio's stream lock keeps whole. Add a regression test that writes from several threads to the real stderr and checks each line is intact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Copilot AI balanced review requested due to automatic review settings November 8, 2026 12:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The new diagnostic mislabels all 4xx responses as “Bad Request,” while regression tests do not verify required stderr output or response-body redaction.

5 open issues
3 previously missed issues

In code that hasn't changed since last review

Medium severity Verify join rejection diagnostics and response-body handling

src/​node/​node_state.h:1005

The join-rejection tests inspect stdout but discard the stderr validation result in tests/infra/network.py:1429, so removing this diagnostic or restoring the response body would still pass. Add a regression case with a distinctive response-body marker that asserts the fatal diagnostic is present on stderr, the marker is absent there, and the detailed stdout message remains available.

Medium severity Report the actual HTTP status instead of always saying Bad Request

src/​node/​node_state.h:1005

This handler receives every 4xx response, not just HTTP 400. The new stderr diagnostic therefore describes responses such as 401 or 404 as "Bad Request", giving operators an incorrect failure reason. Use ccf::http_status_str(status), as handle_join_unexpected_status_unsafe() already does.

Medium severity Assert the fatal diagnostic is written to stderr

tests/​e2e_operations.py:3137

This suppresses the new stderr diagnostic without asserting that it was emitted. The subsequent check_log_for_error_message() reads only stdout (tests/infra/node.py:1215), so removing the fatal-diagnostic write in src/host/run.cpp:443 would still leave this startup regression test passing. Assert the expected fatal line in stderr before ignoring it during shutdown.

1 resolved since last review

Review effort: Balanced

The test exercised libstdc++/glibc behaviour rather than CCF code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Copilot AI balanced review requested due to automatic review settings November 8, 2026 15:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Restore the original frontend LOG_FATAL text, check console loggers only for stdout/stderr routing, and split the aborting task test so the stderr capture needs no manual restore.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Copilot AI balanced review requested due to automatic review settings November 8, 2026 15:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

- Report the actual HTTP status text in the join failure diagnostic instead of always saying Bad Request.
- Emit a fatal diagnostic before an enclave runtime thread exception escapes and terminates the process.
- Check that rejected joiners write the join diagnostic to stderr without the response body, and stop ignoring join diagnostics on shutdown: rejected joiners are already removed from the network before shutdown checks.
- Capitalise the changelog sentence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Copilot AI balanced review requested due to automatic review settings November 8, 2026 15:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread src/node/node_state.h Resolved
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 9, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues remain for the change standardizing fatal diagnostics on stderr.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

The reworded report_fatal_error() call in db3db9b was not clang-formatted, failing the C/C++ format check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db72aaa070234686ab3bcc336e728baf
Copilot AI balanced review requested due to automatic review settings November 9, 2026 15:47
@cjen1-msft

Copy link
Copy Markdown
Contributor

Just to check the counterfactual: why not extend the LOG_FAIL_FMT, and LOG_FATAL_FMT to write to stderr? and then use them consistently?

Comment thread CHANGELOG.md Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

There are no active review issues blocking approval of the fatal stderr diagnostic changes.

No open issues 🎉

Review effort: Balanced

@achamayou

Copy link
Copy Markdown
Member Author

Just to check the counterfactual: why not extend the LOG_FAIL_FMT, and LOG_FATAL_FMT to write to stderr? and then use them consistently?

It would be just FATAL, rather than FAIL, which can happen more than once and which we should not log out to stderr. I don't have a strong reason not to do it, but I also don't see a strong reason to.

I envision stderr messages as shorter, higher guarantee-of-being-written-out strings, whereas LOG_FATAL_FMT can be more detailed, but goes through a more complicated sub-system with things like timestamps, configuration, and therefore higher chance of failure. Maybe it would be useful to draw a starker distinction and ensure stderr writing is always allocation-free.

But to recap, I think stderr writes should always, always happen on unprompted shutdown, and only then. Having them be a separate, as simple as possible path is attractive?

@achamayou
Amaury Chamayou (achamayou) added this pull request to the merge queue Nov 9, 2026
Merged via the queue into microsoft:main with commit 55987a2 Nov 9, 2026
12 checks passed
@achamayou
Amaury Chamayou (achamayou) deleted the achamayou-issue-8623-make-stderr-output-exclusive-to-fatal-pa-257f5d branch November 9, 2026 17:33
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.

Make stderr output exclusive to fatal paths and consistent across all fatal paths

3 participants