Skip to content

Add aspire resources and aspire logs CLI commands for polyglot testing#14122

Merged
davidfowl merged 10 commits intomainfrom
davidfowl/polyglot-testing
Jan 27, 2026
Merged

Add aspire resources and aspire logs CLI commands for polyglot testing#14122
davidfowl merged 10 commits intomainfrom
davidfowl/polyglot-testing

Conversation

@davidfowl
Copy link
Copy Markdown
Contributor

@davidfowl davidfowl commented Jan 25, 2026

Summary

This PR adds CLI commands for polyglot AppHost testing, enabling non-.NET languages (Python, TypeScript, etc.) to programmatically interact with running Aspire applications.

New Commands

aspire ps

List running AppHost processes with their dashboard URLs.

$ aspire ps
PATH                     PID    CLI_PID  DASHBOARD
TestShop.AppHost.csproj  66647  -        https://localhost:16319/login?t=...
thoughts/apphost.cs      26517  26500    https://localhost:17350/login?t=...
$ aspire ps --format json
[
  {
    "appHostPath": "/path/to/TestShop.AppHost.csproj",
    "appHostPid": 66647,
    "cliPid": null,
    "dashboardUrl": "https://localhost:16319/login?t=..."
  }
]

aspire resources

List resources in a running AppHost with their state, health, and endpoints.

$ aspire resources
NAME            TYPE                      STATE    HEALTH   ENDPOINTS
apigateway      Container                 Running  Healthy  http://localhost:57647
basketcache     Container                 Running  Healthy  tcp://localhost:57644
frontend        Project                   Running  Healthy  https://localhost:7269
postgres        Container                 Running  Healthy  tcp://localhost:57645
...
$ aspire resources --format json | jq '.resources[0].name'
"postgres"

Use --watch for streaming updates (NDJSON format):

$ aspire resources --watch --format json | jq '.name'
"frontend"
"postgres"
...

aspire logs

View logs from resources in a running AppHost.

$ aspire logs frontend --tail 5
[frontend] 2026-01-27T09:19:33Z info: Microsoft.Hosting.Lifetime[0]
[frontend] 2026-01-27T09:19:33Z       Hosting environment: Development
...
$ aspire logs frontend --tail 3 --format json
{
  "logs": [
    {
      "resourceName": "frontend",
      "content": "2026-01-27T09:19:33Z info: Microsoft.Hosting.Lifetime[0]",
      "isError": false
    },
    ...
  ]
}

Use --follow for streaming logs (NDJSON format):

$ aspire logs frontend --follow --format json | jq '.content'
"2026-01-27T09:19:33Z info: Microsoft.Hosting.Lifetime[0]"
...

JSON Output Format

Command Format jq Usage
ps --format json Array [...] jq '.[].appHostPath'
resources --format json {"resources": [...]} jq '.resources[].name'
resources --watch --format json NDJSON jq '.name'
logs --format json {"logs": [...]} jq '.logs[].content'
logs --follow --format json NDJSON jq '.content'

Pattern:

  • Snapshots = Wrapped JSON (single document, direct jq usage)
  • Streaming (--watch/--follow) = NDJSON (line-by-line processing)

Key Features

  • Socket-based discovery: Fast AppHost discovery via Unix domain sockets
  • JSON output: Machine-readable output for polyglot test frameworks
  • Streaming support: --watch for resources, --follow for logs
  • Tail support: --tail N to get last N log lines
  • Update notifications to stderr: Won't corrupt JSON output when piped

Related

  • Spec: docs/specs/polyglot-apphost-testing.md

Testing

  • 850 CLI unit tests passing
  • Manual E2E testing with TestShop AppHost
  • Verified jq compatibility for all JSON output formats

Fixes #8069

Copilot AI review requested due to automatic review settings January 25, 2026 23:19
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 25, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14122

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14122"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds new aspire resources and aspire logs CLI commands to support polyglot AppHost integration testing via structured JSON/NDJSON output over the existing auxiliary backchannel.

