Skip to content

Prometheus Metrics

Mantis exports Prometheus-compatible metrics for monitoring notification delivery, rate limiting, circuit breaker states, and audit log throughput. All metrics use the mantis_ prefix and follow Prometheus naming conventions.

MetricTypeLabelsDescription
mantis_notification_deliveries_totalCounterchannel_type, statusTotal notification delivery attempts
mantis_notification_delivery_duration_secondsHistogramchannel_typeDelivery latency (buckets: 0.1s to 30s)
mantis_notification_queue_depthGaugeNumber of pending notification deliveries
mantis_notification_circuit_breaker_stateGaugechannel_id, channel_nameCircuit breaker state per channel
mantis_notification_events_totalCounterevent_type, event_groupTotal notification events emitted
mantis_notification_dead_letter_totalCounterchannel_type, reasonDeliveries moved to dead letter queue
mantis_notification_rate_limited_totalCounterlimit_typeDeliveries rejected by rate limiter
MetricTypeLabelsDescription
mantis_rate_limit_cache_entriesGaugelimiterCurrent entries in rate limit cache
mantis_rate_limit_decisions_totalCounterlimiter, decisionRate limit decisions (allowed/rejected/locked_out)
MetricTypeLabelsDescription
mantis_audit_log_events_totalCounterresultAudit log events by result (accepted/channel_full/channel_closed)

email, webhook, slack, teams, discord

success, failed, rate_limited, circuit_open, permanent_failure, timeout, error

auth (authentication rate limiter with progressive backoff), general (API rate limiter with token bucket)

allowed, rejected, locked_out (auth limiter only)

ValueStateMeaning
0ClosedHealthy, requests flowing normally
1OpenFailing, requests blocked
2Half-openTesting recovery

Thorax serves a Prometheus endpoint at http://<thorax-host>:9090/metrics (the HTTP metrics/health server bound to 0.0.0.0:9090). This endpoint exposes Thorax’s own mantis_* families — gRPC request, client session/registration, command, deployment, dispatch, DB, and TLS series (see [thorax/src/metrics.rs]).

Mandible does not currently expose an HTTP /metrics endpoint. The notification, rate-limiting, and audit-log families documented above are recorded in Mandible’s in-process registry but are not yet served on any port, so they cannot be scraped today. Treat the notification/rate-limit/audit dashboards and alerts below as forward-looking until a Mandible metrics endpoint is wired up.

Add Thorax as a scrape target in your prometheus.yml:

scrape_configs:
- job_name: 'mantis-thorax'
scrape_interval: 15s
static_configs:
- targets: ['thorax:9090']
metrics_path: '/metrics'
metrics:
configs:
- name: mantis
scrape_configs:
- job_name: mantis-thorax
static_configs:
- targets: ['thorax:9090']
# Deliveries per second by status
rate(mantis_notification_deliveries_total[5m])
# Success rate percentage
sum(rate(mantis_notification_deliveries_total{status="success"}[5m]))
/
sum(rate(mantis_notification_deliveries_total[5m])) * 100
# P50 delivery latency
histogram_quantile(0.5, rate(mantis_notification_delivery_duration_seconds_bucket[5m]))
# P99 delivery latency
histogram_quantile(0.99, rate(mantis_notification_delivery_duration_seconds_bucket[5m]))
# Current queue depth
mantis_notification_queue_depth
# Alert if queue depth exceeds 100 for more than 5 minutes
mantis_notification_queue_depth > 100
# Rate limit rejections per second
rate(mantis_rate_limit_decisions_total{decision="rejected"}[5m])
# Lockout events (potential brute force)
increase(mantis_rate_limit_decisions_total{limiter="auth", decision="locked_out"}[1h])
# Any channel with open circuit breaker
mantis_notification_circuit_breaker_state == 1
groups:
- name: mantis
rules:
- alert: MantisNotificationQueueBacklog
expr: mantis_notification_queue_depth > 100
for: 5m
labels:
severity: warning
annotations:
summary: 'Notification queue backlog detected'
description: 'Queue depth is {{ $value }} (threshold: 100)'
- alert: MantisCircuitBreakerOpen
expr: mantis_notification_circuit_breaker_state == 1
for: 2m
labels:
severity: critical
annotations:
summary: 'Circuit breaker open for channel {{ $labels.channel_name }}'
- alert: MantisHighDeliveryFailureRate
expr: |
sum(rate(mantis_notification_deliveries_total{status="failed"}[5m]))
/
sum(rate(mantis_notification_deliveries_total[5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: 'Notification delivery failure rate above 10%'
- alert: MantisAuthBruteForce
expr: increase(mantis_rate_limit_decisions_total{limiter="auth", decision="locked_out"}[15m]) > 5
labels:
severity: critical
annotations:
summary: 'Multiple auth lockouts detected -- possible brute force attack'
- alert: MantisAuditLogDrops
expr: increase(mantis_audit_log_events_total{result="channel_full"}[5m]) > 0
labels:
severity: warning
annotations:
summary: 'Audit log events being dropped due to full channel'

A recommended Grafana dashboard should include the following panels:

  1. Delivery Raterate(mantis_notification_deliveries_total[5m]) stacked by status
  2. Delivery Latency Heatmapmantis_notification_delivery_duration_seconds_bucket
  3. Queue Depthmantis_notification_queue_depth as a time series
  4. Circuit Breaker Statusmantis_notification_circuit_breaker_state as a status map
  5. Rate Limit Activityrate(mantis_rate_limit_decisions_total[5m]) by decision
  6. Dead Lettersincrease(mantis_notification_dead_letter_total[1h]) by reason
  7. Audit Log Throughputrate(mantis_audit_log_events_total[5m]) by result