Skip to main content

wlan_telemetry/util/
cobalt_logger.rs

1// Copyright 2024 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 crate::config::CobaltAllowlist;
6use fidl_fuchsia_metrics::{HistogramBucket, MetricEvent, MetricEventLoggerProxy};
7
8/// Macro wrapper for logging simple events (occurrence, integer, histogram, string)
9/// and log a warning when the status is not Ok.
10// TODO(339221340): remove these allows once the skeleton has a few uses
11#[allow(unused)]
12macro_rules! log_cobalt {
13    ($cobalt_proxy:expr, $method_name:ident, $metric_id:expr, $value:expr, $event_codes:expr $(,)?) => {{
14        let status = $cobalt_proxy.$method_name($metric_id, $value, $event_codes).await;
15        match status {
16            Ok(Ok(())) => (),
17            Ok(Err(e)) => log::info!("Failed logging metric: {}, error: {:?}", $metric_id, e),
18            Err(e) => log::info!("Failed logging metric: {}, error: {}", $metric_id, e),
19        }
20    }};
21}
22
23macro_rules! log_cobalt_batch {
24    ($cobalt_proxy:expr, $events:expr, $context:expr $(,)?) => {{
25        if !$events.is_empty() {
26            let status = $cobalt_proxy.log_metric_events($events).await;
27            match status {
28                Ok(Ok(())) => (),
29                Ok(Err(e)) => {
30                    log::info!(
31                        "Failed logging batch metrics, context: {}, error: {:?}",
32                        $context,
33                        e
34                    );
35                }
36                Err(e) => {
37                    log::info!("Failed logging batch metrics, context: {}, error: {}", $context, e)
38                }
39            }
40        }
41    }};
42}
43
44// TODO(339221340): remove these allows once the skeleton has a few uses
45#[allow(unused)]
46pub(crate) use {log_cobalt, log_cobalt_batch};
47pub struct FilteredCobaltLogger {
48    proxy: MetricEventLoggerProxy,
49    allowlist: CobaltAllowlist,
50}
51
52impl FilteredCobaltLogger {
53    pub fn new(proxy: MetricEventLoggerProxy, allowlist: CobaltAllowlist) -> Self {
54        Self { proxy, allowlist }
55    }
56
57    pub async fn log_occurrence(
58        &self,
59        metric_id: u32,
60        count: u64,
61        event_codes: &[u32],
62    ) -> Result<Result<(), fidl_fuchsia_metrics::Error>, fidl::Error> {
63        if !self.allowlist.contains(metric_id) {
64            return Ok(Ok(()));
65        }
66        self.proxy.log_occurrence(metric_id, count, event_codes).await
67    }
68
69    #[cfg_attr(not(test), expect(dead_code))]
70    pub async fn log_integer(
71        &self,
72        metric_id: u32,
73        value: i64,
74        event_codes: &[u32],
75    ) -> Result<Result<(), fidl_fuchsia_metrics::Error>, fidl::Error> {
76        if !self.allowlist.contains(metric_id) {
77            return Ok(Ok(()));
78        }
79        self.proxy.log_integer(metric_id, value, event_codes).await
80    }
81
82    #[expect(dead_code)]
83    pub async fn log_string(
84        &self,
85        metric_id: u32,
86        string_value: &str,
87        event_codes: &[u32],
88    ) -> Result<Result<(), fidl_fuchsia_metrics::Error>, fidl::Error> {
89        if !self.allowlist.contains(metric_id) {
90            return Ok(Ok(()));
91        }
92        self.proxy.log_string(metric_id, string_value, event_codes).await
93    }
94
95    #[expect(dead_code)]
96    pub async fn log_integer_histogram(
97        &self,
98        metric_id: u32,
99        histogram: &[HistogramBucket],
100        event_codes: &[u32],
101    ) -> Result<Result<(), fidl_fuchsia_metrics::Error>, fidl::Error> {
102        if !self.allowlist.contains(metric_id) {
103            return Ok(Ok(()));
104        }
105        self.proxy.log_integer_histogram(metric_id, histogram, event_codes).await
106    }
107
108    pub async fn log_metric_events(
109        &self,
110        events: &[MetricEvent],
111    ) -> Result<Result<(), fidl_fuchsia_metrics::Error>, fidl::Error> {
112        let filtered_events: Vec<MetricEvent> =
113            events.iter().filter(|e| self.allowlist.contains(e.metric_id)).cloned().collect();
114        if filtered_events.is_empty() {
115            return Ok(Ok(()));
116        }
117        self.proxy.log_metric_events(&filtered_events).await
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use crate::config::CobaltAllowlist;
124    use crate::testing::setup_test;
125    use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
126    use std::collections::HashSet;
127    use std::pin::pin;
128    use std::task::Poll;
129
130    #[fuchsia::test]
131    fn test_filtered_cobalt_logger_allow_all() {
132        let mut test_helper = setup_test();
133        let logger = test_helper.filtered_cobalt_logger();
134
135        let mut test_fut = pin!(async move {
136            logger.log_occurrence(100, 1, &[]).await.unwrap().unwrap();
137            logger.log_integer(101, 42, &[]).await.unwrap().unwrap();
138        });
139
140        assert_eq!(
141            test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
142            Poll::Ready(())
143        );
144        assert_eq!(test_helper.cobalt_events.len(), 2);
145    }
146
147    #[fuchsia::test]
148    fn test_filtered_cobalt_logger_allowlist_only() {
149        let mut test_helper = setup_test();
150        let allowlist = CobaltAllowlist::Only(HashSet::from([1, 2]));
151        let logger = test_helper.filtered_cobalt_logger_with_allowlist(allowlist);
152
153        let mut test_fut = pin!(async move {
154            logger.log_occurrence(1, 1, &[]).await.unwrap().unwrap();
155            logger.log_occurrence(3, 1, &[]).await.unwrap().unwrap();
156            let events = vec![
157                MetricEvent {
158                    metric_id: 2,
159                    event_codes: vec![],
160                    payload: MetricEventPayload::Count(1),
161                },
162                MetricEvent {
163                    metric_id: 4,
164                    event_codes: vec![],
165                    payload: MetricEventPayload::Count(1),
166                },
167            ];
168            logger.log_metric_events(&events).await.unwrap().unwrap();
169        });
170
171        assert_eq!(
172            test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
173            Poll::Ready(())
174        );
175        assert_eq!(test_helper.cobalt_events.len(), 2);
176        assert_eq!(test_helper.cobalt_events[0].metric_id, 1);
177        assert_eq!(test_helper.cobalt_events[1].metric_id, 2);
178    }
179}