Skip to content

Configuration Reference

Monitored Service Configuration

AddCritterWatchMonitoring()

Call this inside UseWolverine() in each service you want to monitor.

Simple form — the telemetry + control queue URIs (the service identity is the Wolverine ServiceName, not a parameter):

cs
builder.Host.UseWolverine(opts =>
{
    // CritterWatch keys this service by its Wolverine ServiceName — there is no
    // separate service-name parameter on AddCritterWatchMonitoring.
    opts.ServiceName = "my-service";

    opts.AddCritterWatchMonitoring(
        critterWatchUri: new Uri("rabbitmq://critterwatch"),
        systemControlUri: new Uri("rabbitmq://my-service-control"));
});

Full options form:

cs
builder.Host.UseWolverine(opts =>
{
    opts.AddCritterWatchMonitoring(
        // URI of the queue CritterWatch listens on for telemetry
        critterWatchUri: new Uri("rabbitmq://critterwatch"),

        // URI of the queue this service listens on for incoming commands
        systemControlUri: new Uri("rabbitmq://trip-service-control"),

        // How this service exports metrics (default: Hybrid)
        metricsMode: WolverineMetricsMode.Hybrid
    );
});

Options Reference

OptionDefaultDescription
RabbitMqUriURI of the RabbitMQ broker (or transport endpoint)
ServiceNameUnique identifier for this service in CritterWatch
LabelServiceNameDisplay name shown in the UI
HeartbeatInterval1 secondHow often state snapshots are published
AgentHealthCheckInterval60 secondsHow often agent health is actively checked
configureBaselines(none)Optional callback to declare expected throughput/execution-time baselines for this service. See Alerts › Editing Thresholds.

Service Name Must Be Unique

The ServiceName is used as the Marten event stream key. Two services with the same name will overwrite each other's state. Use a name that uniquely identifies the service across your entire deployment.

CritterWatch Server Configuration

AddCritterWatch()

cs
var builder = WebApplication.CreateBuilder(args);

builder.AddCritterWatch(
    builder.Configuration.GetConnectionString("critterwatch")!,
    opts =>
    {
        opts.UseRabbitMq(new Uri("amqp://localhost")).AutoProvision();
        opts.ListenToRabbitQueue("critterwatch").Sequential();
    });
// Single-node is the default — nothing else to configure. For a multi-node
// cluster, supply configureClusterShardedTopology (see Deployment › Clustering).

var app = builder.Build();
app.UseCritterWatch();
app.Run();

UseCritterWatch()

Maps all CritterWatch middleware into the ASP.NET Core pipeline:

cs
app.UseCritterWatch();

// With a custom SignalR route:
app.UseCritterWatch(signalRRoute: "/my-hub");

