1use serde::{Deserialize, Serialize};
2
3use super::schema_version::SCHEMA_VERSION;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Observation<T> {
9 pub schema_version: u16,
10 pub observation_id: String,
11 pub captured_at_unix_ms: u64,
13 pub monotonic_elapsed_ms: u64,
15 pub collection_interval_ms: Option<u64>,
16 pub source: SourceId,
17 pub completeness: Completeness,
18 pub data: T,
19}
20
21impl<T> Observation<T> {
22 pub fn new(
23 source: SourceId,
24 interval_ms: Option<u64>,
25 monotonic_elapsed_ms: u64,
26 data: T,
27 completeness: Completeness,
28 ) -> Self {
29 Self {
30 schema_version: SCHEMA_VERSION,
31 observation_id: uuid::Uuid::new_v4().to_string(),
32 captured_at_unix_ms: chrono::Utc::now().timestamp_millis() as u64,
33 monotonic_elapsed_ms,
34 collection_interval_ms: interval_ms,
35 source,
36 completeness,
37 data,
38 }
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct SourceId {
44 pub console_addr: String,
45 pub service_name: Option<String>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Completeness {
50 pub partial: bool,
51 pub missing_fields: Vec<String>,
52 pub warnings: Vec<String>,
53}
54
55impl Completeness {
56 pub fn complete() -> Self {
57 Self {
58 partial: false,
59 missing_fields: Vec::new(),
60 warnings: Vec::new(),
61 }
62 }
63
64 pub fn partial(missing_fields: Vec<String>, warnings: Vec<String>) -> Self {
65 Self {
66 partial: true,
67 missing_fields,
68 warnings,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct RuntimeOverview {
75 pub service_name: Option<String>,
76 pub console_addr: String,
77 pub active_tasks: Option<u64>,
78 pub completed_tasks: Option<u64>,
79 pub tasks_truncated: bool,
80 pub metric_names: Vec<String>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(tag = "kind")]
86pub enum MetricKind {
87 Counter {
88 total: f64,
89 reset_detected: bool,
90 },
91 Gauge {
92 value: f64,
93 },
94 Histogram {
95 count: u64,
96 sum: f64,
97 p50: Option<f64>,
98 p95: Option<f64>,
99 p99: Option<f64>,
100 },
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct MetricObservation {
105 pub name: String,
106 pub kind: MetricKind,
107 pub unit: String,
108 pub observed_at_unix_ms: u64,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct TaskRecord {
113 pub task_id: u64,
114 pub name: Option<String>,
115 pub polls: Option<u64>,
116 pub wakes: Option<u64>,
117 pub self_wakes: Option<u64>,
118 pub busy_ns: Option<u64>,
119 pub scheduled_ns: Option<u64>,
120 pub state: Option<String>,
121 pub location: Option<String>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ResourceRecord {
126 pub resource_id: u64,
127 pub concrete_type: Option<String>,
128 pub kind: Option<String>,
129 pub attributes: Vec<(String, String)>,
130 pub location: Option<String>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct Snapshot {
135 pub overview: RuntimeOverview,
136 pub tasks: Vec<TaskRecord>,
137 pub resources: Vec<ResourceRecord>,
138 pub metrics: Vec<MetricObservation>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct SnapshotDelta {
143 pub base_id: String,
144 pub current_id: String,
145 pub latency_delta_ratio: Option<f64>,
146 pub task_growth: Option<i64>,
147 pub unreliable: bool,
148 pub reason: Option<String>,
149 pub notes: Vec<String>,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
153pub enum FindingStatus {
154 Observed,
155 Hypothesis,
156 Verified,
157 NoIssue,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct Diagnosis {
162 pub status: FindingStatus,
163 pub finding: String,
164 pub hypothesis: Option<String>,
165 pub confidence: f32,
167 pub evidence: Vec<String>,
169 pub verification: Option<String>,
170}
171
172impl Diagnosis {
173 pub fn is_valid(&self) -> bool {
174 !self.evidence.is_empty() && (0.0..=1.0).contains(&self.confidence)
175 }
176}