bt_metrics/
lib.rs

1// Copyright 2022 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 anyhow::{Context as _, Error};
6use log::warn;
7use {fidl_fuchsia_metrics as metrics, fuchsia_async as fasync};
8
9pub use bt_metrics_registry::*;
10
11/// Connects to the MetricEventLoggerFactory service to create a
12/// MetricEventLoggerProxy for the caller.
13pub fn create_metrics_logger() -> Result<metrics::MetricEventLoggerProxy, Error> {
14    let factory_proxy =
15        fuchsia_component::client::connect_to_protocol::<metrics::MetricEventLoggerFactoryMarker>()
16            .context("connecting to metrics")?;
17
18    let (cobalt_proxy, cobalt_server) =
19        fidl::endpoints::create_proxy::<metrics::MetricEventLoggerMarker>();
20
21    let project_spec = metrics::ProjectSpec {
22        customer_id: None, // defaults to fuchsia
23        project_id: Some(PROJECT_ID),
24        ..Default::default()
25    };
26
27    fasync::Task::spawn(async move {
28        match factory_proxy.create_metric_event_logger(&project_spec, cobalt_server).await {
29            Err(e) => warn!("FIDL failure setting up event logger: {e:?}"),
30            Ok(Err(e)) => warn!("CreateMetricEventLogger failure: {e:?}"),
31            Ok(Ok(())) => {}
32        }
33    })
34    .detach();
35
36    Ok(cobalt_proxy)
37}
38
39pub fn log_on_failure(result: Result<Result<(), metrics::Error>, fidl::Error>) {
40    match result {
41        Ok(Ok(())) => (),
42        e => warn!("failed to log metrics: {:?}", e),
43    };
44}
45
46/// An optional client connection to the Cobalt logging service.
47#[derive(Clone, Default)]
48pub struct MetricsLogger(Option<metrics::MetricEventLoggerProxy>);
49
50impl MetricsLogger {
51    pub fn new() -> Self {
52        let logger =
53            create_metrics_logger().map_err(|e| warn!("Failed to create metrics logger: {e}")).ok();
54        Self(logger)
55    }
56
57    /// Test-only
58    pub fn from_proxy(proxy: metrics::MetricEventLoggerProxy) -> Self {
59        Self(Some(proxy))
60    }
61
62    /// Logs an occurrence metric using the Cobalt logger. Does not block execution.
63    pub fn log_occurrence(&self, id: u32, event_codes: Vec<u32>) {
64        let Some(c) = self.0.clone() else { return };
65        fuchsia_async::Task::spawn(async move {
66            log_on_failure(c.log_occurrence(id, 1, &event_codes).await);
67        })
68        .detach();
69    }
70
71    /// Logs an integer metric using the Cobalt logger. Does not block execution.
72    pub fn log_integer(&self, id: u32, value: i64, event_codes: Vec<u32>) {
73        let Some(c) = self.0.clone() else { return };
74        fuchsia_async::Task::spawn(async move {
75            log_on_failure(c.log_integer(id, value, &event_codes).await);
76        })
77        .detach();
78    }
79
80    pub fn log_occurrences<I: 'static + IntoIterator<Item = u32> + std::marker::Send>(
81        &self,
82        id: u32,
83        event_codes: I,
84    ) where
85        <I as IntoIterator>::IntoIter: std::marker::Send,
86    {
87        let Some(c) = self.0.clone() else { return };
88        fasync::Task::spawn(async move {
89            for code in event_codes {
90                log_on_failure(c.log_occurrence(id, 1, &[code]).await);
91            }
92        })
93        .detach();
94    }
95}
96
97/// Test-only
98pub fn respond_to_metrics_req_for_test(
99    request: metrics::MetricEventLoggerRequest,
100) -> metrics::MetricEvent {
101    match request {
102        metrics::MetricEventLoggerRequest::LogOccurrence {
103            metric_id,
104            count,
105            event_codes,
106            responder,
107        } => {
108            let _ = responder.send(Ok(())).unwrap();
109            metrics::MetricEvent {
110                metric_id,
111                event_codes,
112                payload: metrics::MetricEventPayload::Count(count),
113            }
114        }
115        metrics::MetricEventLoggerRequest::LogInteger {
116            metric_id,
117            value,
118            event_codes,
119            responder,
120        } => {
121            let _ = responder.send(Ok(())).unwrap();
122            metrics::MetricEvent {
123                metric_id,
124                event_codes,
125                payload: metrics::MetricEventPayload::IntegerValue(value),
126            }
127        }
128        _ => panic!("unexpected logging to Cobalt"),
129    }
130}