Registration
Register Wolverine.CritterWatch inside your UseWolverine() configuration block. The call must come after transport configuration so the observer can use the transport to publish telemetry.
Simple Registration
Set the service's ServiceName (CritterWatch's key for the service) and point AddCritterWatchMonitoring at the queue CritterWatch listens on for telemetry plus the queue this service listens on for commands:
builder.Host.UseWolverine(opts =>
{
// CritterWatch identifies this service by its Wolverine ServiceName.
opts.ServiceName = "my-service";
opts.UseRabbitMq(new Uri("amqp://localhost")).AutoProvision();
opts.AddCritterWatchMonitoring(
// Queue CritterWatch listens on for this service's telemetry.
critterWatchUri: new Uri("rabbitmq://queue/critterwatch"),
// Queue this service listens on for commands from the console.
systemControlUri: new Uri("rabbitmq://queue/my-service-control"));
});Full Options
builder.Host.UseWolverine(opts =>
{
opts.AddCritterWatchMonitoring(
// URI of the queue CritterWatch listens on for telemetry
critterWatchUri: new Uri("rabbitmq://queue/critterwatch"),
// URI of the queue this service listens on for incoming commands
systemControlUri: new Uri("rabbitmq://queue/trip-service-control"),
// How this service exports metrics (default: Hybrid)
metricsMode: WolverineMetricsMode.Hybrid
);
});Disabling metrics publishing
The metricsMode parameter is also the off switch. Passing WolverineMetricsMode.SystemDiagnosticsMeter stops this service publishing metrics to CritterWatch entirely: Wolverine never starts the metrics accumulator that feeds the CritterWatch observer, so no metrics samples — including the per-tenant breakdowns, which are produced inside that accumulator — ever ride the ServiceUpdates telemetry batches. Metrics remain available through .NET's System.Diagnostics.Metrics for any OTel exporter or scraper the host already has.
// Turn OFF metrics publishing to CritterWatch while keeping every other
// part of the monitoring integration. In SystemDiagnosticsMeter mode
// Wolverine never starts the metrics accumulator that feeds the console,
// so no metrics samples — including the per-tenant breakdowns — ever ride
// the ServiceUpdates telemetry batches. Service registration, heartbeats,
// node lifecycle, listener and DLQ telemetry, projection progress, and
// operator commands are all unaffected.
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseWolverine(opts =>
{
opts.UseRabbitMq(new Uri("amqp://localhost")).AutoProvision();
opts.AddCritterWatchMonitoring(
critterWatchUri: new Uri("rabbitmq://queue/critterwatch"),
systemControlUri: new Uri("rabbitmq://queue/trip-service-control"),
// The off switch. Metrics flow only through .NET's
// System.Diagnostics.Metrics for whatever OTel / scrape pipeline
// the host already has — nothing metrics-shaped goes to the console.
metricsMode: WolverineMetricsMode.SystemDiagnosticsMeter
);
});
builder.Build().Run();Everything else in the integration keeps working exactly as before: service registration and capabilities, heartbeats, node lifecycle, listener and endpoint telemetry, dead-letter and persistence counts, projection progress, and every inbound operator command.
What the console shows: the service advertises its metrics mode on first contact, and the console marks a SystemDiagnosticsMeter service as external metrics only. If you bind the service to an external metrics data source (Prometheus, VictoriaMetrics, Datadog, Application Insights — see Settings → Metrics Data Sources), every metrics view and the metrics-based alert evaluators read through that source transparently. If no external source is bound, the metrics views show an explicit "no metrics provider bound" notice for the service instead of silently empty charts — the rest of the service's dashboards are unaffected.
Turning off native metrics publishing is not just a cosmetic choice: metrics are the dominant share of the console's telemetry ingest and database traffic at scale, and the volume multiplies with tenant count. Under heavy load or high tenant counts this is the recommended posture — see Use an external metrics stack under load for the numbers and the console-side configuration.
Declared Baselines
configureBaselines is an optional callback used to seed CritterWatch's metrics-based alert thresholds at the moment your service first connects. It exists because CritterWatch's "this service is suddenly running 10x above normal" alerts need something to compare against — and a brand-new service has no history yet.
configureBaselines: baselines => baselines
// Service-wide defaults (apply to any message type that doesn't have
// its own declaration).
.ForService(throughputPerHour: 200, avgExecTimeMs: 25)
// Per-message-type override.
.For<TripBooked>(throughputPerHour: 50, avgExecTimeMs: 40)
;Either argument can be omitted (null) when only one of the two figures is known. The values:
- are forwarded to CritterWatch on first contact (alongside
ServiceCapabilities); - are written into the service's alert-overrides record on the CritterWatch side and emitted as
ThroughputBaselineChanged/ExecTimeBaselineChangedevents withSource = ServiceCapabilities; - become the declared branch of the baseline cascade. They yield to observed history once enough samples accumulate, and are themselves editable through the CritterWatch UI (the UI-edited value is identified with
Source = Operatorso audit history makes the provenance clear).
See Alerts › Editing Thresholds for the full cascade and editing model.
What the call wires up
A single AddCritterWatchMonitoring(...) call wires up:
- The runtime hook that observes Wolverine state changes and publishes telemetry once per second.
- Handlers for every inbound command the console can send — DLQ replay/discard, listener pause/drain/restart, projection lifecycle, tenant management, and the rest. See Inbound Commands.
- Transport routing — the queue this service listens on for commands, and the queue it publishes telemetry to.
What you don't need to add
- No changes to your handlers, aggregates, or domain code.
- No changes to your database schema.
- No additional database connections.
- No additional processes.
The integration uses the Wolverine transport you already have.
Multiple Services
Each service must have a unique ServiceName. CritterWatch uses this as the primary key for all service data. Two services with the same name will appear as a single entry in CritterWatch, with state from both processes intermixed.
// TripService/Program.cs — each service publishes telemetry to the shared
// critterwatch queue but listens for commands on its own control queue, and
// sets a unique ServiceName (CritterWatch's primary key for the service).
tripServiceOpts.ServiceName = "trip-service";
tripServiceOpts.AddCritterWatchMonitoring(
new Uri("rabbitmq://queue/critterwatch"),
new Uri("rabbitmq://queue/trip-service-control"));
// RepairShop/Program.cs
repairShopOpts.ServiceName = "repair-shop";
repairShopOpts.AddCritterWatchMonitoring(
new Uri("rabbitmq://queue/critterwatch"),
new Uri("rabbitmq://queue/repair-shop-control"));Non-RabbitMQ Transports
The first argument to AddCritterWatchMonitoring() is used to configure the publishing destination. For non-RabbitMQ transports, use the full options form and configure accordingly.
TIP
RabbitMQ is strongly recommended for production use because it provides reliable message delivery between services and CritterWatch. In-memory transport is suitable for development but does not persist messages if either process restarts.
Clustered BFF deployments
Running CritterWatch behind more than one BFF replica requires both sides to declare the same sharded topology so each shard has exactly one writer. The monitored-side knob is the optional configureShardedTopology parameter on AddCritterWatchMonitoring(...):
opts.AddCritterWatchMonitoring(
critterWatchUri: new Uri("rabbitmq://queue/critterwatch"),
systemControlUri: new Uri("rabbitmq://queue/trip-service-control"),
configureShardedTopology: topology =>
{
topology.UseShardedRabbitQueues("critterwatch", 5);
});Leave it null on single-BFF deployments — the call behaves byte-for-byte the same as the version without the parameter, telemetry rides the unsharded critterwatch URI as before.
The N you pass (5 above) must match the BFF-side configureClusterShardedTopology(...) configuration — a mismatch hashes this service's telemetry to sharded slots no BFF is listening on, so it stalls on the broker and silently disappears from the dashboard. See Clustering for the full N-matching constraint, the rollout order, and the Azure Service Bus / SQS variants.
