Skip to content

feat(aspire): resource-diagnostics helpers + retained log buffer (#6343)#6350

Merged
thomhurst merged 1 commit into
mainfrom
feat/6343-resource-diagnostics-buffer
Jul 1, 2026
Merged

feat(aspire): resource-diagnostics helpers + retained log buffer (#6343)#6350
thomhurst merged 1 commit into
mainfrom
feat/6343-resource-diagnostics-buffer

Conversation

@thomhurst

Copy link
Copy Markdown
Owner

Closes #6343.

Problem

Consumers who want a resource's logs on failure hand-roll ResourceLoggerService.WatchAsync(...). That pattern is racy: fetch logs only after a startup failure (e.g. in a catch) and a resource that has already exited returns "(no logs available)" — the backlog is gone.

What this adds

New public API on AspireFixture<T>:

// last N lines — from the retained buffer, so it works even after the resource exited
IReadOnlyList<string> logs = await fixture.GetResourceLogsAsync("chat", maxLines: 200);

// full per-resource diagnostic block: state, decoded exit code, health, deps, last-N logs
string report = await fixture.DumpResourceDiagnosticsAsync();

// state/exit introspection
ResourceSnapshot? snap = fixture.GetResourceSnapshot("chat"); // Name, State, ExitCode, StartedAt, StoppedAt, HealthStatus

Retained log buffer (opt-in): new AspireFixtureOptions.RetainResourceLogs + ResourceLogBufferCapacity (default 500 lines/resource). A bounded per-resource ring buffer (BoundedLogBuffer) subscribes before StartAsync via a single background pump shared with live forwarding, and is kept alive past teardown so post-mortem reads are reliable even for an exited/disposed resource. Scoped by the existing ResourceLogNames.

When retention is off, the helpers fall back to a best-effort live WatchAsync read (degraded — may race to empty). Both paths return raw log content.

Self-documenting failed boot: when ForwardResourceLogs is on (the "same opt-in as log forwarding" the issue calls for), a failed InitializeAsync emits a consolidated diagnostics dump to the progress log — covering even the failure paths that don't build their own diagnostics. The dump never masks the original exception (always rethrows).

Acceptance criteria

  • GetResourceLogsAsync returns buffered lines even for an exited resource (no "(no logs available)" race) when retention is enabled.
  • DumpResourceDiagnosticsAsync produces a single string with per-resource state + decoded exit code + last-N logs.
  • Buffer is bounded (configurable cap) to avoid unbounded memory.

Design notes

Tests

Pure (no-Docker) tests for BoundedLogBuffer rolling/cap semantics and the helpers' graceful behavior on an un-initialized fixture. TUnit.Aspire.Core builds net8/9/10; all pure Aspire tests green. Buffer-backed and snapshot-reading paths of a running app are exercised by the existing Docker-backed integration tests.

Reviewer notes

A self-review pass caught and fixed two issues before submission: the WatchAsync fallback in GetResourceLogsAsync now guards ObjectDisposedException (keeps the helper non-throwing under a concurrent teardown), and the buffer vs. fallback paths now return identically-formatted (raw) lines.

🤖 Generated with Claude Code

Add first-class post-mortem diagnostics to AspireFixture<T> so consumers stop
hand-rolling ResourceLoggerService.WatchAsync — which races to "(no logs
available)" when a resource has already exited.

New public API:
- GetResourceLogsAsync(name, maxLines): most-recent lines from the retained
  buffer when enabled (reliable even after exit/dispose), else a best-effort
  live WatchAsync read. Both paths now return raw Content.
- GetResourceSnapshot(name) -> new ResourceSnapshot record (name, state, exit
  code, start/stop timestamps, health).
- DumpResourceDiagnosticsAsync(): one report per waited-on resource (state,
  decoded exit code, health, deps, last-N logs); non-throwing.

New opt-in options: RetainResourceLogs + ResourceLogBufferCapacity (default
500). The bounded per-resource buffer (BoundedLogBuffer) subscribes before
StartAsync via a single background pump shared with live forwarding, and is
kept alive past teardown for post-mortem reads.

On startup failure, when ForwardResourceLogs is on, the fixture self-documents
by dumping consolidated diagnostics to the progress log (covers failure paths
that don't build their own), always rethrowing the original exception.

Pure (no-Docker) tests cover the buffer's rolling/cap semantics and the
helpers' graceful behavior on an un-initialized fixture.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 48 complexity

Metric Results
Complexity 48

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@thomhurst thomhurst merged commit 7bb643c into main Jul 1, 2026
13 checks passed
@thomhurst thomhurst deleted the feat/6343-resource-diagnostics-buffer branch July 1, 2026 18:01
This was referenced Jul 1, 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.

[TUnit.Aspire] First-class resource-diagnostics helper + retained log buffer

1 participant