Adding a new metric type
Data in the Glean SDK is stored in so-called metrics. You can find the full list of implemented metric types in the user overview.
Adding a new metric type involves defining the metric type's API, its persisted and in-memory storage as well as its serialization into the ping payload.
The metric type's API
A metric type implementation is defined in its own file under glean-core/src/metrics/
, e.g. glean-core/src/metrics/counter.rs
for a Counter.
Start by defining a structure to hold the metric's metadata:
#[derive(Clone, Debug)]
pub struct CounterMetric {
meta: CommonMetricData
}
Implement the MetricType
trait to create a metric from the meta data as well as expose the meta data.
This also gives you a should_record
method on the metric type.
impl MetricType for CounterMetric {
fn meta(&self) -> &CommonMetricData {
&self.meta
}
fn meta_mut(&mut self) -> &mut CommonMetricData {
&mut self.meta
}
}
Its implementation should have a way to create a new metric from the common metric data. It should be the same for all metric types.
impl CounterMetric {
pub fn new(meta: CommonMetricData) -> Self {
Self { meta }
}
}
Implement each method for the type. The first argument to accept should always be glean: &Glean
, that is: a reference to the Glean
object, used to access the storage:
impl CounterMetric { // same block as above
pub fn add(&self, glean: &Glean, amount: i32) {
// Always include this check!
if !self.should_record() {
return;
}
// Do error handling here
glean
.storage()
.record_with(&self.meta, |old_value| match old_value {
Some(Metric::Counter(old_value)) => Metric::Counter(old_value + amount),
_ => Metric::Counter(amount),
})
}
}
Use glean.storage().record()
to record a fixed value or glean.storage.record_with()
to construct a new value from the currently stored one.
The storage operation makes use of the metric's variant of the Metric
enumeration.
The Metric
enumeration
Persistence and in-memory serialization as well as ping payload serialization are handled through the Metric
enumeration.
This is defined in glean-core/src/metrics/mod.rs
.
Variants of this enumeration are used in the storage implementation of the metric type.
To add a new metric type, include the metric module and declare its use, then add a new variant to the Metric
enum:
mod counter;
// ...
pub use self::counter::CounterMetric;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Metric {
// ...
Counter(i32),
}
Then modify the below implementation and define the right ping section name for the new type. This will be used in the ping payload:
impl Metric {
pub fn ping_section(&self) -> &'static str {
match self {
// ...
Metric::Counter(_) => "counter",
}
}
}
Finally, define the ping payload serialization (as JSON).
In the simple cases where the in-memory representation maps to its JSON representation it is enough to call the json!
macro.
impl Metric { // same block as above
pub fn as_json(&self) -> JsonValue {
match self {
// ...
Metric::Counter(c) => json!(c),
}
}
}
For more complex serialization consider implementing serialization logic as a function returning a serde_json::Value
or another object that can be serialized.
For example, the DateTime
serializer has the following entry, where get_iso_time_string
is a function to convert from the DateTime
metric representation to a string:
Metric::Datetime(d, time_unit) => json!(get_iso_time_string(*d, *time_unit)),
In the next step we will create the FFI wrapper and platform-specific wrappers.