This document aims to give a high-level overview of the Glean JavaScript SDK (aka Glean.js) architecture.
This is a great place to start for newcomers to the project.
For in-depth documentation on specific implementation details of the library, refer to the Glean.js developer documentation.
The Glean JavaScript SDK is part of the Glean project. An end-to-end data collection platform developed by Mozilla and primarily targeting Mozilla products across multiple platforms.
Glean provides multiple client SDKs for different programming languages and platforms. One of the aspects that guide Glean SDK development is cross-platform consistency and the Glean JavaScript SDK is no exception to that. It is built to work on websites -- and to be easily extendable to other platforms as well.
The Glean JavaScript SDK is the latest addition to the family of Glean SDKs. The other Glean SDKs,
namely Kotlin, Swift, Python, Rust and Firefox Desktop SDKs are not housed in this repository, but
in the mozilla/glean
repository and for the case of the
Firefox Desktop SDK in the mozilla-central repository.
On a very high level a Glean SDK is a library which exposes APIs for users to record structured data and submit that data in an (again) structured format to a specific endpoint.
Users cannot simply record arbitrary data, though. The Glean SDKs expose specialized metrics APIs for different collection needs. The SDK is responsible for validating that the data given by a user is in the correct format and only then recording it (or recording an error in case the data provided is not correct).
When data is submitted, the Glean SDK is responsible for assembling the correct group (aka ping) of data points (aka metrics) in the format expected by the Glean pipeline, uploading the data and managing the local storage. Each metric can have different lifetimes and the SDK will manage its storage so that data does not remain in storage after it's lifetime is expired.
The Glean SDK tries to do all of this is the least disruptive way possible to users.
All of the SDKs tasks are queued and executed asynchronously. The APIs exposed by the Glean SDK will only do the en-queuing of tasks, a quick synchronous operation. Internally, the Glean SDK will handle the queued tasks asynchronously, catching any errors thrown along the way so that Glean never crashes a users application.
All of the SDKs tasks are executed synchronously, immediately as they are called. The APIs exposed by Glean are the same as the async implementation, but without queueing of any tasks. Errors will be caught without ever crashing the application.
The Glean JavaScript SDK source code lives under the glean/src
folder.
├── core/ # Contains the bulk of the library's implementation
├── entry/ # Contains the different general API entry points for each platform
├── platform/ # Contains platforms specific implementations for specific modules
├── plugins/ # Contains plugins implementations
├── cli.ts # Contains the Glean CLI implementation
├── metrics.yaml
└── pings.yaml
The core/
folder is where developers will probably spend most of their time
when working on Glean.js. This folder contains the majority of the library implementation.
All of the code under this folder is expected to work across all platforms. Platform specific code
is centralized on the platform/
folder which is covered in a later section of this document.
glean.ts
contains the Glean general API
implementation where Glean state is initialized and managed. Due to its centralizer aspect,
this file inevitably imports many of the other files on the library. As such,
developers should avoid importing this file in any of the other files on the core/
folder,
because that can quickly cause a circular dependency mess.
context.ts
is where circular dependencies go to die. When Glean.js was first implemented,
glean.ts
not only managed and initialized Glean state, but it also exposed Glean state to the rest
of the library. That caused a huge issue with circular dependencies. The context.ts
file was
created to mitigate that issue. This file houses the Context
structure, a singleton that holds
setters and getters to all of Glean's internal state. This file should avoid any imports that are
not import type
.
dispatcher.ts
is where Glean's dispatcher implementation lives. The dispatcher is the structure
that manages Glean's task queue.
metrics/types
is where all of the specific metric type implementations are. Each metric type
should be implemented in a single file and no other file types should be added to this folder,
because the files under this folder are exposed through the @mozilla/glean/private/metrics/*
entry point.
To see all the exposed entry points, check out Glean.js' package.json
file.
The entry/
folder contains the main entry points for the Glean.js package per platform.
For example, when a user does import Glean from @mozilla/glean/web
it's the entry/web.ts
file that they are getting and not core/glean.ts
.
The main difference between each platform's file is that a different Platform
implementation is
imported per file.
Some modules such as storage and uploader, cannot be written in such a way that works
for all platforms, because each platform provides different APIs for these tasks. In order
for useless platform specific code not to bloat the size of the library on each platform,
the platform/
module contains implementations of identical interfaces in different platforms.
This allows the pattern of only importing the necessary implementation of these modules on each platform. It also makes testing easier, because the exact same suite of tests can be run for each of the platform-specific implementations, thus guaranteeing that each module works exactly the same on all platforms.
The storage module varies for each platform. The storage mechanism used by each platform is as follows:
web
-localStorage
The plugins/
folder contains the Glean.js' plugins code.
Each plugin is expected to be implemented in a single file, because these files are
exposed through the @mozilla/glean/plugins/*
entry point.
This file is what gets executed when a user that has installed Glean through npm invokes the glean
command. It serves as a wrapper to call glean_parser
commands more easily from JavaScript projects.
There are certain places where we need different implementations for internal services based on the platform. In these instances, the service will have its own folder with the following folder structure:
├── service/
├──── async.ts
├──── shared.ts # Shared base class defining all available methods AND any reusable helper functions
└──── sync.ts