Timespan

Timespans are used to make a measurement of how much time is spent in a particular task.

To measure the distribution of multiple timespans, see Timing Distributions. To record absolute times, see Datetimes.

It is not recommended to use timespans in multiple threads, since calling start or stop out of order will be recorded as an invalid_state error.

Recording API

start

Starts tracking time. Uses an internal monotonic timer.

import org.mozilla.yourApplication.GleanMetrics.Auth

fun onShowLogin() {
    Auth.loginTime.start()
    // ...
}
import org.mozilla.yourApplication.GleanMetrics.Auth;

void onShowLogin() {
    Auth.INSTANCE.loginTime.start();
    // ...
}
func onShowLogin() {
    Auth.loginTime.start()
    // ...
}
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

def on_show_login():
    metrics.auth.login_time.start()
    # ...

#![allow(unused)]
fn main() {
use fog::metrics;

fn show_login() {
    metrics::auth::login_time.start();
    // ...
}
}
import * as auth from "./path/to/generated/files/auth.js";

function onShowLogin() {
    auth.loginTime.start();
    // ...
}

C++

#include "mozilla/glean/GleanMetrics.h"
void OnShowLogin() {
  mozilla::glean::auth::login_time.Start();
  // ...
}

JavaScript

function onShowLogin() {
  Glean.auth.loginTime.start();
  // ...
}

Recorded errors

  • invalid_state: If the metric is already tracking time (start has already been called and not canceled).

Limits

  • The maximum resolution of the elapsed duration is limited by the clock used on each platform.
  • This also determines the behavior of a timespan over sleep:
    • On Android, the SystemClock.elapsedRealtimeNanos() function is used, so it is limited by the accuracy and performance of that timer. The time measurement includes time spent in sleep.
    • On iOS, the mach_absolute_time function is used, so it is limited by the accuracy and performance of that timer. The time measurement does not include time spent in sleep.
    • On Python 3.7 and later, time.monotonic_ns() is used. On earlier versions of Python, time.monotonics() is used, which is not guaranteed to have nanosecond resolution.
    • On other platforms time::precise_time_ns is used, which uses a high-resolution performance counter in nanoseconds provided by the underlying platform.

stop

Stops tracking time. The metric value is set to the elapsed time.

import org.mozilla.yourApplication.GleanMetrics.Auth

fun onLogin() {
    Auth.loginTime.stop()
    // ...
}
import org.mozilla.yourApplication.GleanMetrics.Auth;

void onLogin() {
    Auth.INSTANCE.loginTime.stop();
    // ...
}
func onLogin() {
    Auth.loginTime.stop()
    // ...
}
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

def on_login():
    metrics.auth.login_time.stop()
    # ...

#![allow(unused)]
fn main() {
use fog::metrics;

fn login() {
    metrics::auth::login_time.stop();
    // ...
}
}
import * as auth from "./path/to/generated/files/auth.js";

function onLogin() {
    auth.login_time.stop();
    // ...
}

C++

#include "mozilla/glean/GleanMetrics.h"
void OnLogin() {
  mozilla::glean::auth::login_time.Stop();
  // ...
}

JavaScript

function onLogin() {
  Glean.auth.loginTime.stop();
  // ...
}

Recorded errors

cancel

Cancels a previous start. No error is recorded if there was no previous start.

import org.mozilla.yourApplication.GleanMetrics.Auth

fun onLoginCancel() {
    Auth.loginTime.cancel()
    // ...
}
import org.mozilla.yourApplication.GleanMetrics.Auth;

void onLoginCancel() {
    Auth.INSTANCE.loginTime.cancel();
    // ...
}
func onLoginCancel() {
    Auth.loginTime.cancel()
    // ...
}
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

def on_login_cancel():
    metrics.auth.login_time.cancel()
    # ...

#![allow(unused)]
fn main() {
use fog::metrics;

fn login_cancel() {
    metrics::auth::login_time.cancel();
    // ...
}
}
import * as auth from "./path/to/generated/files/auth.js";

function onLoginCancel() {
    auth.login_time.cancel();
    // ...
}

C++

#include "mozilla/glean/GleanMetrics.h"
void OnLoginCancel() {
  mozilla::glean::auth::login_time.Cancel();
  // ...
}

JavaScript

function onLoginCancel() {
  Glean.auth.loginTime.cancel();
  // ...
}

measure

Some languages support convenient auto timing of blocks of code. measure is treated as a start and stop pair for the purposes of error recording. Exceptions (if present in the language) are treated as a cancel.

import org.mozilla.yourApplication.GleanMetrics.Auth

Auth.loginTime.measure {
    // Process login flow
}
import org.mozilla.yourApplication.GleanMetrics.Auth

Auth.INSTANCE.loginTime.measure() -> {
    // Process login flow
    return null;
});
Auth.loginTime.measure {
    // Process login flow
}
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

with metrics.auth.login_time.measure():
    # ... Do the login ...

setRawNanos

Explicitly sets the timespan's value.

Regardless of the time unit chosen for the metric, this API expects the raw value to be in nanoseconds.

Only use this if you have to

This API should only be used if the code being instrumented cannot make use of start, stop, and cancel or measure. Time is hard, and this API can't help you with it.

import org.mozilla.yourApplication.GleanMetrics.Auth

fun afterLogin(loginElapsedNs: Long) {
    Auth.loginTime.setRawNanos(loginElapsedNs)
    // ...
}
import org.mozilla.yourApplication.GleanMetrics.Auth;

