archivist_lib/logs/
stats.rs1use diagnostics_data::Severity;
6use fuchsia_inspect::{
7 ArrayProperty, IntArrayProperty, IntProperty, Node, NumericProperty, Property, StringProperty,
8 UintArrayProperty, UintProperty,
9};
10use fuchsia_inspect_derive::Inspect;
11use fuchsia_sync::Mutex;
12
13#[derive(Debug, Default, Inspect)]
14pub struct LogStreamStats {
15 sockets_opened: UintProperty,
16 sockets_closed: UintProperty,
17 last_timestamp: IntProperty,
18 total: LogCounter,
19 rolled_out: LogCounter,
20 fatal: LogCounter,
21 error: LogCounter,
22 warn: LogCounter,
23 info: LogCounter,
24 debug: LogCounter,
25 trace: LogCounter,
26 url: StringProperty,
27 invalid: LogCounter,
28 inspect_node: Node,
29}
30
31impl LogStreamStats {
32 pub fn set_url(&self, url: &str) {
33 self.url.set(url);
34 }
35
36 pub fn open_socket(&self) {
37 self.sockets_opened.add(1);
38 }
39
40 pub fn close_socket(&self) {
41 self.sockets_closed.add(1);
42 }
43
44 pub fn increment_rolled_out(&self, msg_len: usize) {
45 self.rolled_out.increment_bytes(msg_len);
46 }
47
48 pub fn increment_invalid(&self, bytes: usize) {
49 self.invalid.increment_bytes(bytes);
50 }
51
52 pub fn ingest_message(&self, bytes: usize, severity: Severity) {
53 self.total.count(bytes);
54 match severity {
55 Severity::Trace => self.trace.count(bytes),
56 Severity::Debug => self.debug.count(bytes),
57 Severity::Info => self.info.count(bytes),
58 Severity::Warn => self.warn.count(bytes),
59 Severity::Error => self.error.count(bytes),
60 Severity::Fatal => self.fatal.count(bytes),
61 }
62 }
63}
64
65#[derive(Debug, Default, Inspect)]
66struct LogCounter {
67 number: UintProperty,
68 bytes: UintProperty,
69
70 inspect_node: Node,
71}
72
73impl LogCounter {
74 fn count(&self, bytes: usize) {
75 self.number.add(1);
76 self.bytes.add(bytes as u64);
77 }
78
79 fn increment_bytes(&self, bytes: usize) {
80 self.number.add(1);
81 self.bytes.add(bytes as u64);
82 }
83}
84
85pub struct GlobalAnalytics {
86 _node: Node,
87 logs_node: Node,
88}
89
90impl GlobalAnalytics {
91 pub fn new(parent: &Node) -> Self {
92 let node = parent.create_child("global_analytics");
93 let logs_node = node.create_child("logs");
94 Self { _node: node, logs_node }
95 }
96
97 pub fn logs_node(&self) -> &Node {
98 &self.logs_node
99 }
100}
101
102pub struct SaturationCurve {
103 boot_times: IntArrayProperty,
104 message_counts: UintArrayProperty,
105 cursor: Mutex<usize>,
106 size: usize,
107 _node: Node,
108}
109
110impl SaturationCurve {
111 pub fn new(parent: &Node, size: usize) -> Self {
112 let node = parent.create_child("saturation_curve");
113 let boot_times = node.create_int_array("boot_times", size);
114 let message_counts = node.create_uint_array("message_counts", size);
115 Self { boot_times, message_counts, cursor: Mutex::new(0), size, _node: node }
116 }
117
118 pub fn record(&self, boot_time: i64, message_count: u64) {
119 let mut cursor = self.cursor.lock();
120 self.boot_times.set(*cursor, boot_time);
121 self.message_counts.set(*cursor, message_count);
122 *cursor = (*cursor + 1) % self.size;
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use diagnostics_assertions::assert_data_tree;
130 use fuchsia_inspect::Inspector;
131
132 #[fuchsia::test]
133 async fn saturation_curve_circular() {
134 let inspector = Inspector::default();
135 let curve = SaturationCurve::new(inspector.root(), 3);
136
137 curve.record(1, 10);
138 curve.record(2, 20);
139 curve.record(3, 30);
140
141 assert_data_tree!(inspector,
142 root: {
143 saturation_curve: {
144 boot_times: vec![1i64, 2, 3],
145 message_counts: vec![10u64, 20, 30],
146 }
147 });
148
149 curve.record(4, 40);
151
152 assert_data_tree!(inspector,
153 root: {
154 saturation_curve: {
155 boot_times: vec![4i64, 2, 3],
156 message_counts: vec![40u64, 20, 30],
157 }
158 });
159 }
160}