Initializing
Glean needs to be initialized in order to be able to send pings, record metrics and perform maintenance tasks. Thus it is advised that Glean be initialized as soon as possible in an application's lifetime and importantly, before any other libraries in the application start using Glean.
Libraries are not required to initialize Glean
Libraries rely on the same Glean singleton as the application in which they are embedded. Hence, they are not expected to initialize Glean as the application should already do that.
Behavior when uninitialized
Any API called before Glean is initialized is queued and applied at initialization. To avoid unbounded memory growth the queue is bounded (currently to a maximum of 1 million tasks), and further calls are dropped.
The number of calls dropped, if any,
is recorded in the glean.error.preinit_tasks_overflow
metric.
Behavior once initialized
When upload is enabled
Once initialized, if upload is enabled, Glean applies all metric recordings and ping submissions, for both user-defined and builtin metrics and pings.
This always happens asynchronously.
When upload is disabled
If upload is disabled, any persisted metrics, events and pings (other than first_run_date
) are cleared.
Pending deletion-request
pings are sent.
Subsequent calls to record metrics and submit pings will be no-ops.
Because Glean does that as part of its initialization, users are required to always initialize Glean.
Glean must be initialized even if upload is disabled.
This does not apply to special builds where telemetry is disabled at build time. In that case, it is acceptable to not call initialize at all.
API
Glean.initialize(configuration)
Initializes Glean.
May only be called once. Subsequent calls to initialize
are no-op.
Configuration
The available initialize
configuration options may vary depending on the SDK.
Below are listed the configuration options available on most SDKs.
Note that on some SDKs some of the options are taken as a configuration object.
Check the respective SDK documentation for details.
Configuration Option | Default value | Description |
---|---|---|
applicationId | On Android/iOS: determined automatically. Otherwise required. | Application identifier. For Android and iOS applications, this is the id used on the platform's respective app store and is extracted automatically from the application context. |
uploadEnabled | Required | The user preference on whether or not data upload is enabled. |
channel | - | The application's release channel. When present, the app_channel will be reported in all pings' client_info sections. |
appBuild | On Android/iOS: determined automatically. Otherwise: - | A build identifier e.g. the build identifier generated by a CI system (e.g. "1234/A"). If not present, app_build will be reported as "Unknown" on all pings' client_info sections. |
appDisplayVersion | - | The user visible version string for the application running Glean. If not present, app_display_version will be reported as "Unknown" on all pings' client_info sections. |
serverEndpoint | https://incoming.telemetry.mozilla.org | The server pings are sent to. |
maxEvents | Glean.js: 1. Other SDKs: 500. | The maximum number of events the Glean storage will hold on to before submitting the 'events' ping. Refer to the events ping documentation for more information on its scheduling. |
httpUploader | - | A custom HTTP uploader instance, that will overwrite Glean's provided uploader. Useful for users that wish to use specific uploader implementations. See Custom Uploaders for more information on how and when the use this feature. |
logLevel | - | The level for how verbose the internal logging is. The level filter options in order from least to most verbose are: Off , Error , Warn , Info , Debug , Trace . See the log crate docs for more information. |
enableEventTimestamps | true | Whether to add a wall clock timestamp to all events. |
rateLimit | 15 pings per 60s interval | Specifies the maximum number of pings that can be uploaded per interval of a specified number of seconds. |
experimentationId | - | Optional. An identifier derived by the application to be sent in all pings for the purpose of experimentation. See the experiments API documentation for more information. |
enableInternalPings | true | Whether to enable the internal "baseline", "events", and "metrics" pings. |
delayPingLifetimeIo | false | Whether Glean should delay persistence of data from metrics with ping lifetime. On Android data is automatically persisted every 1000 writes and on backgrounding when enabled. |
To learn about SDK specific configuration options available, refer to the Reference section.
Always initialize Glean with the correct upload preference
Glean must always be initialized with real values.
Always pass the user preference, e.g.
Glean.initialize(uploadEnabled=userSettings.telemetryEnabled)
or the equivalent for your application.Calling
Glean.setUploadEnabled(false)
at a later point will triggerdeletion-request
pings and regenerate client IDs. This should only be done if the user preference actually changes.
An excellent place to initialize Glean is within the onCreate
method of the class that extends Android's Application
class.
import org.mozilla.yourApplication.GleanMetrics.GleanBuildInfo
import org.mozilla.yourApplication.GleanMetrics.Pings
class SampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// If you have custom pings in your application, you must register them
// using the following command. This command should be omitted for
// applications not using custom pings.
Glean.registerPings(Pings)
// Initialize the Glean library.
Glean.initialize(
applicationContext,
// Here, `settings()` is a method to get user preferences, specific to
// your application and not part of the Glean API.
uploadEnabled = settings().isTelemetryEnabled,
configuration = Configuration(),
buildInfo = GleanBuildInfo.buildInfo
)
}
}
The Glean Kotlin SDK supports use across multiple processes. This is enabled by setting a dataPath
value in the Glean.Configuration
object passed to Glean.initialize
. You do not need to set a dataPath
for your main process. This configuration should only be used by a non-main process.
Requirements for a non-main process:
Glean.initialize
must be called with thedataPath
value set in theGlean.Configuration
.- The default
dataPath
for Glean is{context.applicationInfo.dataDir}/glean_data
. If you try to use this path,Glean.initialize
will fail and throw an error. - Set the default process name as your main process. If this is not set up correctly, pings from the non-main process will not send.
Configuration.Builder().setDefaultProcessName(<main_process_name>)
Note: When initializing from a non-main process with a specified dataPath
, the lifecycle observers will not be set up. This means you will not receive otherwise scheduled baseline or metrics pings.
Consuming Glean through Android Components
When the Glean Kotlin SDK is consumed through Android Components, it is required to configure an HTTP client to be used for upload.
For example:
// Requires `org.mozilla.components:concept-fetch`
import mozilla.components.concept.fetch.Client
// Requires `org.mozilla.components:lib-fetch-httpurlconnection`.
// This can be replaced by other implementations, e.g. `lib-fetch-okhttp`
// or an implementation from `browser-engine-gecko`.
import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient
import mozilla.components.service.glean.config.Configuration
import mozilla.components.service.glean.net.ConceptFetchHttpUploader
val httpClient = ConceptFetchHttpUploader(lazy { HttpURLConnectionClient() as Client })
val config = Configuration(httpClient = httpClient)
Glean.initialize(
context,
uploadEnabled = true,
configuration = config,
buildInfo = GleanBuildInfo.buildInfo
)
An excellent place to initialize Glean is within the application(_:)
method of the class that extends the UIApplicationDelegate
class.
import Glean
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// If you have custom pings in your application, you must register them
// using the following command. This command should be omitted for
// applications not using custom pings.
Glean.shared.registerPings(GleanMetrics.Pings)
// Initialize the Glean library.
Glean.shared.initialize(
// Here, `Settings` is a method to get user preferences specific to
// your application, and not part of the Glean API.
uploadEnabled = Settings.isTelemetryEnabled,
configuration = Configuration(),
buildInfo = GleanMetrics.GleanBuild.info
)
}
}
The Glean Swift SDK supports use across multiple processes. This is enabled by setting a dataPath
value in the Glean.Configuration
object passed to Glean.initialize
. You do not need to set a dataPath
for your main process. This configuration should only be used by a non-main process.
Requirements for a non-main process:
Glean.initialize
must be called with thedataPath
value set in theGlean.Configuration
.- On iOS devices, Glean stores data in the Application Support directory. The default
dataPath
Glean uses is{FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]}/glean_data
. If you try to use this path,Glean.initialize
will fail and throw an error.
Note: When initializing from a non-main process with a specified dataPath
, the lifecycle observers will not be set up. This means you will not receive otherwise scheduled baseline or metrics pings.
The main control for the Glean Python SDK is on the glean.Glean
singleton.
from glean import Glean, Configuration
Glean.initialize(
application_id="my-app-id",
application_version="0.1.0",
# Here, `is_telemetry_enabled` is a method to get user preferences specific to
# your application, and not part of the Glean API.
upload_enabled=is_telemetry_enabled(),
configuration=Configuration(),
)
Unlike in other implementations, the Python SDK does not automatically send any pings. See the custom pings documentation about adding custom pings and sending them.
The Glean Rust SDK should be initialized as soon as possible.
use glean::{ClientInfoMetrics, Configuration};
let cfg = Configuration {
data_path,
application_id: "my-app-id".into(),
// Here, `is_telemetry_enabled` is a method to get user preferences specific to
// your application, and not part of the Glean API.
upload_enabled: is_telemetry_enabled(),
max_events: None,
delay_ping_lifetime_io: false,
server_endpoint: Some("https://incoming.telemetry.mozilla.org".into()),
uploader: None,
use_core_mps: true,
};
let client_info = ClientInfoMetrics {
app_build: env!("CARGO_PKG_VERSION").to_string(),
app_display_version: env!("CARGO_PKG_VERSION").to_string(),
channel: None,
locale: None,
};
glean::initialize(cfg, client_info);
The Glean Rust SDK does not support use across multiple processes, and must only be initialized on the application's main process.
Unlike in other implementations, the Rust SDK does not provide a default uploader.
See PingUploader
for details.
import Glean from "@mozilla/glean/<platform>";
Glean.initialize(
"my-app-id",
// Here, `isTelemetryEnabled` is a method to get user preferences specific to
// your application, and not part of the Glean API.
isTelemetryEnabled(),
{
appDisplayVersion: "0.1.0"
}
);
Custom Uploaders
A custom HTTP uploader may be provided at initialization time in order to overwrite Glean's native ping uploader implementation. Each SDK exposes a base class for Glean users to extend into their own custom uploaders.
See BaseUploader
for details on how to implement a custom upload on Kotlin.
See HttpPingUploader
for details on how to implement a custom upload on Swift.
See BaseUploader
for details on how to implement a custom upload on Python.
See PingUploader
for details on how to implement a custom upload on Rust.
import { Uploader, UploadResult, UploadResultStatus } from "@mozilla/glean/uploader";
import Glean from "@mozilla/glean/<platform>";
/**
* My custom uploader implementation
*/
export class MyCustomUploader extends Uploader {
async post(url: string, body: string, headers): Promise<UploadResult> {
// My custom POST request code
}
}
Glean.initialize(
"my-app-id",
// Here, `isTelemetryEnabled` is a method to get user preferences specific to
// your application, and not part of the Glean API.
isTelemetryEnabled(),
{
httpClient: new MyCustomUploader()
}
);
Testing API
When unit testing metrics and pings, Glean needs to be put in testing mode. Initializing Glean for tests is referred to as "resetting". It is advised that Glean is reset before each unit test to prevent side effects of one unit test impacting others.
How to do that and the definition of "testing mode" varies per Glean SDK. Refer to the information below for SDK specific information.
Using the Glean Kotlin SDK's unit testing API requires adding
Robolectric 4.0 or later as a testing dependency.
In Gradle, this can be done by declaring a testImplementation
dependency:
dependencies {
testImplementation "org.robolectric:robolectric:4.3.1"
}
In order to put the Glean Kotlin SDK into testing mode apply the JUnit GleanTestRule
to your test class.
Testing mode will prevent issues with async calls when unit testing the Glean SDK on Kotlin. It also
enables uploading and clears the recorded metrics at the beginning of each test run.
The rule can be used as shown:
@RunWith(AndroidJUnit4::class)
class ActivityCollectingDataTest {
// Apply the GleanTestRule to set up a disposable Glean instance.
// Please note that this clears the Glean data across tests.
@get:Rule
val gleanRule = GleanTestRule(ApplicationProvider.getApplicationContext())
@Test
fun checkCollectedData() {
// The Glean Kotlin SDK testing APIs can be called here.
}
}
This will ensure that metrics are done recording when the other test functions are used.
Note: There's no automatic test rule for Glean tests implemented in Swift.
In order to prevent issues with async calls when unit testing the Glean SDK, it is important to put the Glean Swift SDK into testing mode. When the Glean Swift SDK is in testing mode, it enables uploading and clears the recorded metrics at the beginning of each test run.
Activate it by resetting Glean in your test's setup:
// All pings and metrics testing APIs are marked as `internal`
// so you need to import `Glean` explicitly in test mode.
import XCTest
class GleanUsageTests: XCTestCase {
override func setUp() {
Glean.shared.resetGlean(clearStores: true)
}
// ...
}
This will ensure that metrics are done recording when the other test functions are used.
The Glean Python SDK contains a helper function glean.testing.reset_glean()
for resetting Glean for tests.
It has two required arguments: the application ID, and the application version.
Each reset of the Glean Python SDK will create a new temporary directory for Glean to store its data in. This temporary directory is automatically cleaned up the next time the Glean Python SDK is reset or when the testing framework finishes.
The instructions below assume you are using pytest as the test runner. Other test-running libraries have similar features, but are different in the details.
Create a file conftest.py
at the root of your test directory, and add the following to reset Glean at the start of every test in your suite:
import pytest
from glean import testing
@pytest.fixture(name="reset_glean", scope="function", autouse=True)
def fixture_reset_glean():
testing.reset_glean(application_id="my-app-id", application_version="0.1.0")
Note
Glean uses a global singleton object. Tests need to run single-threaded or need to ensure exclusivity using a lock.
The Glean Rust SDK contains a helper function test_reset_glean()
for resetting Glean for tests.
It has three required arguments:
- the configuration to use
- the client info to use
- whether to clear stores before initialization
You can call it like below in every test:
#![allow(unused)] fn main() { let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf(); let glean::Configuration { data_path: tmpname, application_id: "app-id".into(), upload_enabled: true, max_events: None, delay_ping_lifetime_io: false, server_endpoint: Some("invalid-test-host".into()), uploader: None, use_core_mps: false, }; let client_info = glean::ClientInfoMetrics::unknown(); glean::test_reset_glean(cfg, client_info, false); }
The Glean JavaScript SDK contains a helper function testResetGlean()
for resetting Glean for tests.
It expects the same list of arguments as Glean.initialize
.
Each reset of the Glean JavaScript SDK will clear stores. Calling testResetGlean
will also make
metrics and pings testing APIs available and replace ping uploading with a mock implementation that
does not make real HTTP requests.
import { testResetGlean } from "@mozilla/glean/testing"
describe("myTestSuite", () => {
beforeEach(async () => {
await testResetGlean("my-test-id");
});
});