dotnet / aspire

Tools, templates, and packages to accelerate building observable, production-ready apps
https://learn.microsoft.com/dotnet/aspire
MIT License
3.94k stars 482 forks source link

Add `DebuggerDisplay` attribute to `DistributedApplicationModel` #6416

Open IEvangelist opened 1 month ago

IEvangelist commented 1 month ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

It's tedious to examine the DistributedApplicationModel when debugging.

Image

Describe the solution you'd like

It would be awesome if the type made use of the DebuggerDisplay so that at a glance, you could quickly understand what's all inside the model and what connection info each resource contains, etc.

Additional context

@davidfowl said that if I logged an issue that @JamesNK would make it so... 🤓

davidfowl commented 1 month ago

Look @JamesNK, your love language 😄

JamesNK commented 1 month ago

The app model already has debugger display attributes:

You can see these in effect in your screenshot. Without them you'd see type names and not much else when debugging.

I'm not sure what there is to improve more. Do you have specific examples?

IEvangelist commented 1 month ago

The app model already has debugger display attributes:

  • The model gives a count of the number of resources
  • Resources give their type and count of annotations
  • Annotations give their type and the most important informaiton

You can see these in effect in your screenshot. Without them you'd see type names and not much else when debugging.

I'm not sure what there is to improve more. Do you have specific examples?

Is the information for the URL not yet available, or anything like that? I was hoping that at a glance in addition to the minimal info displayed like counts, names and whatnot, that it might include URLs, ports, schemes, dependency graph, or is that info not available?

JamesNK commented 1 month ago

That's difficult because the model represents an abstraction of what could run. For example, if a resource has 5 replicas, how would we display statuses and endpoints for all 5 instances?

IEvangelist commented 1 month ago

Yeah, I understand it's difficult and there are likely edge cases, such as replicas. I was just hoping to be able to quickly see allocated endpoint URLs for named resources, at a glance. I'm learning the eventing APIs and as part of that I realized that there's an event for after endpoints have been allocated, and I wrote the following code just to help visualize the endpoints:

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using ResourceEndpoints = (string Name, System.Collections.Generic.List<Aspire.Hosting.ApplicationModel.EndpointAnnotation>);
using System.Text;

var builder = DistributedApplication.CreateBuilder(args);

_ = builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>(
    static (@event, cancellationToken) =>
{
    ResourceEndpoints[] endpoints = [ ..@event.Model
        .Resources
        .Select(static r => (r.Name, Endpoints: r.Annotations.Where(static a => a is EndpointAnnotation).Cast<EndpointAnnotation>().ToList()))
        .Where(static t => t.Endpoints.Count is > 0)
    ];

    var logger = @event.Services.GetRequiredService<ILogger<Program>>();

    logger.LogInformation("AfterEndpointsAllocatedEvent");

    var builder = new StringBuilder("Endpoints:");
    builder.AppendLine();

    foreach (var (name, annotations) in endpoints)
    {
        builder.AppendFormat("  {0}:", name);
        builder.AppendLine();

        foreach (var annotation in annotations)
        {
            builder.AppendFormat("    {0}", annotation.AllocatedEndpoint);
            builder.AppendLine();
        }

        builder.AppendLine();
    }

    logger.LogInformation("{Details}", builder.ToString());

    return Task.CompletedTask;
});

var cache = builder.AddRedis("cache");

var apiService = builder.AddProject<Projects.AspireApp69_ApiService>("apiservice");

builder.AddProject<Projects.AspireApp69_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(cache)
    .WaitFor(cache)
    .WithReference(apiService)
    .WaitFor(apiService);

builder.Build().Run();

On my machine, this logs the following - I'm just suggesting that we have something nice like this in the debugger display.

info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 9.0.0-rc.1.24511.1+2c1822cdfbec623cbdefbc711ece00f4eddb7490
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
      Application host directory is: C:\Users\david\source\repos\AspireApp69\AspireApp69.AppHost
info: Program[0]
      AfterEndpointsAllocatedEvent
info: Program[0]
      Endpoints:
        cache:
          tcp://localhost:62658

        apiservice:
          https://localhost:7417
          http://localhost:5352

        webfrontend:
          https://localhost:7205
          http://localhost:5285

info: Aspire.Hosting.DistributedApplication[0]
      Now listening on: https://localhost:17094
info: Aspire.Hosting.DistributedApplication[0]
      Login to the dashboard at https://localhost:17094/login?t=d437e077bf07b025490b0306161ad4c2
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application started. Press Ctrl+C to shut down.

Couldn't we conditionally display allocated endpoints for their corresponding named resources? Again, not sure what that would look like for replicas.