using Prometheus; public sealed class ClinicalMetrics { // --- Counters --- // Labeled by observation_code and source so clinicians can see which device types // and which codes dominate the ingest volume. public readonly Counter ObservationsIngestedTotal = Metrics.CreateCounter( "observations_ingested_total", "Total observations ingested, labeled by observation code and source.", labelNames: new[] { "observation_code", "source" }); // Labeled by alert_type and severity (e.g. QSOFA_SCREEN/WARNING, SOFA_SEPSIS/CRITICAL). public readonly Counter ClinicalAlertsTotal = Metrics.CreateCounter( "clinical_alerts_total", "Total clinical alerts generated, labeled by type and severity.", labelNames: new[] { "alert_type", "severity" }); public readonly Counter News2ScoresTotal = Metrics.CreateCounter( "news2_scores_total", "Total NEWS2 scores computed, labeled by risk level.", labelNames: new[] { "risk_level" }); // Incremented by EscalationWorkerService when it processes a message from // alerts.escalation.queue. A rising escalations_total is the strongest operational // signal that critical alerts are not being acknowledged by the attending physician. public readonly Counter EscalationsTotal = Metrics.CreateCounter( "escalations_total", "Total alert escalations processed through the DLQ escalation path."); public readonly Counter TrendAlertsTotal = Metrics.CreateCounter( "trend_alerts_total", "Total RAPID_DETERIORATION alerts generated, labeled by observation code.", labelNames: new[] { "observation_code" }); public readonly Counter AlertSuppressionsTotal = Metrics.CreateCounter( "alert_suppressions_total", "Total alert suppression windows set after acknowledgment.", labelNames: new[] { "alert_type" }); public readonly Counter QsofaDetectionsTotal = Metrics.CreateCounter( "qsofa_detections_total", "Total QSOFA_SCREEN alerts generated by the qSOFA screening engine."); public readonly Counter SepsisBundleComplianceTotal = Metrics.CreateCounter( "sepsis_bundle_compliance_total", "Sepsis bundle compliance outcomes.", labelNames: new[] { "status" }); public readonly Counter GcsScoresTotal = Metrics.CreateCounter( "gcs_scores_total", "GCS scores computed, labeled by classification.", labelNames: new[] { "classification" }); public readonly Counter SofaScoresTotal = Metrics.CreateCounter( "sofa_scores_total", "SOFA scores computed, labeled by whether a delta alert was created.", labelNames: new[] { "has_delta_alert" }); // --- Histograms --- // Measures the full ingest transaction: Redis cache lookup + alert evaluation + // outbox write + COMMIT. The 99th percentile matters for patient safety — // a slow ingest path delays the critical alert creation. public readonly Histogram ObservationIngestDuration = Metrics.CreateHistogram( "observation_ingest_duration_seconds", "Ingest transaction duration from request receipt to COMMIT.", new HistogramConfiguration { Buckets = new[] { 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0 } }); public readonly Histogram News2ScoringDuration = Metrics.CreateHistogram( "news2_scoring_duration_seconds", "Time to compute a NEWS2 score from Redis state.", new HistogramConfiguration { Buckets = new[] { 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 } }); public readonly Histogram TrendAnalysisDuration = Metrics.CreateHistogram( "trend_analysis_duration_seconds", "Time to evaluate trend for one observation.", new HistogramConfiguration { Buckets = new[] { 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 } }); public readonly Histogram SofaScoringDuration = Metrics.CreateHistogram( "sofa_scoring_duration_seconds", "SOFA scoring computation time", new HistogramConfiguration { Buckets = new[] { 0.001, 0.005, 0.01, 0.025, 0.05, 0.1 } }); // --- Gauges (set by background collectors, not incremented inline) --- // The most clinically significant panel. A non-zero value means a patient's // critical alert has gone unacknowledged for more than 5 minutes. // In a real deployment this panel drives an on-call pager alert at the nurse station. public readonly Gauge AlertsUnacknowledgedGauge = Metrics.CreateGauge( "alerts_unacknowledged_gauge", "Count of open CRITICAL alerts older than 5 minutes with no acknowledgment."); // Per consumer group so the dashboard can show whether es-indexer, sepsis-engine, // or data-lake-writer is falling behind the observation stream. public readonly Gauge KafkaConsumerLag = Metrics.CreateGauge( "kafka_consumer_lag", "Approximate consumer group lag in messages, labeled by consumer group.", labelNames: new[] { "consumer_group" }); // An outbox that is growing means the relay is not keeping up or Kafka is unavailable. // In a patient safety system, a growing outbox delays alert delivery to all consumers. public readonly Gauge OutboxPendingEvents = Metrics.CreateGauge( "outbox_pending_events", "Count of outbox events not yet relayed to Kafka."); }