Spyglass.AspNetCore 0.2.1

dotnet add package Spyglass.AspNetCore --version 0.2.1
                    
NuGet\Install-Package Spyglass.AspNetCore -Version 0.2.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Spyglass.AspNetCore" Version="0.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Spyglass.AspNetCore" Version="0.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Spyglass.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Spyglass.AspNetCore --version 0.2.1
                    
#r "nuget: Spyglass.AspNetCore, 0.2.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Spyglass.AspNetCore@0.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Spyglass.AspNetCore&version=0.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Spyglass.AspNetCore&version=0.2.1
                    
Install as a Cake Tool

Spyglass

A lightweight, embeddable dashboard for ASP.NET Core applications — add visibility into your running app with a single line of code.

Installation

dotnet add package Spyglass.AspNetCore

Getting started

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSpyglass();
builder.Services.AddHealthChecks(); // optional — health panel populates automatically

var app = builder.Build();

app.MapSpyglassDashboard();

app.Run();

Navigate to /spyglass to see the dashboard. The route is configurable:

app.MapSpyglassDashboard("/admin/status");

Monitoring external dependencies

Register the services your app depends on and Spyglass will show their connectivity status. Probes are lightweight TCP + protocol handshakes — no client libraries are pulled into your app.

builder.Services.AddSpyglass()
    .AddDependencyProbes(probes => probes
        .AddPostgres()
        .AddMongoDb()
        .AddRedis()
        .AddKafka()
        .AddRabbitMq());

Custom probes are supported for any dependency. The probe receives a CancellationToken and should return true for healthy, false for unhealthy, or throw to surface an error message.

builder.Services.AddSpyglass()
    .AddDependencyProbes(probes => probes
        .AddDependency("Orders API", "https://orders.internal/health", async ct =>
        {
            using var http = new HttpClient();
            var response = await http.GetAsync("https://orders.internal/health", ct);
            return response.IsSuccessStatusCode;
        }));

Features

Overview

Live request metrics (total, req/s, avg and max response time, active requests, 4xx/5xx error counts), memory and CPU usage, GC statistics, thread pool utilization, and server uptime. Each stat card displays a canvas sparkline backed by a 300-sample (5-minute) ring buffer sampled every second. Health checks, dependencies, and (when log capture is enabled) a live Warnings & Errors feed are shown alongside a compact view of registered configuration keys and services. Metrics update on a configurable interval (1 s – 30 s) with a live countdown and pause control.

Health checks

Results of all registered IHealthCheck implementations with status (Healthy, Degraded, Unhealthy) and description text. Populates automatically — no additional registration required.

Dependencies

Real-time connectivity status for external services. Built-in probes for Postgres, MongoDB, Redis, Kafka, RabbitMQ, MySQL/MariaDB, SQL Server, Memcached, NATS, and Elasticsearch/OpenSearch. Custom probes supported for any other service. Probe results are cached for 5 seconds to avoid hammering slow or unreachable targets on every poll. Failure messages distinguish between connection refused, timeout, and DNS/host not found.

Request log

Capture every incoming HTTP request — path, method, status code, response time, and timestamp — in a fixed-size in-memory ring buffer and stream them live to the dashboard. Enable with .AddRequestLog():

builder.Services.AddSpyglass()
    .AddRequestLog();

The Requests page shows entries in a scrollable table with:

  • Status class filter chips (1xx / 2xx / 3xx / 4xx / 5xx / Err) — toggle which classes are visible; each chip is colour-coded to match its status badge
  • Path search — real-time substring filter
  • Sortable columns — Time, Method, Path, Status, Elapsed; default is newest-first
  • Auto-scroll — follows the tail as new entries arrive; disabled automatically when a non-time sort is active

Up to 2 000 entries are retained. The current buffer is loaded as a snapshot when a client connects, then live entries are pushed via SignalR in 250 ms batches. Spyglass's own routes are never captured.

Logs

Real-time log streaming from the application's own logger. Log capture is opt-in — enable it with .AddLogging() on the builder:

builder.Services.AddSpyglass()
    .AddLogging();

Entries are streamed to connected clients in batches via SignalR. The page supports:

  • Per-level filter chips and category search
  • Virtual scrolling — renders only visible rows regardless of entry count
  • Click any row to expand the full message and exception text in a side panel
  • Up to 10 000 entries retained in memory, with a 10-minute TTL

The minimum capture level defaults to Information. Pass a LogLevel to raise it — the UI automatically disables filter chips for levels below the minimum:

builder.Services.AddSpyglass()
    .AddLogging(LogLevel.Warning);

