Files
vigilcare-clinical/VigilCareClinicalAPI/Observability/Metrics/ClinicalMetrics.cs
T

166 lines
7.3 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 FhirReadTotal = Metrics.CreateCounter(
"fhir_read_total",
"FHIR resource read/search operations.",
labelNames: new[] { "resource_type", "interaction", "outcome" });
public readonly Counter AuthorizationFailuresTotal = Metrics.CreateCounter(
"authorization_failures_total",
"Authorization failures by permission and role.",
labelNames: new[] { "permission", "role" });
public readonly Counter FhirMappingErrorsTotal = Metrics.CreateCounter(
"fhir_mapping_errors_total",
"FHIR mapping failures.",
labelNames: new[] { "reason" });
public readonly Counter PhiAccessLogsTotal = Metrics.CreateCounter(
"phi_access_logs_total",
"PHI access log entries written.",
labelNames: new[] { "access_type" });
public readonly Counter ClinicalSyncBatchesTotal = Metrics.CreateCounter(
"clinical_sync_batches_total",
"Sync batches processed",
labelNames: new[] { "status" });
// --- 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 }
});
public readonly Histogram ClinicalSyncBatchDuration = Metrics.CreateHistogram(
"clinical_sync_batch_duration_seconds",
"Batch processing duration",
new HistogramConfiguration
{
Buckets = new[] { 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0 }
});
// --- 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.");
public readonly Gauge WardGatewaysOffline = Metrics.CreateGauge(
"ward_gateways_offline_gauge",
"Ward gateways with status OFFLINE or DEGRADED",
labelNames: new[] { "site_code" });
public readonly Gauge WardGatewayBufferDepth = Metrics.CreateGauge(
"ward_gateway_buffer_depth",
"Reported unsynced event count per gateway",
labelNames: new[] { "gateway_code", "department" });
}