Changes:

  • Introduces aspire resources (snapshot + watch/NDJSON) and aspire logs (snapshot + follow/NDJSON) CLI commands.
  • Extends the auxiliary backchannel RPC surface and DTOs to expose richer resource snapshots and stream resource logs.
  • Shares the resource JSON schema between Dashboard and CLI, and adds an initial spec document plus end-to-end coverage.

Reviewed changes

Copilot reviewed 61 out of 64 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs Registers the new CLI commands for test DI.
tests/Aspire.Cli.EndToEnd.Tests/ResourcesCommandTests.cs Adds E2E coverage for aspire resources output (table + --json).
tests/Aspire.Cli.EndToEnd.Tests/LogsCommandTests.cs Adds E2E coverage for aspire logs <resource> output (text + --json).
src/Shared/Model/Serialization/ResourceJson.cs Moves resource JSON schema to a shared namespace and extends it with additional fields.
src/Aspire.Hosting/Backchannel/BackchannelDataTypes.cs Expands backchannel DTOs for richer resource snapshots and introduces log line DTO.
src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Implements richer resource snapshot mapping and adds log streaming/snapshot RPC methods.
src/Aspire.Dashboard/Model/TelemetryExportService.cs Updates Dashboard to reference shared resource JSON schema.
src/Aspire.Dashboard/Model/Serialization/ResourceJsonSerializerContext.cs Updates serializer context to use shared resource JSON schema types.
src/Aspire.Dashboard/Aspire.Dashboard.csproj Links shared ResourceJson.cs into Dashboard build.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf Adds localization entry for new Run --json help text.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.zh-Hant.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.zh-Hans.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.tr.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.ru.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.pt-BR.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.pl.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.ko.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.ja.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.it.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.fr.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.es.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.de.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/ResourcesCommandStrings.cs.xlf Adds localized strings for aspire resources.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.zh-Hant.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.zh-Hans.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.tr.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.ru.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.pt-BR.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.pl.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.ko.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.ja.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.it.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.fr.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.es.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.de.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/xlf/LogsCommandStrings.cs.xlf Adds localized strings for aspire logs.
src/Aspire.Cli/Resources/RunCommandStrings.resx Adds --json help text for aspire run --detach.
src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs Updates strongly-typed resources for Run strings.
src/Aspire.Cli/Resources/ResourcesCommandStrings.resx Adds resource strings for aspire resources.
src/Aspire.Cli/Resources/ResourcesCommandStrings.Designer.cs Adds strongly-typed resources for Resources strings.
src/Aspire.Cli/Resources/LogsCommandStrings.resx Adds resource strings for aspire logs.
src/Aspire.Cli/Resources/LogsCommandStrings.Designer.cs Adds strongly-typed resources for Logs strings.
src/Aspire.Cli/Program.cs Registers the new CLI commands in the host container.
src/Aspire.Cli/Commands/StopCommand.cs Refactors AppHost selection logic to use the shared resolver.
src/Aspire.Cli/Commands/RunCommand.cs Adds --json output support for detached runs via source-generated JSON serialization.
src/Aspire.Cli/Commands/RootCommand.cs Adds resources and logs to the CLI root command.
src/Aspire.Cli/Commands/ResourcesCommand.cs Implements aspire resources command (table, JSON, watch/NDJSON).
src/Aspire.Cli/Commands/LogsCommand.cs Implements aspire logs command (JSON/NDJSON, follow, file output).
src/Aspire.Cli/Backchannel/AppHostConnectionResolver.cs Adds reusable socket-first AppHost discovery/selection helper.
src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Adds RPC client wrappers for resource log streaming/snapshot retrieval.
src/Aspire.Cli/Aspire.Cli.csproj Links shared ResourceJson.cs into CLI build.
docs/specs/polyglot-apphost-testing.md Adds draft spec documenting CLI primitives, schemas, and wrapper concepts.
Files not reviewed (3)
  • src/Aspire.Cli/Resources/LogsCommandStrings.Designer.cs: Language not supported
  • src/Aspire.Cli/Resources/ResourcesCommandStrings.Designer.cs: Language not supported
  • src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs: Language not supported
Comments suppressed due to low confidence (3)

