1# Structured Metrics and CrOS Events API 2 3This document describes the client side API for defining and recording Structured Metrics and CrOS Events. Structured Metrics is supported on ChromeOS, Windows, Mac, and Linux. ChromeOS support includes LaCrOS, Ash Chrome, and Platform2 processes. 4 5This is not a technical document. 6 7## Overview 8A single element of Structured Metrics is called an Event. These events are defined as a part of a project. Each project is self contained and the events of different projects are unable to be correlated. 9 10## Getting Started 11Below are the initial steps that much be take *before* implementation for Structured Metrics and CrOS Events. 121. Complete a project privacy review: Structured Metrics doesn't have a template and [CrOS Events PRD template (Google Only)](https://docs.google.com/document/d/1NywDfiLq0NVy0xyjl68r3ibfovETIguQPLp3-dI2KLo/edit?usp=sharing&resourcekey=0-J2YNtLAZ2r2j35qxqtqSxg). 132. Complete Privacy Review 143. (Optional) Design Doc 15 16## Defining Projects and Events 17Projects and Events are declared in [//tools/metrics/structured/sync/structured.xml](https://source.chromium.org/chromium/chromium/src/+/main:tools/metrics/structured/sync/structured.xml). 18 19### Required Information 20* A project has a `name` attribute. All names must be in CamelCase as they are used directly for code generation. 21* Each project must have one `owner` element, multiple are allowed. 22* An `id` element, valid values: project and none. When value is project then a unique id determined per-project. When value is none the an unique id is not assigned for the events of this project. 23* A `scope` element, valid values: profile and device. When value is profile then the unique id is determined per-profile and per-profile. When value is device then the unique id is determined per-device and per-profile. If id is none this element has no meaning. Currently, Windows, Mac, and Linux only support device scope. Scope is not applicable to projects in the ChromiumOS repository. 24* A `key-rotation` element. The key rotation describes how often the key used to generate the id is rotated to a new value. E.G. If the key rotations value is 90 then every 90 days the key will be changed resulting in new id's. 25* The project, each event, and metric must have a `summary` element. This will be used to describe the purpose of the project and when an event is recorded. 26* A series of events are defined. Please see [Event Definition](#event-definition) for more details. 27* _(Optional)_ A project has a comma separated list of targets to build for. If not defined, defaults to `chromium`. If the target `webui` is added, the project will also generate code to be used in WebUI's Renderer process (i.e. Typescript). 28 29 30### Project Example 31```xml 32<project name="Navigation" targets="chromium,webui"> 33 <owner>navigator1@chromium.com</owner> 34 <owner>navigation-team@google.com</owner> 35 <id>project</id> 36 <scope>device</scope> 37 <key-rotation>90</key-rotation> 38 <summary> 39 Users navigation within a page. 40 </summary> 41 42 <!--Events--> 43</project> 44``` 45 46### Event Definition 47* Each event must have a `name` attribute. 48* Event has an optional attribute `force_record` which allows for events to be recorded if UMA is not consented. If you think your use-case needs this functionality contact OWNERs. 49* The previously described `summary` element. 50* An event can have multiple `metric` elements. Each metric must have a `name` attribute and `type` attribute. Valid types are: hmac-string, raw-string, int, double, int-array, and project defined [Enum](#enums). `int-array` metric type has an additional attribute called `max` which is the max length of the integer array. 51* An event must have at least one metric unless it is a part of CrOS Event, see [CrOS Events](#cros-events) 52 53#### Enums 54An enum can be defined that can be used by all events of the project it was defined. Multiple enums can be defined per-project. It is recommended that the first element is a reasonable default. The syntax is defined in the [Event Examples](#event-examples) section. 55 56#### Event Examples 57```xml 58<project name="Navigation"> 59 <!--Project Metadata--> 60 61 <enum name="Direction"> 62 <variant value="0">None</variant> 63 <variant value="1">Forward</variant> 64 <variant value="3">Back</variant> 65 </enum> 66 67 68 <event name="PageTransition"> 69 <summary> 70 Captures when a user transitions page. 71 </summary> 72 73 <metric name="PageId" type="int"> 74 <summary> 75 The source page of transition. 76 </summary> 77 <metric> 78 <metric name="TransitionDirection" type="Direction"> 79 <summary> 80 The direction of the transition. 81 </summary> 82 <metric> 83 </event> 84 85 <event name="Impression"> 86 <summary> 87 Impression of the transition. 88 </summary> 89 90 <metric name="ElementIds" type="int-array" max="10"> 91 <summary> 92 Elements of source page. 93 </summary> 94 </metric> 95 </event> 96</project> 97``` 98 99## CrOS Events 100CrOS Event's is a part of Structured Metrics which provides sequencing information at the time the event was recorded. This allows for events to be sequenced. We use a proxy for timestamp which is the reset id and system uptime. Reset id is manually managed when the device boots. 101 102To declare events to be used in CrOS Events they must be declared in the [CrOSEvents](https://source.chromium.org/chromium/chromium/src/+/main:tools/metrics/structured/sync/structured.xml) project. The `name` of each event must be `<UseCase>.<EventName>`, e.g. `AppDiscovery.AppInstalled`. 103 104We are working on an alternative method for declaring CrOS Events projects that is more scalable and easier to maintain. 105 106## Client API (C++) 107By default, all projects in `structured.xml` will be built for the `chromium` target, meaning each event in the project will have an Event API generated in C++. 108 109### Event API (C++) 110At build time, a C++ API is generated from the xml. The project name will become the namespace (`navigation`) and the event name is the event's class name (`PageTransition`). Each metric has a setter method in the format `Set<MetricName>` (`SetPageId`). There are l-value and r-value versions for each metric method. 111 112### Recording (C++) 113In order to record an event the `StructuredMetricsClient` singleton is needed, defined in [//components/metrics/structured/structured_metrics_client.h](https://source.chromium.org/chromium/chromium/src/+/main:components/metrics/structured/structured_metrics_client.h). 114 115Recording can be done by: 116 117```cpp 118#include "components/metrics/structured/structured_metrics_client.h" // for StructuredMetricsClient 119#include "components/metrics/structured/structured_events.h" // for event definitions 120 121// Shorten the namespace if desired. 122using nav = metrics::structured::v2::navigation; 123 124ms::StructuredMetricsClient::Record( 125 nav::PageTransition() 126 .SetPageId(page_id) 127 .SetTransitionDirection(nav::Direction::Forward)); 128``` 129 130## Client API (Typescript) 131When a project is given a `target` of `webui`, they will have an Event API for each event, generated in Typescript, meant to be used for WebUI. The events are sent from the Renderer process to the Browser process using Mojo. Additionally, there is a recording API that can be used to record the event. 132 133### Event API (Typescript) 134At build time, a Typescript API is generated from the xml for each event. Each event will generate a builder class in Typescript. Using the same example for the C++ Event API, we will get the following: 135* Builder class name is `<ProjectName>_<EventName>`, e.g. `Navigation_PageTransition` 136* Two setter methods generated, `setPageId()` and `setTransitionDirection()` 137* One `build()` method 138 139#### Enums in Typescript 140Enums are declared at the top level in the generated `structured_events.ts` file. In the above example, we will then get an enum declared like: 141```ts 142export enum Navigation_Direction { 143None = 0, 144Forward = 1, 145Back = 3 146}; 147``` 148 149#### CrOS Events `recorded_time_since_boot` 150Since it is not possible to obtain the system uptime when recording the event in Typescript, the system uptime is inferred using the event timestamp, used in the following equation: 151 152``` 153event_system_uptime = browser_system_uptime - (browser_timestamp - event_timestamp) 154``` 155 156 157### Recording (Typescript) 158In order to record an event, the `recordStructuredEvent()` function exported by `chrome://resources/ash/common/metrics/structured_metrics_service.js` can be used. It will create the required instance of the `StructuredMetricsServiceInterface`, which is the Mojo interface. 159 160Creating an event and recording it would then look like: 161```ts 162import {recordStructuredEvent} from 'chrome://resources/ash/common/metrics/structured_metrics_service.js'; 163import * as StructuredEvents from 'chrome://resources/ash/common/metrics/structured_events.js'; 164 165// Construct the event. 166let structured_event: any = new StructuredEvents.Navigation_PageTransition() 167 .setPageId(page_id) 168 .setTransitionDirection( 169 StructuredEvents.Navigation_Direction.Forward) 170 .build(); 171 172// Record the event. 173recordStructuredEvent(structured_event); 174``` 175 176## Local Verification 177Structured Metrics has a debug page at `chrome://metrics-internals/structured` that can be used to verify recorded events and their contents. The page must be manually refreshed to see recently recorded events. 178 179<!-- TODO: Expand with image and additional functionality --> 180 181<!-- TODO: Maybe add documentation about basic server side processing --> 182