1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::{format_err, Context, Error};
use fidl_fuchsia_metrics::{MetricEventLoggerFactoryMarker, MetricEventLoggerProxy, ProjectSpec};
use fuchsia_component::client::connect_to_protocol;
use session_framework_metrics_registry::cobalt_registry as metrics;
use tracing::warn;
use {fuchsia_async as fasync, fuchsia_zircon as zx};

/// Creates a `LoggerProxy` connected to Cobalt.
///
/// The connection is performed in a Future run on the global executor, but the `LoggerProxy`
/// can be used immediately.
///
/// # Returns
/// `LoggerProxy` for log messages to be sent to.
pub fn get_logger() -> Result<MetricEventLoggerProxy, Error> {
    let (logger_proxy, server_end) =
        fidl::endpoints::create_proxy().context("Failed to create endpoints")?;
    let logger_factory = connect_to_protocol::<MetricEventLoggerFactoryMarker>()
        .context("Failed to connect to the Cobalt MetricEventLoggerFactory")?;

    fasync::Task::spawn(async move {
        if let Err(err) = logger_factory
            .create_metric_event_logger(
                &ProjectSpec { project_id: Some(metrics::PROJECT_ID), ..Default::default() },
                server_end,
            )
            .await
        {
            warn!(%err, "Failed to create Cobalt logger");
        }
    })
    .detach();

    Ok(logger_proxy)
}

/// Reports the time elapsed while launching a session.
///
/// # Parameters
/// - `logger_proxy`: The cobalt logger.
/// - `start_time`: The time when `session_manager` starts launching a session.
/// - `end_time`: The time when `session_manager` has bound to a session. This must be strictly after
///               `start_time`.
///
/// # Returns
/// `Ok` if the time elapsed was logged successfully.
pub async fn log_session_launch_time(
    logger_proxy: MetricEventLoggerProxy,
    start_time: zx::MonotonicTime,
    end_time: zx::MonotonicTime,
) -> Result<(), Error> {
    let elapsed_time = (end_time - start_time).into_micros();
    if elapsed_time < 0 {
        return Err(format_err!("End time must be after start time."));
    }

    logger_proxy
        .log_integer(
            metrics::SESSION_LAUNCH_TIME_MIGRATED_METRIC_ID,
            elapsed_time,
            &[metrics::SessionLaunchTimeMigratedMetricDimensionStatus::Success as u32],
        )
        .await
        .context("Could not log session launch time.")?
        .map_err(|e| format_err!("Logging session launch time returned an error: {:?}", e))?;

    Ok(())
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;
    use assert_matches::assert_matches;
    use fidl::endpoints::create_proxy_and_stream;
    use fidl_fuchsia_metrics::{MetricEventLoggerMarker, MetricEventLoggerRequest};
    use futures::TryStreamExt;

    /// Tests that the right payload is sent to Cobalt when logging the session launch time.
    #[fuchsia::test(allow_stalls = false)]
    async fn test_log_session_launch_time() {
        let (logger_proxy, mut logger_server) =
            create_proxy_and_stream::<MetricEventLoggerMarker>()
                .expect("Failed to create Logger FIDL.");
        let start_time = zx::MonotonicTime::from_nanos(0);
        let end_time = zx::MonotonicTime::from_nanos(5000);

        fasync::Task::spawn(async move {
            let _ = log_session_launch_time(logger_proxy, start_time, end_time).await;
        })
        .detach();

        assert_matches!(
            logger_server.try_next().await.unwrap(),
            Some(MetricEventLoggerRequest::LogInteger {
                metric_id: metrics::SESSION_LAUNCH_TIME_MIGRATED_METRIC_ID,
                value: 5,
                event_codes,
                responder: _,
            }) => {
                assert_eq!(
                    event_codes,
                    vec![metrics::SessionLaunchTimeMigratedMetricDimensionStatus::Success as u32]
                );
            }
        )
    }

    /// Tests that an error is raised if end_time < start_time.
    #[fuchsia::test(allow_stalls = false)]
    async fn test_log_session_launch_time_swap_start_end_time() {
        let (logger_proxy, _logger_server) = create_proxy_and_stream::<MetricEventLoggerMarker>()
            .expect("Failed to create Logger FIDL.");
        let start_time = zx::MonotonicTime::from_nanos(0);
        let end_time = zx::MonotonicTime::from_nanos(5000);

        assert!(log_session_launch_time(logger_proxy, end_time, start_time).await.is_err());
    }
}