docs/specs/polyglot-apphost-testing.md:206

  • The documented aspire resources JSON schema uses fields like type, endpoints, and properties as an object, but the CLI currently serializes ResourceJson (e.g., resourceType, urls, and properties as an array of name/value pairs). Please align the spec’s examples/schema with the actual output (or adjust the CLI output to match the spec).
**Output (JSON):**
```json
{
  "resources": [
    {
      "name": "redis",
      "type": "Container",
      "state": "Running",
      "healthStatus": "Healthy",
      "endpoints": [
        {
          "name": "tcp",
          "url": "tcp://localhost:6379",
          "isInternal": false
        }
      ],
      "connectionString": "localhost:6379",
      "properties": {
        "container.image": "redis:7.4",
        "container.id": "abc123def456"
      }
    },
    {
      "name": "api",
      "type": "Project",
      "state": "Running",
      "healthStatus": "Healthy",
      "endpoints": [
        {
          "name": "http",
          "url": "http://localhost:5001",
          "isInternal": false
        },
        {
          "name": "https",
          "url": "https://localhost:5002",
          "isInternal": false
        }
      ],
      "connectionString": null,
      "properties": {
        "project.path": "/path/to/Api/Api.csproj"
      }
    }
  ]
}

aspire resources --watch

Streams resource snapshots as NDJSON (newline-delimited JSON).

aspire resources --watch [--project <path>]

Output (NDJSON):

{"name":"redis","type":"Container","state":"Starting","healthStatus":null,"endpoints":[],"connectionString":null}
{"name":"redis","type":"Container","state":"Running","healthStatus":"Healthy","endpoints":[{"name":"tcp","url":"tcp://localhost:6379"}],"connectionString":"localhost:6379"}
{"name":"api","type":"Project","state":"Starting","healthStatus":null,"endpoints":[]}
{"name":"api","type":"Project","state":"Running","healthStatus":"Healthy","endpoints":[{"name":"http","url":"http://localhost:5001"}]}
**docs/specs/polyglot-apphost-testing.md:314**
* The `aspire logs` section documents options/behaviors that aren’t implemented (`--tail`, `--timestamps`, and non-follow `aspire logs` returning all resources, plus a different JSON shape). Please either scope the spec to what’s implemented in this PR or mark the unimplemented flags/output shapes explicitly as future work to avoid misleading consumers.

aspire logs

Retrieves resource console logs from the ResourceLoggerService. These are the same logs displayed in the Aspire Dashboard's console logs view - stdout/stderr from containers, projects, and executables.

See GitHub Issue #8069 for the original feature request.

aspire logs [resource] [--project <path>] [--follow] [--output <dir>] [--json]

Arguments:

  • resource - Optional resource name. If omitted, shows logs for all resources.

Options:

  • --follow, -f - Stream logs in real-time (like docker logs -f)
  • --output <dir> - Write logs to files (one file per resource) instead of console
  • --tail <lines> - Number of lines to show from the end (default: all)
  • --timestamps - Include timestamps in output
  • --json - Output logs as JSON/NDJSON for programmatic consumption
  • --project <path> - Path to the AppHost project

Implementation Notes:

  • Uses auxiliary backchannel RPC to call WatchResourceLogsAsync on the running AppHost
  • Logs come from ResourceLoggerService.WatchAsync(resourceName)
  • Same log source as Dashboard's WatchResourceConsoleLogs gRPC method

JSON Output

Use --json for structured log output that's easy to parse programmatically.

Single snapshot (aspire logs --json):

{
  "logs": [
    {
      "resource": "redis",
      "lineNumber": 1,
      "content": "1:C 15 Jan 2024 10:30:14.123 * oO0OoO0OoO0Oo Redis is starting",
      "isError": false
    },
    {
      "resource": "api",
      "lineNumber": 1,
      "content": "info: Application started",
      "isError": false
    }
  ]
}

Streaming (aspire logs --json --follow):

Outputs NDJSON (one JSON object per line):

{"resource":"redis","timestamp":"2024-01-15T10:30:14.123Z","line":"Ready to accept connections","isError":false}
{"resource":"api","timestamp":"2024-01-15T10:30:15.456Z","line":"info: Application started","isError":false}
{"resource":"api","timestamp":"2024-01-15T10:30:15.789Z","line":"warn: Cache miss","isError":false}

Get All Logs

Retrieve logs from all resources at once. Useful for collecting diagnostic information after a test run.

# Get all logs from all resources
aspire logs

# Save all logs to files (one per resource)
aspire logs --output ./test-artifacts

Output (interleaved with resource prefix):

[redis] 1:C 15 Jan 2024 10:30:14.123 * oO0OoO0OoO0Oo Redis is starting
[redis] 1:M 15 Jan 2024 10:30:14.456 * Ready to accept connections
[api] info: Application started
[api] info: Listening on http://localhost:5001
[postgres] LOG: database system is ready to accept connections

Output (files with --output ./logs):

./logs/
  redis.log
  api.log
  postgres.log

Tail/Follow Logs

Stream logs in real-time, similar to docker logs -f or kubectl logs -f.

# Follow all logs
aspire logs --follow

# Follow logs for a specific resource
aspire logs api --follow

# Show last 50 lines then follow

src/Aspire.Cli/Commands/ResourcesCommand.cs:126

  • ExecuteSnapshotAsync breaks on the first repeated resource snapshot (!seenResources.Add(...)), which can return an incomplete resource list if some resources haven’t produced an initial snapshot yet (or if updates arrive before all initial resources are observed). Consider implementing a dedicated non-streaming RPC to fetch the current snapshot set, or keep consuming WatchResourceSnapshotsAsync until an idle timeout with no new resource names (and then emit the latest snapshot per resource).
            if (!seenResources.Add(snapshot.Name))
            {
                // We've already seen this resource, so we have a complete picture
                // Update to latest and break
                var existingIndex = resources.FindIndex(r => r.Name == snapshot.Name);
                if (existingIndex >= 0)
                {
                    resources[existingIndex] = resourceJson;
                }
                break;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Aspire.Cli/Commands/ResourcesCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/RunCommand.cs
Comment thread src/Aspire.Cli/Commands/LogsCommand.cs Outdated
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs
Comment thread docs/specs/polyglot-apphost-testing.md
@sebastienros
Copy link
Copy Markdown
Contributor

Suggest using --format json instead of --json.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 25, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 208a7ad:

Test Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording

📹 Recordings uploaded automatically from CI run #21392075478

@davidfowl davidfowl force-pushed the davidfowl/polyglot-testing branch 6 times, most recently from 7a5d300 to 29c3cb4 Compare January 26, 2026 05:43
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md Outdated
Comment thread docs/specs/polyglot-apphost-testing.md Outdated
Comment thread docs/specs/polyglot-apphost-testing.md Outdated
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread docs/specs/polyglot-apphost-testing.md
Comment thread src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Outdated
Comment thread src/Aspire.Cli/Backchannel/AppHostConnectionResolver.cs
Comment thread src/Aspire.Cli/Backchannel/AppHostConnectionResolver.cs
Comment thread src/Aspire.Cli/Commands/DoctorCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/LogsCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/PsCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/LogsCommand.cs
Comment thread src/Aspire.Cli/Commands/ResourcesCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/ResourcesCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/ResourcesCommand.cs Outdated
@davidfowl
Copy link
Copy Markdown
Contributor Author

CLI Comparison: Aspire vs Docker

Command Mapping

Feature Docker Aspire Notes
List containers/resources docker ps aspire ps / aspire resources ps lists AppHosts, resources lists resources within an AppHost
Follow logs docker logs -f aspire logs -f ✅ Matches Docker convention
JSON format docker ps --format json aspire ps --format json ✅ Similar (Docker also supports Go templates)
Filter by name docker ps -f name=xxx aspire resources <name> Aspire uses positional argument
Timestamps docker logs -t Not implemented
Tail lines docker logs -n 100 Not implemented
Since/Until docker logs --since Not implemented
Quiet mode docker ps -q Not implemented

✅ Aligned with Docker Conventions

  • -f / --follow for streaming logs
  • --format json for machine-readable output
  • Resource name as positional argument

📋 Potential Future Enhancements

Priority Feature Docker Equivalent
Medium aspire logs --tail N docker logs -n N
Low aspire logs --timestamps docker logs -t
Low aspire resources -q docker ps -q

These can be added in follow-up PRs based on user feedback.

Comment thread src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Outdated
Comment thread src/Aspire.Cli/Commands/DoctorCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/DoctorCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/DoctorCommand.cs Outdated
Comment thread src/Aspire.Cli/Commands/LogsCommand.cs
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs
Comment thread src/Aspire.Hosting/Backchannel/BackchannelDataTypes.cs Outdated
Comment thread src/Shared/Model/Serialization/ResourceJson.cs
Comment thread src/Shared/Model/Serialization/ResourceJson.cs Outdated
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented Jan 27, 2026

Need unit tests for the new and changed commands.

@davidfowl davidfowl force-pushed the davidfowl/polyglot-testing branch from 35d8afb to d062119 Compare January 27, 2026 07:23
This PR adds CLI commands for polyglot AppHost testing scenarios:

- `aspire ps` - List running Aspire AppHosts with process info and dashboard URLs
- `aspire resources [name]` - Show resources in a running AppHost (table or JSON)
- `aspire logs [resource]` - Stream or fetch logs from resources

- `--format json|table` output option for all commands
- `--follow` mode for streaming logs in real-time
- `--tail N` option to show last N log lines (like Docker)
- `--watch` mode for streaming resource updates
- Automatic AppHost discovery via backchannel sockets
- DCP replica name resolution (shows unique names for replicas)
- Non-ASCII character support in JSON output (Chinese, Japanese, etc.)

- Added `GetResourceSnapshotsAsync` RPC for fetching resource state
- Added `GetResourceLogsAsync` RPC with follow/snapshot modes
- Fixed `ResourceLogSource` to handle non-cancellable tokens
- Added `CommonErrorData` to JSON serialization context for StreamJsonRpc
- Fixed `ShowStatusAsync` to handle empty status text for JSON output

- Added unit tests for LogsCommand, ResourcesCommand, PsCommand
- Tests cover JSON serialization, option parsing, validation, error handling
@davidfowl davidfowl force-pushed the davidfowl/polyglot-testing branch from d062119 to ab7e7b0 Compare January 27, 2026 07:34
@davidfowl
Copy link
Copy Markdown
Contributor Author

Done

- Add EnsureConnected() helper to reduce duplication in AppHostAuxiliaryBackchannel
- Use JavaScriptEncoder.UnsafeRelaxedJsonEscaping for all CLI JSON output
- Use DisplayRawText instead of DisplayPlainText for JSON output
- Remove OSC 8 hyperlink formatting in PsCommand (terminals auto-detect URLs)
- Use DateTimeOffset consistently instead of DateTime for timestamps
- Add RelaxedEscaping property to all JSON contexts

// Lazy-initialized stderr console for update notifications
private static IAnsiConsole? s_stderrConsole;
private static IAnsiConsole StderrConsole => s_stderrConsole ??= AnsiConsole.Create(new AnsiConsoleSettings
Copy link
Copy Markdown
Member

@JamesNK JamesNK Jan 27, 2026

Choose a reason for hiding this comment

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

I needed to do this same thing to display first run experience: https://github.com/dotnet/aspire/pull/13963/changes#diff-f211b05473c1f49afd029a211c33b506235ee9e499189695eba9dd281f77fc69R314

I think we need to improve how we do this, but I'm ok with merging this as-is. Can improve after this PR and telemetry PR are merged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, we should consolidate the stderr console pattern. I'll follow up after both PRs merge to refactor this into a shared helper.

Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

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

I think unit tests around streamed JSON formatting would be good. Assure that it correctly follows line deliminted format.

@davidfowl davidfowl enabled auto-merge (squash) January 27, 2026 09:38
@davidfowl davidfowl merged commit c6d1188 into main Jan 27, 2026
330 checks passed
@davidfowl davidfowl deleted the davidfowl/polyglot-testing branch January 27, 2026 10:03
@dotnet-policy-service dotnet-policy-service Bot added this to the 13.2 milestone Jan 27, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an aspire logs [resource resource] command

5 participants