fxa_client/internal/telemetry.rs
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 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::{commands, FirefoxAccount};
use crate::Result;
use serde_derive::*;
use sync_guid::Guid;
impl FirefoxAccount {
/// Gathers and resets telemetry for this account instance.
/// This should be considered a short-term solution to telemetry gathering
/// and should called whenever consumers expect there might be telemetry,
/// and it should submit the telemetry to whatever telemetry system is in
/// use (probably glean).
///
/// The data is returned as a JSON string, which consumers should parse
/// forgivingly (eg, be tolerant of things not existing) to try and avoid
/// too many changes as telemetry comes and goes.
pub fn gather_telemetry(&mut self) -> Result<String> {
let telem = std::mem::replace(&mut self.telemetry, FxaTelemetry::new());
Ok(serde_json::to_string(&telem)?)
}
}
// A somewhat mixed-bag of all telemetry we want to collect. The idea is that
// the app will "pull" telemetry via a new API whenever it thinks there might
// be something to record.
// It's considered a temporary solution until either we can record it directly
// (eg, via glean) or we come up with something better.
// Note that this means we'll lose telemetry if we crash between gathering it
// here and the app submitting it, but that should be rare (in practice,
// apps will submit it directly after an operation that generated telememtry)
/// The reason a tab/command was received.
#[derive(Copy, Clone, Debug, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum ReceivedReason {
/// A push notification for the command was received.
Push,
/// Discovered while handling a push notification for a later message.
PushMissed,
/// Explicit polling for missed commands.
Poll,
}
#[derive(Copy, Clone, Debug, Serialize)]
pub enum Command {
#[serde(rename = "send_tab")]
SendTab,
#[serde(rename = "close_tabs")]
CloseTabs,
}
#[derive(Debug, Serialize)]
pub struct SentCommand {
pub command: Command,
pub flow_id: String,
pub stream_id: String,
}
impl SentCommand {
pub fn for_send_tab() -> Self {
Self::new(Command::SendTab)
}
pub fn for_close_tabs() -> Self {
Self::new(Command::CloseTabs)
}
pub fn clone_with_new_stream_id(&self) -> Self {
Self {
command: self.command,
flow_id: self.flow_id.clone(),
stream_id: Guid::random().into_string(),
}
}
fn new(command: Command) -> Self {
Self {
command,
flow_id: Guid::random().into_string(),
stream_id: Guid::random().into_string(),
}
}
}
#[derive(Debug, Serialize)]
pub struct ReceivedCommand {
pub command: Command,
pub flow_id: String,
pub stream_id: String,
pub reason: ReceivedReason,
}
impl ReceivedCommand {
pub fn for_send_tab(payload: &commands::SendTabPayload, reason: ReceivedReason) -> Self {
Self {
command: Command::SendTab,
flow_id: payload.flow_id.clone(),
stream_id: payload.stream_id.clone(),
reason,
}
}
pub fn for_close_tabs(payload: &commands::CloseTabsPayload, reason: ReceivedReason) -> Self {
Self {
command: Command::SendTab,
flow_id: payload.flow_id.clone(),
stream_id: payload.stream_id.clone(),
reason,
}
}
}
// We have a naive strategy to avoid unbounded memory growth - the intention
// is that if any platform lets things grow to hit these limits, it's probably
// never going to consume anything - so it doesn't matter what we discard (ie,
// there's no good reason to have a smarter circular buffer etc)
const MAX_TAB_EVENTS: usize = 200;
#[derive(Debug, Default, Serialize)]
pub struct FxaTelemetry {
commands_sent: Vec<SentCommand>,
commands_received: Vec<ReceivedCommand>,
}
impl FxaTelemetry {
pub fn new() -> Self {
FxaTelemetry {
..Default::default()
}
}
pub fn record_command_sent(&mut self, sent: SentCommand) {
if self.commands_sent.len() < MAX_TAB_EVENTS {
self.commands_sent.push(sent);
}
}
pub fn record_command_received(&mut self, recd: ReceivedCommand) {
if self.commands_received.len() < MAX_TAB_EVENTS {
self.commands_received.push(recd);
}
}
}