129 lines
5.8 KiB
C#
129 lines
5.8 KiB
C#
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" });
|
|
|
|
public readonly Counter FhirIngestTotal = Metrics.CreateCounter(
|
|
"fhir_ingest_total",
|
|
"FHIR resource ingest operations.",
|
|
labelNames: new[] { "resource_type", "outcome" });
|
|
|
|
public readonly Counter FhirMappingErrorsTotal = Metrics.CreateCounter(
|
|
"fhir_mapping_errors_total",
|
|
"FHIR mapping failures.",
|
|
labelNames: new[] { "reason" });
|
|
|
|
// --- 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.");
|
|
} |