Commit 01c37f2e authored by Michael Droettboom's avatar Michael Droettboom Committed by Alessio Placitelli
Browse files

1529226: Allow custom pings to not send the client_id

parent 3206e550
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ apply plugin: 'kotlin-android'
 * created during unit testing.
 * This uses a specific version of the schema identified by a git commit hash.
 */
String GLEAN_PING_SCHEMA_GIT_HASH = "0248519"
String GLEAN_PING_SCHEMA_GIT_HASH = "64b852c"
String GLEAN_PING_SCHEMA_URL = "https://raw.githubusercontent.com/mozilla-services/mozilla-pipeline-schemas/$GLEAN_PING_SCHEMA_GIT_HASH/schemas/glean/baseline/baseline.1.schema.json"

android {
+27 −4
Original line number Diff line number Diff line
@@ -3,9 +3,29 @@
Applications can define metrics that are sent in custom pings. Unlike the
built-in pings, custom pings are sent explicitly by the application.

## Defining a custom ping

Custom pings must be defined in a `pings.yaml` file, which is in the same
directory alongside your app's `metrics.yaml` file.

Each ping has the following parameters:

- `description` (required): A human-readable description of the ping.
- `include_client_id` (required): A boolean indicating whether to include the
  `client_id` in the [`client_info` section](pings.md#The-client_info-section)).

For example, to define a custom ping specifically for search information:

```YAML
search:
  description: >
    A ping to record search data.
  include_client_id: false
```

## Sending metrics in a custom ping

To send a metric on a custom ping, you must first add the custom ping's name to
To send a metric on a custom ping, you add the custom ping's name to
the `send_in_pings` parameter in the `metrics.yaml` file.

For example, to define a new metric to record the default search engine, which
@@ -32,9 +52,12 @@ type, you can add the special value `default` to `send_in_pings`:

## Sending a custom ping

To send a custom ping, use `Glean.sendPings`. It is up to the application to
call this method at the schedule and cadence that is appropriate for the ping.
To send a custom ping, call the `send` method on the `PingType` object that
glean generated for your ping.

For example, to send the custom ping defined above:

```kotlin
Glean.sendPings(listOf("search"))
import org.mozilla.yourApplication.GleanMetrics.Pings
Pings.search.send()
```
+37 −0
Original line number Diff line number Diff line
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This file defines the built-in pings that are recorded by glean telemetry. They are
# automatically converted to Kotlin code at build time using the `glean_parser`
# PyPI package.

$schema: moz://mozilla.org/schemas/glean/pings/1-0-0

baseline:
  description: >
    This ping is intended to provide metrics that are managed by the library
    itself, and not explicitly set by the application or included in the
    application's `metrics.yaml` file.
    The `baseline` ping is automatically sent when the application is moved to
    the background.
  include_client_id: true

metrics:
  description: >
    The `metrics` ping is intended for all of the metrics that are explicitly
    set by the application or are included in the application's `metrics.yaml`
    file (except events).
    The reported data is tied to the ping's *measurement window*, which is the
    time between the collection of two `metrics` ping. Ideally, this window is
    expected to be about 24 hours, given that the collection is scheduled daily
    at 4AM. Data in the `ping_info` section of the ping can be used to infer the
    length of this window.
  include_client_id: true

events:
  description: >
    The events ping's purpose is to transport all of the event metric information.
    The `events` ping is automatically sent when the application is moved to
    the background.
  include_client_id: true
+2 −1
Original line number Diff line number Diff line
@@ -161,6 +161,7 @@ ext.gleanGenerateMetricsAPI = {
        args "-s"
        args "namespace=$fullNamespace"
        args "$projectDir/metrics.yaml"
        args "$projectDir/pings.yaml"

        // If we're building the Glean library itself (rather than an
        // application using Glean) pass the --allow-reserved flag so we can
@@ -183,7 +184,7 @@ ext.gleanGenerateMetricsAPI = {
    // Only attach the generation task if the metrics file is available. We don't need to
    // fail hard otherwise, as some 3rd party project might just want metrics included in
    // glean and nothing more.
    if (file("$projectDir/metrics.yaml").exists()) {
    if (file("$projectDir/metrics.yaml").exists() || file("$projectDir/pings.yaml").exists()) {
        // This is an Android-Gradle plugin 3+-ism.  Culted from reading the source,
        // searching for "registerJavaGeneratingTask", and finding
        // https://github.com/GoogleCloudPlatform/endpoints-framework-gradle-plugin/commit/2f2b91476fb1c6647791e2c6fe531a47615a1e85.
+40 −51
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ import java.util.UUID
import mozilla.components.service.glean.config.Configuration
import mozilla.components.service.glean.firstrun.FileFirstRunDetector
import mozilla.components.service.glean.GleanMetrics.GleanInternalMetrics
import mozilla.components.service.glean.GleanMetrics.Pings
import mozilla.components.service.glean.ping.PingMaker
import mozilla.components.service.glean.private.PingType
import mozilla.components.service.glean.scheduler.GleanLifecycleObserver
import mozilla.components.service.glean.scheduler.MetricsPingScheduler
import mozilla.components.service.glean.scheduler.PingUploadWorker
@@ -60,15 +62,6 @@ open class GleanInternalAPI internal constructor () {

    internal lateinit var pingStorageEngine: PingStorageEngine

    companion object {
        internal const val BASELINE_STORE_NAME = "baseline"
        internal val BUILTIN_PINGNAMES = listOf(
            BASELINE_STORE_NAME,
            MetricsPingScheduler.STORE_NAME,
            "events"
        )
    }

    /**
     * Initialize glean.
     *
@@ -336,15 +329,11 @@ open class GleanInternalAPI internal constructor () {
     */
    fun handleBackgroundEvent() {
        // Schedule the baseline and event pings
        sendPingsInternal(listOf(BASELINE_STORE_NAME, "events"))
        sendPings(listOf(Pings.baseline, Pings.events))
    }

    /**
     * Send a list of pings by name.
     *
     * Only custom pings (pings not managed by Glean itself) will be sent by
     * this function. If the name of a Glean-managed ping is passed in, an
     * error is logged to logcat.
     * Send a list of pings.
     *
     * While the collection of metrics into pings happens synchronously, the
     * ping queuing and ping uploading happens asyncronously.
@@ -352,36 +341,9 @@ open class GleanInternalAPI internal constructor () {
     *
     * If the ping currently contains no content, it will not be sent.
     *
     * @param pingNames List of pings to send.
     * @param pings List of pings to send.
     */
    fun sendPings(pingNames: List<String>) {
        val pingsToSend = pingNames.filter { pingName ->
            if (BUILTIN_PINGNAMES.contains(pingName)) {
                logger.error("Attempted to send built-in ping $pingName")
                false
            } else {
                true
            }
        }

        // Send pings is a "fire and forget" operation, we don't need to wait on
        // it and we don't care about its return value.
        sendPingsInternal(pingsToSend)
    }

    /**
     * Send a list of pings by name.
     *
     * While the collection of metrics into pings happens synchronously, the
     * ping queuing and ping uploading happens asyncronously.
     * There are no guarantees that this will happen immediately.
     *
     *
     * If the ping currently contains no content, it will not be sent.
     *
     * @param pingNames List of pings to send.
     */
    internal fun sendPingsInternal(pingNames: List<String>) = Dispatchers.API.launch {
    internal fun sendPings(pings: List<PingType>) = Dispatchers.API.launch {
        if (!isInitialized()) {
            logger.error("Glean must be initialized before sending pings.")
            return@launch
@@ -393,10 +355,10 @@ open class GleanInternalAPI internal constructor () {
        }

        val pingSerializationTasks = mutableListOf<Job>()
        for (pingName in pingNames) {
            assembleAndSerializePing(pingName)?.let {
        for (ping in pings) {
            assembleAndSerializePing(ping)?.let {
                pingSerializationTasks.add(it)
            } ?: logger.debug("No content for ping '$pingName', therefore no ping queued.")
            } ?: logger.debug("No content for ping '$ping.name', therefore no ping queued.")
        }

        // If any ping is being serialized to disk, wait for the to finish before spinning up
@@ -409,20 +371,47 @@ open class GleanInternalAPI internal constructor () {
        }
    }

    /**
     * Send a list of pings by name.
     *
     * Each ping will be looked up in the known instances of [PingType]. If the
     * ping isn't known, an error is logged and the ping isn't queued for uploading.
     *
     * While the collection of metrics into pings happens synchronously, the
     * ping queuing and ping uploading happens asyncronously.
     * There are no guarantees that this will happen immediately.
     *
     * If the ping currently contains no content, it will not be sent.
     *
     * @param pingNames List of ping names to send.
     * @return true if any pings had content and were queued for uploading
     */
    internal fun sendPingsByName(pingNames: List<String>): Job? {
        val pings = pingNames.mapNotNull { pingName ->
            PingType.pingRegistry.get(pingName)?.let {
                it
            } ?: run {
                logger.error("Attempted to send unknown ping '$pingName'")
                null
            }
        }
        return sendPings(pings)
    }

    /**
     * Collect and assemble the ping and serialize the ping to be read when uploaded, but only if
     * glean is initialized, upload is enabled, and there is ping data to send.
     *
     * @param pingName This is the ping store/name for which to build and schedule the ping
     * @param ping This is the object describing the ping
     */
    internal fun assembleAndSerializePing(pingName: String): Job? {
    internal fun assembleAndSerializePing(ping: PingType): Job? {
        // Since the pingMaker.collect() function returns null if there is nothing to send we can
        // use this to avoid sending an empty ping
        return pingMaker.collect(pingName)?.let { pingContent ->
        return pingMaker.collect(ping)?.let { pingContent ->
            // Store the serialized ping to file for PingUploadWorker to read and upload when the
            // schedule is triggered
            val pingId = UUID.randomUUID()
            pingStorageEngine.store(pingId, makePath(pingName, pingId), pingContent)
            pingStorageEngine.store(pingId, makePath(ping.name, pingId), pingContent)
        }
    }

Loading