DiagnosticExplorer.Hosting
5.3.0
dotnet add package DiagnosticExplorer.Hosting --version 5.3.0
NuGet\Install-Package DiagnosticExplorer.Hosting -Version 5.3.0
<PackageReference Include="DiagnosticExplorer.Hosting" Version="5.3.0" />
<PackageVersion Include="DiagnosticExplorer.Hosting" Version="5.3.0" />
<PackageReference Include="DiagnosticExplorer.Hosting" />
paket add DiagnosticExplorer.Hosting --version 5.3.0
#r "nuget: DiagnosticExplorer.Hosting, 5.3.0"
#:package DiagnosticExplorer.Hosting@5.3.0
#addin nuget:?package=DiagnosticExplorer.Hosting&version=5.3.0
#tool nuget:?package=DiagnosticExplorer.Hosting&version=5.3.0
DiagnosticExplorer
Add live diagnostic data to a .NET application, then inspect it in a browser.
Dashboard
Inspect registered objects, their properties, and live diagnostic events from one browser view.
Quick Start
This package adds configuration-based Diagnostic Explorer hosting:
<PackageReference Include="DiagnosticExplorer.Hosting" Version="5.0.0" />
This appsettings.json section enables a local viewer and a remote-service
connection. Use either host or both, and tune retention without changing code:
{
"DiagnosticExplorer": {
"Enabled": true,
"Hosts": [
{ "Type": "SelfHost", "Url": "http://127.0.0.1:50101" },
{ "Type": "Remote", "Url": "http://localhost:50000/diagnostics" }
],
"EventRetention": {
"MaxEventsPerSink": 1000,
"MaxAgeMinutes": 30
},
"LogEventRetention": {
"MaxEvents": 5000,
"MaxAgeMinutes": 5
}
}
}
This generic-host setup reads the host configuration, starts the selected hosts, registers diagnostic objects, and configures their presentation:
using DiagnosticExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<WidgetService>();
builder.Services.ConfigureDiagnosticExplorer(
builder.Configuration,
diagnostics =>
{
diagnostics.RegisterObjects(RegisterObjects);
ConfigureClasses(diagnostics);
ConfigureEventRoutes(diagnostics);
}
);
await builder.Build().RunAsync();
RegisterObjects runs when diagnostics are collected. Resolve an application
service, then register the dynamic objects it owns:
private static void RegisterObjects(IDiagRegistrar registrar)
{
WidgetService widgetService = registrar.GetRequiredService<WidgetService>();
registrar.RegisterService<WidgetService>("Application", "Widget service");
foreach (Widget widget in widgetService.Widgets)
registrar.Register(widget, "Widgets", widget.Name);
}
RegisterService<T> resolves T from the application service provider before
registering it. Use it for long-lived services, typically singletons.
Register remains available for objects created at runtime.
Without a type profile, Diagnostic Explorer displays public scalar properties, collection counts, and complex values as drilldown icons. A type profile lets you start small and make the display intentional. This is a good first profile:
private static void ConfigureClasses(IDiagConfigurator diagnostics)
{
diagnostics.Configure<Widget>(options =>
{
options.ExcludeAll();
options.Property(widget => widget.Name)
.WithLabel("Widget name")
.AllowSet();
options.Property(widget => widget.Status);
});
}
ExcludeAll() starts with an empty display, then each Property(...) adds one
row. Use IncludeAll() when the default public-property view is closer to what
you need. WithLabel(...) changes only the text users see; it does not change
which property is read. AllowSet() makes a writable property editable in the
viewer.
Organize properties
Add Category(...) when a group makes the display easier to scan. Properties
without a category appear at the top of the view, so there is no need to create
a catch-all category such as General.
diagnostics.Configure<Widget>(options =>
{
options.ExcludeAll();
options.Property(widget => widget.Name).WithLabel("Widget name");
options.Property(widget => widget.Status).WithCategory("Health");
options.Property(widget => widget.LastUpdated)
.WithLabel("Last updated")
.WithCategory("Health")
.ShowElapsed();
});
Show nested details
Use Expand() to place an object's properties directly in the current view.
First-level expanded sections start open by default. Pass false when the
details should start collapsed.
diagnostics.Configure<Widget>(options =>
{
options.Property(widget => widget.Connection)
.WithLabel("Connection")
.Expand(initiallyExpanded: false)
.WithExpandedHover();
});
WithExpandedHover() opens the same nested details on hover. It is useful when
you want the main display compact but still need quick inspection.
Configure collections
For supported collection properties, Property(...) shows a count by default.
Choose one of the collection outputs to show the items instead:
diagnostics.Configure<Widget>(options =>
{
options.Property(widget => widget.Components)
.WithLabel("Components")
.WithCategory("Inventory")
.ListItems(items => items
.WithName(component => component.Name)
.WithValue(component => component.Status))
.WithMaxItems(50)
.WithDrillDown();
options.Property(widget => widget.Tags)
.WithCategory("Inventory")
.ConcatItems(", ")
.WithTextWrap();
});
ListItems(...) gives each item its own row. ConcatItems(...) creates one
compact text value. ExpandItems(items => items.WithName(item => item.Id))
creates an expanded section for the collection, then an item section for each
value with that item's diagnostic properties. Without WithName(...), item
names default to the collection name and a zero-based index. A configured name
must be distinct for every item; include an identifier when a readable name
alone is not unique. Pass
WithInitiallyCollapsed() when the collection should start collapsed:
options.Property(widget => widget.Gadgets)
.ExpandItems(items => items.WithName(gadget => gadget.FullName).WithInitiallyCollapsed());
Chain WithPrimaryPropertiesOnly() after ExpandItems(...) or Expand() to
show only direct, uncategorized properties. Nested Expand() and Custom()
sections are omitted, keeping the expanded section focused on primary values.
WithMaxItems(...) limits list, category, and concatenated outputs; the viewer
shows how many items were omitted. WithTextWrap() lets a concatenated value
wrap instead of cutting it off.
Arrays, the common collection interfaces, List<T>, HashSet<T>,
ObservableCollection<T>, and BindingList<T> are supported. Dictionaries
are also supported and display key/value pairs.
Group derived diagnostics
Use Custom(...).Expand() to group derived properties in an expandable main
view section. The projection may include a collection output:
options.Custom("Gadgets", projection =>
{
projection.Property("All gadgets", form => form.Gadgets)
.ExpandItems(items => items.WithName(gadget => gadget.FullName));
}).Expand();
The Gadgets section contains the generated item sections directly; an
ExpandItems(...) member does not add another collection section inside an
expanded custom projection. This is useful when the group is diagnostic-only
rather than a property on the application type.
Drill into a value
WithDrillDown() makes a complex value, collection item, or custom property
interactive while keeping its rendered value. Use WithDrillDownOnly() to show
only a [show more] text value, or pass a custom string instead.
diagnostics.Configure<Widget>(options =>
{
options.Property(widget => widget.Configuration)
.WithDrillDown(maxItems: 50);
options.Property("Connection snapshot", widget => new
{
widget.Connection.Endpoint,
widget.Connection.IsConnected,
})
.WithDrillDownOnly("View connection");
});
diagnostics.ConfigureDrillDown<WidgetConfiguration>(options =>
{
options.ExcludeAll();
options.Property(instance => instance.Status);
options.Property(instance => instance.Owner).WithDrillDown();
});
ConfigureDrillDown<T>(...) controls what appears in that overlay. If you do
not add a drilldown profile, Diagnostic Explorer reuses the normal type profile.
Drilldowns are not shown for null values or empty collections.
Use Property("name", value) for a named or computed property. AsJson()
with WithJsonHover() fetches JSON only when users hover over the value.
Private fields and anonymous objects
Use a named delegate property to expose computed or private state. Write the configuration inside the declaring type when it needs private-field access:
private static void ConfigureDiagnostics(IDiagConfigurator diagnostics)
{
diagnostics.Configure<Widget>(options =>
{
options.Property("Retry count", widget => widget._retryCount).WithCategory("Internal");
options.Property("Last error", widget => widget._lastError?.Message).WithCategory("Internal");
});
}
For DateTime and DateTimeOffset values, date display options are available
directly from Property:
options.Property(widget => widget._lastUpdated).ShowElapsed();
options.Property("Last updated", widget => widget._lastUpdated).ShowDate(false).ShowElapsed();
For RateCounter values, configure rate and total displays the same way:
options.Property(widget => widget._requests).ShowRate(false).ShowTotal();
options.Property("Background requests", widget => widget._backgroundRequests).ShowTotal();
Return an anonymous object for a small, read-only diagnostic snapshot. Its generated public properties render in the drilldown view:
options.Property(
"Connection snapshot",
widget => new
{
widget.Connection.Endpoint,
widget.Connection.IsConnected,
}
)
.WithDrillDownOnly("View connection");
The widget sample configuration has more examples of custom properties, collection outputs, warnings, and errors. For an agent-focused, end-to-end integration guide, see configuring Diagnostic Explorer in an application.
This helper routes events from named loggers to separate event sinks. Each integration below uses these same routes:
private static void ConfigureEventRoutes(IDiagConfigurator diagnostics)
{
diagnostics.ConfigureEventRouting(routes =>
routes
.UseMatchMode(EventSinkRouteMatchMode.AllMatches)
.Route("Widgets", route => route.AtLeast(LogLevel.Information).To("Widgets", "Widget Events"))
.Route("Gadgets", route => route.AtLeast(LogLevel.Warning).To("Gadgets", "Gadget Warnings"))
.Route("*", route => route.AtLeast(LogLevel.Error).To("System", "Errors")));
}
For an application without a generic host, create the configuration, register objects directly, and start the configured hosts yourself:
using DiagnosticExplorer;
DiagnosticConfiguration diagnostics = DiagnosticManager.Configure(config =>
{
config.ConfigureHosting(applicationConfiguration);
config.RegisterObjects(registrar => registrar.Register(widget, "Widgets", "Widget 42"));
ConfigureClasses(config);
ConfigureEventRoutes(config);
});
await DiagnosticHostingService.StartAsync(diagnostics);
try
{
RunApplication();
}
finally
{
await DiagnosticHostingService.Stop();
}
Run the application and open the configured SelfHost URL in a browser. For a
Remote host, run DiagnosticService, open its configured URL, and select
your registered application.
Download
Download the latest DiagnosticService Windows Service installer.
Microsoft.Extensions.Logging
This optional package forwards Microsoft.Extensions.Logging events to
Diagnostic Explorer:
<PackageReference Include="DiagnosticExplorer.Extensions.Logging" Version="5.0.0" />
This registers the logging provider after ConfigureDiagnosticExplorer:
using DiagnosticExplorer;
using DiagnosticExplorer.Extensions.Logging;
using Microsoft.Extensions.Logging;
services.AddLogging(logging => logging.AddDiagnosticExplorer());
The provider sends matching Microsoft.Extensions.Logging events to Diagnostic
Explorer, including structured properties:
logger.LogInformation("Processed {WidgetCount} widgets", widgetCount);
NLog
This package adds a Diagnostic Explorer target to NLog:
<PackageReference Include="DiagnosticExplorer.NLog" Version="5.0.0" />
This configuration sends NLog events through the routes configured above:
using DiagnosticExplorer.NLog;
using NLog;
using NLog.Config;
LoggingConfiguration logging = new();
logging.AddDiagnosticExplorer();
LogManager.Configuration = logging;
NLog templates retain their event properties:
logger.Info("Processed {WidgetCount} widgets", widgetCount);
Serilog
This package adds a Diagnostic Explorer sink to Serilog:
<PackageReference Include="DiagnosticExplorer.Serilog" Version="5.0.0" />
This configuration sends Serilog events through the routes configured above:
using DiagnosticExplorer.Serilog;
using Serilog;
using ILogger logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.DiagnosticExplorer()
.CreateLogger();
Serilog properties are forwarded with the event:
logger.Information("Processed {WidgetCount} widgets", widgetCount);
| Product | Versions 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. |
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- DiagnosticExplorer (>= 5.3.0)
- DiagnosticExplorer.Log4Net (>= 5.3.0)
- MessagePack (>= 2.5.301)
- Microsoft.AspNet.SignalR (>= 2.4.3)
- Microsoft.AspNetCore.SignalR.Client (>= 8.0.0)
- Microsoft.AspNetCore.SignalR.Protocols.MessagePack (>= 8.0.28)
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.Hosting (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- Microsoft.Owin (>= 4.2.2)
- Microsoft.Owin.Host.HttpListener (>= 4.2.2)
- Microsoft.Owin.Hosting (>= 4.2.2)
- System.Reactive (>= 5.0.0)
- TypedSignalR.Client (>= 3.6.0)
-
net8.0
- DiagnosticExplorer (>= 5.3.0)
- DiagnosticExplorer.Log4Net (>= 5.3.0)
- MessagePack (>= 2.5.301)
- Microsoft.AspNetCore.SignalR.Client (>= 8.0.0)
- Microsoft.AspNetCore.SignalR.Protocols.MessagePack (>= 8.0.28)
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.Hosting (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- System.Reactive (>= 5.0.0)
- TypedSignalR.Client (>= 3.6.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.3.0 | 286 | 9/2/2026 |
| 5.2.0 | 85 | 9/2/2026 |
| 5.1.0 | 93 | 9/1/2026 |
| 5.0.31 | 91 | 8/31/2026 |
| 5.0.30 | 102 | 8/30/2026 |
| 5.0.29 | 95 | 8/30/2026 |
| 5.0.28 | 90 | 8/29/2026 |
| 5.0.27 | 95 | 8/29/2026 |
| 5.0.26 | 102 | 8/28/2026 |
| 5.0.25 | 96 | 8/28/2026 |
| 5.0.24 | 88 | 8/28/2026 |
| 5.0.23 | 89 | 8/28/2026 |
| 5.0.22 | 88 | 8/28/2026 |
| 5.0.21 | 84 | 8/28/2026 |
| 5.0.20 | 96 | 8/28/2026 |
| 5.0.19 | 93 | 8/28/2026 |
| 5.0.18 | 94 | 8/27/2026 |
| 5.0.17 | 87 | 8/27/2026 |
| 5.0.16 | 91 | 8/27/2026 |
| 5.0.15 | 97 | 8/27/2026 |