void afterLogin(long loginElapsedNs) {
    Auth.INSTANCE.loginTime.setRawNanos(loginElapsedNs);
    // ...
}
func afterLogin(_ loginElapsedNs: UInt64) {
    Auth.loginTime.setRawNanos(loginElapsedNs)
    // ...
}
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

def after_login(login_elapsed_ns):
    metrics.auth.login_time.set_raw_nanos(login_elapsed_ns)
    # ...

#![allow(unused)]
fn main() {
use fog::metrics;
use std::time::duration;

fn after_login(login_elapsed: Duration) {
    metrics::auth::login_time.set_raw(login_elapsed);
    // ...
}
}
import * as auth from "./path/to/generated/files/auth.js";

function onAfterLogin(loginElapsedNs) {
    auth.loginTime.setRawNanos(loginElapsedNs);
    // ...
}

These are different

Firefox Desktop's setRaw uses the units specified in the metric definition. e.g. if the Timespan's time_unit is millisecond, then the duration parameter is a count of milliseconds.

C++

#include "mozilla/glean/GleanMetrics.h"

void AfterLogin(uint32_t aDuration) {
  mozilla::glean::auth::login_time.SetRaw(aDuration);
  // ...
}

JavaScript

function afterLogin(aDuration) {
  Glean.auth.loginTime.setRaw(aDuration);
  // ...
}

Recorded errors

  • invalid_value: If you attempt to record a negative elapsed duration.
  • invalid_state: If you call this API after calling start or call this API multiple times.

Testing API

testGetValue

Get the currently-stored value.

import org.mozilla.yourApplication.GleanMetrics.Auth

assertTrue(Auth.loginTime.testGetValue() > 0)
import org.mozilla.yourApplication.GleanMetrics.Auth;

assertTrue(Auth.INSTANCE.loginTime.testGetValue() > 0);
@testable import Glean

XCTAssert(try Auth.loginTime.testGetValue() > 0)
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

assert metrics.auth.login_time.test_get_value() > 0

#![allow(unused)]
fn main() {
use fog::metrics;

assert!(metrics::login_time.test_get_value().unwrap() > 0);
}
import * as auth from "./path/to/generated/files/auth.js";

assert(await auth.loginTime.testGetValue() > 0);

C++

#include "mozilla/glean/GleanMetrics.h"

ASSERT_TRUE(mozilla::glean::auth::login_time.TestGetValue().isOk());
ASSERT_GE(mozilla::glean::auth::login_time.TestGetValue().unwrap().value(), 0);

JavaScript

// testGetValue will throw NS_ERROR_LOSS_OF_SIGNIFICANT_DATA on error.
Assert.ok(Glean.auth.loginTime.testGetValue() > 0);

testHasValue

Test if the metric currently stores any value.

import org.mozilla.yourApplication.GleanMetrics.Auth

assertTrue(Auth.loginTime.testHasValue())
import org.mozilla.yourApplication.GleanMetrics.Auth;

assertTrue(Auth.INSTANCE.loginTime.testHasValue() > 0);
@testable import Glean

XCTAssert(try Auth.loginTime.testHasValue() > 0)
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

assert metrics.auth.login_time.test_has_value() > 0

#![allow(unused)]
fn main() {
use fog::metrics;

assert!(metrics::login_time.test_has_value().unwrap() > 0);
}

testGetNumRecordedErrors

Gets the number of errors recorded during operations on this metric.

import org.mozilla.yourApplication.GleanMetrics.Auth

assertEquals(1, Auth.loginTime.testGetNumRecordedErrors(ErrorType.InvalidValue))
import org.mozilla.yourApplication.GleanMetrics.Auth;

assertEquals(
    1,
    Auth.INSTANCE.loginTime.testGetNumRecordedErrors(
        ErrorType.InvalidValue
    )
);
@testable import Glean

XCTAssertEqual(1, Auth.loginTime.testGetNumRecordedErrors(.invalidValue))
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

assert 1 == metrics.auth.local_time.test_get_num_recorded_errors(
    ErrorType.INVALID_VALUE
)

#![allow(unused)]
fn main() {
use fog::metrics;

assert_eq!(1, login_time.test_get_num_recorded_errors(ErrorType::InvalidValue));
}
import * as auth from "./path/to/generated/files/auth.js";
import { ErrorType } from "@mozilla/glean/<platform>";;

assert.strictEqual(
  1,
  await auth.loginTime.testGetNumRecordedErrors(ErrorType.InvalidValue)
);

Metric parameters

Example timespan metric definition:

auth:
  login_time:
    type: timespan
    description: >
      Measures the time spent logging in.
    time_unit: millisecond
    bugs:
      - https://bugzilla.mozilla.org/show_bug.cgi?id=000000
    data_reviews:
      - https://bugzilla.mozilla.org/show_bug.cgi?id=000000#c3
    notification_emails:
      - me@mozilla.com
    expires: 2020-01-01
    data_sensitivity:
      - interaction

For a full reference on metrics parameters common to all metric types, refer to the metrics YAML registry format reference page.

Extra metric parameters

time_unit

Timespans have a required time_unit parameter to specify the smallest unit of resolution that the timespan will record. The allowed values for time_unit are:

  • nanosecond
  • microsecond
  • millisecond
  • second
  • minute
  • hour
  • day

Consider the resolution that is required by your metric, and use the largest possible value that will provide useful information so as to not leak too much fine-grained information from the client.

Values are truncated

It is important to note that the value sent in the ping is truncated down to the nearest unit. Therefore, a measurement of 500 nanoseconds will be truncated to 0 microseconds.

Data questions

  • How long did it take for the user to log in?

Reference