When logging is not enabled, the Logs tab is visually disabled and the Warnings & Errors widget is hidden from the Overview page.

Log capture surfaces what your application already logs. Sensitive data should be excluded at the source by configuring log levels and scopes in the application itself.

Endpoints

Browse registered HTTP endpoints, SignalR hubs, and gRPC services. Each endpoint type is opt-in — enable one or more on the builder:

builder.Services.AddSpyglass()
    .AddHttpRoutes()   // HTTP endpoints
    .AddSignalR()      // SignalR hubs
    .AddGrpc();        // gRPC services

When two or more types are enabled, the Endpoints page shows an inner tab bar to switch between them. When none are enabled, the Endpoints tab is visually disabled.

HTTP routes are grouped by controller name (MVC/API controllers) or by the first path segment (minimal APIs). Groups are sorted alphabetically; routes within each group are sorted by path then method. SignalR hub routes are excluded — they appear in the SignalR tab instead. Each route has a collapsible try/execute panel pre-filled with auto-generated example values — send requests directly from the dashboard without leaving the browser. Required parameters (non-nullable value types or [Required]-annotated) are marked with * and validated before sending. System.ComponentModel.DataAnnotations constraint attributes ([Range], [StringLength], [MaxLength], [MinLength], [RegularExpression]) are displayed as hint text below the input. For endpoints with a structured request body, a body builder generates a typed form field for each property (checkbox for booleans, date/time picker for DateTime, numeric input for numbers, dropdown for enums, etc.). A Raw toggle switches to a plain JSON textarea at any time; the two views stay in sync.

SignalR hubs are discovered automatically from EndpointDataSource. Each hub entry lists all public hub methods with parameter types, return types, and whether the method streams from server to client (IAsyncEnumerable<T> or ChannelReader<T> return types are detected automatically). An interactive hub listener panel lets you connect to any registered hub directly from the dashboard: each subscribed topic gets its own scrollable column. For typed hubs (Hub<TClient>), available client-side method names are pre-populated from the TClient interface via reflection. You can also invoke any server-side hub method by name with an optional JSON argument. Streaming methods (detected from their return type) use connection.stream() automatically.

gRPC services are discovered via reflection — no compile-time dependency on Grpc.AspNetCore is required in the host app. Each service entry lists all methods with request/response message types and the streaming mode (Unary, ClientStreaming, ServerStreaming, Bidirectional). Click any method row to expand a try panel:

  • Unary — enter a JSON request body, click Send, see the single response
  • Client streaming — enter a JSON array of request messages; all are sent to the service in order before the response is returned
  • Server streaming — enter a single JSON request; each message the server emits is shown as a numbered block
  • Bidirectional — enter a JSON array of request messages; one response block is shown per message

The request body textarea is pre-populated with all the message's fields set to their default values. The server-side proxy resolves the service implementation directly from DI — no HTTP/2 or gRPC-Web infrastructure is required.

Auth tab is always present in the tab bar on the Endpoints page (no separate opt-in call required). It shows:

  • All registered authentication schemes with handler type and display name; JWT Bearer schemes additionally surface their configured Authority and Audience
  • Named authorization policies showing required roles and whether authentication is required
  • DefaultPolicy and FallbackPolicy from AuthorizationOptions (below the named-policies table), when configured
  • The active JWT bearer token (read from localStorage), decoded inline — exp, iat, nbf, and auth_time claims are formatted as human-readable local dates with relative suffixes (e.g. "in 4 min", "2 h ago")

Benchmark

Send load against any endpoint in the app and see latency and throughput stats in real time. Enable the tab with .AddBenchmark():

builder.Services.AddSpyglass()
    .AddBenchmark();

Three run modes are available:

  • Count — fires a fixed number of requests, with an optional warm-up phase
  • Duration — fires for a fixed time window at a configured rate (req/s) and concurrency
  • Free Run — fires indefinitely until you press Stop; you can pause at any time, adjust the URL, method, rate, concurrency, or body, and then resume

Throughput is modelled as rate × concurrency: on each rate tick all available concurrency slots are filled, so setting rate = 10 and concurrency = 5 targets 50 req/s.

If .AddHttpRoutes() is also enabled, a route picker appears above the URL field. Selecting a route pre-fills the URL (with example parameter values substituted), method, and body.

Results are updated every 500 ms during a run and include latency percentiles (min / mean / std dev / p50 / p95 / p99 / p99.9 / max / first response), throughput (total requests / wall time / req/s / target req/s / success rate), and a status-code class breakdown (2xx / 4xx / 5xx / network errors). A latency histogram is shown below the stat cards.

