Files
vigilcare-records/VigilCareRecordsAPI/Diagnostics/DiagnosticsMetrics.cs
T

109 lines
4.5 KiB
C#

using Prometheus;
/// <summary>
/// Application-level Prometheus metrics for the digitization pipeline.
/// All metrics are static singletons — safe for concurrent use across
/// all services and background workers.
///
/// prometheus-net throws InvalidOperationException if a metric with the
/// same name but different label configuration is registered twice.
/// Static fields guarantee each metric is created exactly once.
/// </summary>
public static class DiagnosticsMetrics
{
/// <summary>
/// Gauge: count of digitization batches per status.
/// Updated periodically by MetricsCollectorService.
/// Labels: status (UPLOADED, IN_ENTRY, PENDING_VERIFICATION, etc.)
///
/// This is a gauge (not a counter) because statuses change — a batch
/// moves from UPLOADED to IN_ENTRY, decrementing one label and
/// incrementing another. The gauge is set to the current count
/// each collection cycle.
/// </summary>
public static readonly Gauge BatchesByStatus = Metrics.CreateGauge(
"digitization_batches_by_status",
"Number of digitization batches grouped by current status.",
new GaugeConfiguration
{
LabelNames = new[] { "status" }
});
/// <summary>
/// Histogram: how long a promotion operation takes in seconds.
/// Recorded in PromotionService when a batch transitions to Promoted.
/// Buckets tuned for typical promotion durations (50ms to 30s).
///
/// The p50/p95/p99 can be derived from the bucket boundaries in
/// Grafana using histogram_quantile().
/// </summary>
public static readonly Histogram PromotionDuration = Metrics.CreateHistogram(
"digitization_promotion_duration_seconds",
"Duration of batch promotion operations in seconds.",
new HistogramConfiguration
{
Buckets = new[] { 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0 }
});
/// <summary>
/// Counter: total number of batch rejections.
/// Incremented in VerificationService on every rejection.
/// Labels: reason_category (verification_failed, clinical_rejected)
///
/// Supervisors need to distinguish between verification-stage rejections
/// (data entry errors) and clinical-stage rejections (clinical judgment
/// issues). The label enables separate alerting thresholds.
/// </summary>
public static readonly Counter RejectionTotal = Metrics.CreateCounter(
"digitization_rejection_total",
"Total number of digitization batch rejections.",
new CounterConfiguration
{
LabelNames = new[] { "reason_category" }
});
private static readonly string[] RejectionReasonCategories =
{
"verification_failed",
"clinical_rejected"
};
static DiagnosticsMetrics()
{
// Expose all reason_category label combinations at 0 before any rejections occur.
foreach (var category in RejectionReasonCategories)
RejectionTotal.WithLabels(category).Inc(0);
}
/// <summary>
/// Gauge: age in seconds of the oldest batch in PendingVerification status.
/// Updated periodically by MetricsCollectorService.
/// A high value indicates the verification queue is backed up.
///
/// This is a gauge because it reflects a point-in-time measurement —
/// the age of the oldest pending batch right now.
/// </summary>
public static readonly Gauge QueueAgeSeconds = Metrics.CreateGauge(
"digitization_queue_age_seconds",
"Age in seconds of the oldest batch in pending_verification status.");
public static readonly Counter PromotionRetryTotal = Metrics.CreateCounter(
"digitization_promotion_retry_total",
"Total promotion retry attempts.",
new CounterConfiguration
{
LabelNames = new[] { "outcome" }
});
public static readonly Gauge PromotionPendingRetries = Metrics.CreateGauge(
"digitization_promotion_pending_retries",
"Count of promotion attempts with a scheduled retry that have not yet succeeded.");
public static readonly Gauge PromotionExhaustedTotal = Metrics.CreateGauge(
"digitization_promotion_exhausted_total",
"Count of APPROVED batches where all retry attempts are exhausted.");
public static readonly Gauge ApprovalQueueAgeSeconds = Metrics.CreateGauge(
"digitization_approval_queue_age_seconds",
"Age in seconds of the oldest batch in APPROVED status awaiting promotion retry.");
}