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
124
125
126
127
128
129
130
131
132
// 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},
    fidl_fuchsia_metrics::{MetricEventLoggerFactoryMarker, MetricEventLoggerProxy, ProjectSpec},
    fuchsia_async as fasync,
    fuchsia_component::client::connect_to_protocol,
    fuchsia_zircon as zx,
    session_framework_metrics_registry::cobalt_registry as metrics,
    tracing::warn,
};

/// 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::Time,
    end_time: zx::Time,
) -> 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)]
mod tests {
    use {
        super::*,
        fidl::endpoints::create_proxy_and_stream,
        fidl_fuchsia_metrics::{MetricEventLoggerMarker, MetricEventLoggerRequest},
        futures::TryStreamExt,
    };

    /// Tests that the right payload is sent to Cobalt when logging the session launch time.
    #[fasync::run_singlethreaded(test)]
    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::Time::from_nanos(0);
        let end_time = zx::Time::from_nanos(5000);

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

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

    /// Tests that an error is raised if end_time < start_time.
    #[fasync::run_singlethreaded(test)]
    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::Time::from_nanos(0);
        let end_time = zx::Time::from_nanos(5000);

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