Outgoing HTTP

Capture all outgoing HTTP calls made through IHttpClientFactory-registered clients. Enable with .AddOutgoingHttp():

builder.Services.AddSpyglass()
    .AddOutgoingHttp();

The Outgoing page captures the named client, HTTP method, URL, response status code, latency, and whether the request resulted in an error. Up to 500 entries are retained. The current buffer is loaded as a snapshot when a client connects, then live entries are pushed via SignalR in 250 ms batches.

The page includes status class filter chips, URL and client name search filters, sortable columns, auto-scroll, and a click-to-expand detail panel. Only clients registered through IHttpClientFactory are captured — manually constructed HttpClient instances are not.

Configuration

Browse all configuration keys registered in the app. Filterable by provider (appsettings.json, environment variables, etc.), searchable by key or value, and sortable by any column.

In non-production environments, values whose key contains a sensitive term — password, secret, token, apikey, connectionstring, credentials, private_key, etc. — are shown as dots with a click-to-reveal toggle. No data is hidden from the server; values are only de-emphasised in the browser to reduce accidental on-screen exposure.

Services

Browse all services registered in the DI container. Searchable and sortable. Columns include service type, implementation type, namespace, lifetime, number of constructor dependencies (Injects), and number of services that depend on it (Dependents). Click any row to expand a detail panel showing the full constructor dependency list with each dependency's resolved lifetime.

Metadata badges appear inline:

  • hosted — service implements IHostedService
  • disposable — service implements IDisposable or IAsyncDisposable
  • open-generic — registered as an open generic (e.g. IRepository<>)
  • ×N — multiple registrations of the same service type
  • keyed service key — for ASP.NET Core 8 keyed services

The summary strip shows a lifetime breakdown and a count of services with detected issues.

Lifetime issue detection:

  • Captive dependency — a Scoped or Transient service injected into a Singleton
  • Root-scope capture — a Singleton that injects IServiceProvider (calling GetService on the root container can resolve Scoped services outside a scope)

Autofac integration

If your app uses Autofac as the DI container, install the companion package to surface native Autofac registrations in the Services tab alongside your IServiceCollection services:

dotnet add package Spyglass.AspNetCore.Autofac

Configure Autofac as the service provider factory and call .AddAutofac() on the Spyglass builder:

using Autofac;
using Autofac.Extensions.DependencyInjection;

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

builder.Services.AddSpyglass()
    .AddAutofac();

builder.Host.ConfigureContainer<ContainerBuilder>(container =>
{
    container.RegisterType<SlackNotifier>().As<INotifier>().SingleInstance();
    container.RegisterType<AuditLog>().As<IAuditLog>().InstancePerLifetimeScope();
    container.RegisterType<FeatureFlags>().As<IFeatureFlags>().InstancePerDependency();
});

Autofac-native registrations appear in the Services tab tagged with an autofac source badge. Their lifetimes are shown verbatim — SingleInstance, InstancePerLifetimeScope, InstancePerDependency — without mapping to the IServiceCollection equivalents. Registrations bridged from IServiceCollection are deduplicated automatically so each service appears only once.

Authorization

MapSpyglassDashboard() returns a RouteGroupBuilder, so you can protect the entire dashboard with any ASP.NET Core authorization policy:

app.MapSpyglassDashboard()
   .RequireAuthorization("AdminOnly");

Production use

Spyglass is a developer and operator tool. Protect the route at the infrastructure level (reverse proxy, firewall, VPN) rather than relying solely on application-layer access control.

When the environment is Production, Spyglass automatically redacts:

  • Configuration values — key names and providers remain visible; values are replaced with ***
  • Health check descriptions — status (Healthy / Degraded / Unhealthy) is shown; the description text is hidden
  • Dependency targets and error messages — service name and status are shown; the host/port and failure message are hidden

A ⚠ indicator appears next to the environment badge to make it immediately clear the dashboard is connected to live production data.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Spyglass.AspNetCore:

Package Downloads
Spyglass.AspNetCore.Autofac

Autofac integration for the Spyglass observability dashboard. Surfaces native Autofac registrations (SingleInstance, InstancePerLifetimeScope, InstancePerDependency) alongside IServiceCollection services in the Spyglass Services tab.

Spyglass.AspNetCore.EntityFrameworkCore

EF Core integration for the Spyglass observability dashboard. Captures slow queries and errors via a DbCommandInterceptor and surfaces them in a dedicated Queries tab with live streaming, SQL preview, and click-to-expand detail.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.1 173 6/7/2026
0.2.0 156 6/7/2026
0.1.0 157 6/4/2026