UseCritterWatch() registers:

  • Wolverine HTTP endpoints under /api/critterwatch/*
  • SignalR hub at /api/messages (configurable)
  • Static file serving for the embedded Vue SPA
  • Client-side routing fallback for the SPA

Storage schema

All CritterWatch data — its documents (service summaries, alerts, metrics rollups) and its event store — is isolated to a dedicated database schema, so it never collides with the host application's own tables. The default schema is critterwatch, and the name is configurable via the schemaName parameter:

cs
builder.AddCritterWatch(
    builder.Configuration.GetConnectionString("critterwatch")!,
    schemaName: "monitoring");   // default: "critterwatch"

The same schemaName parameter is available on the lower-level opts.AddCritterWatchServices(...) registration (both the Marten/PostgreSQL and Polecat/SQL Server flavors) and on opts.AddCritterWatchEmbedded(...). CritterWatch only ever creates and migrates tables inside that one schema.

Why a dedicated schema matters

This is what lets CritterWatch share a database with the application it monitors without stepping on it — most important for embedded CritterWatch (CritterWatch.Embedded), where the console runs inside your app's own process and database. Point schemaName at any schema you like; CritterWatch keeps all of its storage there.

Docker Compose

A complete docker-compose.yml for local development:

yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: critterwatch
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "5672:5672"    # AMQP
      - "15672:15672"  # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest

volumes:
  postgres_data:

Connection String Formats

PostgreSQL

Host=localhost;Port=5432;Database=critterwatch;Username=postgres;Password=postgres

For cloud providers:

Host=my-postgres.postgres.database.azure.com;Database=critterwatch;Username=app@my-postgres;Password=secret;SSL Mode=Require

RabbitMQ

amqp://guest:guest@localhost:5672/
amqps://user:pass@my-rabbit.cloud:5671/  # TLS

Amazon SQS

AddCritterWatchMonitoring works identically against the SQS transport. Two queues are involved per monitored service:

QueueDirectionPurpose
critterWatchUriservice → CritterWatchMetrics, heartbeats, capability snapshots
systemControlUriCritterWatch → servicePause / restart listeners, rebuild projections, DLQ ops
csharp
opts.UseAmazonSqsTransport();
opts.AddCritterWatchMonitoring(
    critterWatchUri:  SqsEndpointUri.Queue("critterwatch"),
    systemControlUri: SqsEndpointUri.Queue("critterwatch-control-trip-service"));

Dead letter queues with AutoProvision() off

By default the WolverineFx.AmazonSqs transport attaches every listener to its DefaultDeadLetterQueueName (wolverine-dead-letter-queue). With AutoProvision() enabled the broker creates that queue on startup, so the control listener AddCritterWatchMonitoring installs is wired up cleanly.

In production environments with AutoProvision() off, that default DLQ typically isn't pre-provisioned. The broker startup then fails:

Wolverine.AmazonSqs.WolverineSqsTransportException: Error while trying to
initialize Amazon SQS queue 'wolverine-dead-letter-queue'
 ---> Amazon.SQS.Model.QueueDoesNotExistException

Three ways to handle it, pick whichever fits your infra automation:

1. Provision the default DLQ alongside your application queues (CDK / Terraform / etc.). Nothing to change in code.

2. Point the control listener at an existing DLQ you already provision. Re-open the same endpoint after AddCritterWatchMonitoring and chain DeadLetterQueueName:

csharp
opts.AddCritterWatchMonitoring(critterWatchUri, systemControlUri);
opts.ListenToSqsQueue("critterwatch-control-trip-service", q =>
{
    q.DeadLetterQueueName = "trip-service-dlq";
});

3. Disable native DLQs on this transport entirely (rely on Wolverine's durability instead):

csharp
opts.UseAmazonSqsTransport(t => t.DisableAllNativeDeadLetterQueues());
opts.AddCritterWatchMonitoring(critterWatchUri, systemControlUri);

Same shape applies to other native-DLQ transports

Azure Service Bus and the other transports with a default DLQ behave the same way. Whichever DLQ strategy you pick for the rest of your application's listeners, the queues AddCritterWatchMonitoring installs inherit the same contract — there's no special CritterWatch DLQ to provision.

Native DLQs and CritterWatch management

CritterWatch's Dead Letter Queue explorer manages the durable (database) DLQ only — the failed messages Wolverine persists to its message store. It has no visibility into broker-native dead-letter queues (Amazon SQS DLQ, Azure Service Bus $DeadLetterQueue, RabbitMQ DLX). A message that dead-letters natively stays at the broker and won't appear in CritterWatch until it's forwarded into the Wolverine database.

So for CritterWatch to manage a service's dead letters, those failures need to land in — or be propagated into — the durable store. Two approaches:

A. Send failures straight to the durable store (no native DLQ). Simplest for CritterWatch; it changes where your dead letters live, so weigh it against any existing native-DLQ tooling or alarms you rely on.

csharp
// Amazon SQS — disable native DLQs transport-wide; failures go to the
// Wolverine durability database instead.
opts.UseAmazonSqsTransport(t => t.DisableAllNativeDeadLetterQueues());

On RabbitMQ the per-endpoint equivalent is the WolverineStorage dead-letter mode (failures bypass the native DLX and go to the durable store).

B. Keep the native DLQ and forward it into the durable store. Preserves your existing native dead-lettering and surfaces those messages in CritterWatch — the better fit when retrofitting onto a running system.

  • RabbitMQ has this built in — Wolverine listens on the native DLQ, reconstructs each envelope, and writes it to the durability database where CritterWatch can replay or discard it:

    csharp
    opts.UseRabbitMq(/* ... */).EnableDeadLetterQueueRecovery();
  • Amazon SQS / Azure Service Bus don't yet have a built-in equivalent (tracked upstream in wolverine#3103). Until it lands, either use approach A, or stand up a listener on the native DLQ that forwards each message to the store via IMessageInbox.MoveToDeadLetterStorageAsync(...).

Retrofitting without disrupting the host

Adding CritterWatch shouldn't change queues your application already owns. Keep AutoProvision() scoped (or pre-provision CritterWatch's critterwatch and control queues through your infrastructure automation), and use per-endpoint DLQ settings so CritterWatch never re-declares or alters the host's existing dead-letter infrastructure.

appsettings.json

The recommended approach for managing connection strings:

json
{
  "ConnectionStrings": {
    "critterwatch": "Host=localhost;Database=critterwatch;Username=postgres;Password=postgres",
    "rabbitmq": "amqp://localhost"
  }
}
cs
builder.AddCritterWatch(
    builder.Configuration.GetConnectionString("critterwatch")!,
    opts =>
    {
        var rabbitUri = new Uri(
            builder.Configuration.GetConnectionString("rabbitmq")!);
        opts.UseRabbitMq(rabbitUri).AutoProvision();
        opts.ListenToRabbitQueue("critterwatch").Sequential();
    });

Alert thresholds

Most alert thresholds are tuned in the UI rather than in configuration files — see Alert Configuration for the live preview, history tab, and three-level cascade (global → per-service → per-message-type).

Defaults that ship with the console:

ThresholdDefault
DLQ count Warning / Critical10 / 100
Projection lag Warning / Critical30s / 300s
Agent unhealthy Warning / Critical2 / 5 consecutive checks
DLQ rate / hour Warning / Critical10 / 50
Failure rate Warning / Critical5% / 20%
Throughput multiplier Warning / Critical3× / 10× of baseline
Exec time Warning / Critical+50% / +200% over baseline

For services that need different defaults baked in (rather than tuned post-deploy), declare baselines from AddCritterWatchMonitoring — see Registration → Declared Baselines.

Free for read-only monitoring. A commercial license is required for administrative actions and the MCP server.