Fluens.Web.Messaging.Dashboard
0.7.4
dotnet add package Fluens.Web.Messaging.Dashboard --version 0.7.4
NuGet\Install-Package Fluens.Web.Messaging.Dashboard -Version 0.7.4
<PackageReference Include="Fluens.Web.Messaging.Dashboard" Version="0.7.4" />
<PackageVersion Include="Fluens.Web.Messaging.Dashboard" Version="0.7.4" />
<PackageReference Include="Fluens.Web.Messaging.Dashboard" />
paket add Fluens.Web.Messaging.Dashboard --version 0.7.4
#r "nuget: Fluens.Web.Messaging.Dashboard, 0.7.4"
#:package Fluens.Web.Messaging.Dashboard@0.7.4
#addin nuget:?package=Fluens.Web.Messaging.Dashboard&version=0.7.4
#tool nuget:?package=Fluens.Web.Messaging.Dashboard&version=0.7.4
Fluens.Web.Messaging.Dashboard
Cluster-wide live monitoring surface for the Fluens cross-application
messaging mesh. It rolls up the Fluens.Messaging meter instruments
(consume.duration / inbox.lag / retry.count / deadletter.count / delivery.latency) from every
instance into one cluster aggregate, streams it over Server-Sent-Events to a single static dashboard page, and
exposes the per-module dead-letter query + replay surface — all backed by the same Consul the mesh already
runs for discovery and durable subscriptions (ADR-0013). There is no second metrics backend. Without Consul the
dashboard degrades cleanly to a per-instance view.
How it works
Each instance publishes its own StatsSnapshot roll-up into Consul KV; a blocking-query watch aggregates every
live snapshot into a cluster-wide sum (ADR-0013, reusing the ADR-0007 durable-KV + long-poll pattern).
StatsSnapshot— an immutable, additive point-in-time roll-up of theFluens.Messagingmeter instruments for one instance (or, once aggregated, the cluster). The two histograms (consume.duration,delivery.latency) keep both a count and a millisecond total so the cluster mean derives astotal / count; the counters and the inbox-lag scalar add directly.StatsSnapshot.AggregateCluster(clusterId, instances)is a pure per-metric sum — an empty set yields a zeroed snapshot (the local-only view), nevernull. It round-trips AOT-safely via the source-generatedStatsSnapshotSerializerContext.MeterListenerSnapshotSource(IDisposable, internal) — the instance-local snapshot source: a BCLMeterListenerover theFluens.Messagingmeter that accumulates this instance's counters (retry.count,deadletter.count) as running deltas and histograms (consume.duration,delivery.latency) as count + millisecond total.Build()pulls the per-moduleinbox.laggauge viaRecordObservableInstruments()and sums the per-module measurements into oneInboxLag, returning this instance'sStatsSnapshot— the real roll-up the publisher writes to KV.StatsPublisher(hosted) — publishes this instance's snapshot to the Consul-KV keyfluens/<ClusterId>/messaging/stats/<app>/<instanceId>(<app>= the canonical catalog identityAppInfo.CachePrefix,<instanceId>=AppInfo.CachePrefixUnique). The key is acquired under a Consul session with a TTL (SessionBehavior.Delete), so a dead instance's snapshot auto-evicts from the cluster aggregate when its session lapses — no explicit cleanup path. Each cycle re-acquires the snapshot under the renewed session (drift repair) and renews the session TTL on the same cadence.StatsCatalogWorker(hosted) — a KV blocking-query watch over the cluster stats prefixfluens/<ClusterId>/messaging/stats/. On every index advance — including when a peer's session-TTL-bound key auto-expires — it decodes every per-instance snapshot still present and rebuilds the cluster aggregate via the pureStatsSnapshot.AggregateCluster, atomically swapping it intoCurrent.MessagingDashboardEndpoints(IEndpoint) — maps, under the configured route prefix: the staticdashboard.htmlat the prefix root; aGET /streamServer-Sent-Events (text/event-stream) feed that streams the cluster aggregate (first snapshot immediately on connect, then on the configured interval); andGET /dead-lettersover the per-moduleIMessagingMaintenancesurface plusPOST /dead-letters/{id}/replayoverIMessagingRecovery(ADR-0011 / ADR-0014). Both surfaces are resolved per-request and optional — when an interface is not registered the corresponding endpoint returns an empty / no-op result rather than failing. The page surfaces the SSE connection state with a coloured indicator next to the status text — amber pulsing while connecting, solid green when live, red pulsing while the browser'sEventSourceis reconnecting.
Local-only graceful degradation
When Discovery is not registered (no IConsulClient in DI) or DiscoveryOptions.ClusterId is blank, both stats
workers degrade to local-only: the publisher writes nothing and never starts a session; the worker never
watches and Current stays the seeded zeroed (this-instance-only) aggregate. Neither throws and messaging is
never blocked. The local-snapshot source is read from the Func<StatsSnapshot> in DI, which delegates to the
singleton MeterListenerSnapshotSource roll-up of this instance's Fluens.Messaging meter. The dashboard is
non-load-bearing — it is a live operational glance, not a historical metrics store.
Usage
builder.AddFluens()
.AddWeb()
.AddMessaging()
.AddMessagingMesh(ingress => ingress.UseNpgsql(connectionString))
.AddMessagingDashboard();
var app = builder.Build();
app.MapMessagingMesh();
app.MapMessagingDashboard(); // serves dashboard.html + SSE feed + admin/replay under RoutePrefix
AddMessagingDashboard registers the hosted StatsPublisher + StatsCatalogWorker and the
MessagingDashboardEndpoints. The cluster identity is the single-source DiscoveryOptions.ClusterId (the
dashboard has no cluster id of its own); register AddDiscovery() so an IConsulClient is present for the
cluster-wide view (without it the dashboard runs local-only). When MessagingDashboardOptions.Enabled is
false, AddMessagingDashboard is a no-op (registers nothing) and MapMessagingDashboard maps no routes —
mirroring the MapFluensHealthchecks "maps nothing when absent" convention.
Configuration
MessagingDashboardOptions is bound from the "Fluens:Web:Messaging:Dashboard" configuration section:
| Property | Default | Description |
|---|---|---|
Enabled |
true |
Master flag; when false the dashboard registers nothing and maps no routes. |
StreamIntervalSeconds |
5 |
Cadence at which the SSE feed pushes the current cluster aggregate (first snapshot is immediate). |
RoutePrefix |
/messaging/dashboard |
Base route the static page, SSE feed, and admin/replay endpoints are mapped under. |
The Consul-KV publish / session-renew cadence (PublishIntervalSeconds, default 10) and the Consul session
TTL (SessionTtlSeconds, default 30 — how long after its last renew a dead instance's key auto-expires) are
read directly from the same "Fluens:Web:Messaging:Dashboard" section. The cluster id is single-source in
Fluens:Web:Discovery (DiscoveryOptions.ClusterId) — it is the stats key namespace and is not a dashboard
option.
This package does not declare a
Microsoft.AspNetCore.Appframework reference — it inherits ASP.NET Core transitively viaFluens.Web.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- Fluens.Messaging (>= 0.7.4)
- Fluens.Web.Api (>= 0.7.4)
- Fluens.Web.Discovery (>= 0.7.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.