Skip to main content

archivist_lib/logs/
stats.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // Should wrap around
150        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}