diff --git a/.buildconfig.yml b/.buildconfig.yml index 56ff8a8388..201199aed9 100644 --- a/.buildconfig.yml +++ b/.buildconfig.yml @@ -1,4 +1,4 @@ -libraryVersion: 60.3.0 +libraryVersion: 60.4.0 groupId: org.mozilla.telemetry projects: glean: diff --git a/.circleci/config.yml b/.circleci/config.yml index ee9b503fa7..f3db1ffc30 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -62,11 +62,6 @@ commands: - skip-if-doc-only - setup-rust-toolchain: rust-version: <> - - run: - name: Test - command: | - export GLEAN_TEST_COVERAGE=$(realpath glean_coverage.txt) - cargo test --verbose --jobs 6 -- --nocapture - run: name: Install required Python dependencies command: | @@ -74,6 +69,11 @@ commands: sudo apt install --yes --no-install-recommends \ python3-pip \ python3.10-venv + - run: + name: Test + command: | + export GLEAN_TEST_COVERAGE=$(realpath glean_coverage.txt) + cargo test --workspace --verbose --jobs 6 -- --nocapture - run: name: Run Rust sample command: | @@ -86,6 +86,10 @@ commands: name: Run Rust RLB crash test command: | glean-core/rlb/tests/test-thread-crashing.sh + - run: + name: Run Rust RLB delayed ping data test + command: | + glean-core/rlb/tests/test-delayed-ping-data.sh - run: name: Upload coverage report command: | diff --git a/.dictionary b/.dictionary index 1f070bd431..baafec85af 100644 --- a/.dictionary +++ b/.dictionary @@ -1,4 +1,4 @@ -personal_ws-1.1 en 282 utf-8 +personal_ws-1.1 en 289 utf-8 AAR AARs ABI @@ -108,6 +108,7 @@ XCFramework Xcode YAML aarch +addon alphanumerics analytics anonymized @@ -115,6 +116,7 @@ aspell async autodetected backend +backgrounding backoff backport barcode @@ -127,6 +129,7 @@ booleans brizental bugfixes camelCase +cancelled carthage changelog chutten @@ -147,6 +150,7 @@ datetime destructor deterministically dev +devtools dexter distributable docstrings @@ -175,6 +179,7 @@ html iMacs illumos init +initializer inlined instrumentations integrations @@ -223,6 +228,7 @@ preinit py pytest rethrow +retransmission rfloor rkv rkv's @@ -270,6 +276,7 @@ urlbar validator vendored vendoring +virtualenv walkthrough walkthroughs webextension diff --git a/CHANGELOG.md b/CHANGELOG.md index 0909bd426c..6145aeffb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,24 @@ # Unreleased changes -[Full changelog](https://github.com/mozilla/glean/compare/v60.3.0...main) +[Full changelog](https://github.com/mozilla/glean/compare/v60.4.0...main) + +# v60.4.0 (2024-07-23) + +[Full changelog](https://github.com/mozilla/glean/compare/v60.3.0...v60.4.0) + +* General + * Bump the string length limit to 255 characters ([#2857](https://github.com/mozilla/glean/pull/2857)) + * New metric `glean.database.write_time` to measure database writes ([#2845](https://github.com/mozilla/glean/pull/2845)) + * Require glean_parser v14.3.0 ([bug 1909244](https://bugzilla.mozilla.org/show_bug.cgi?id=1909244)) +* Android + * Delay log init until Glean is getting initialized ([#2858](https://github.com/mozilla/glean/pull/2858)) + * Update to Gradle v8.8 ([#2860](https://github.com/mozilla/glean/pull/2860)) + * Updated Kotlin to version 1.9.24 ([#2861](https://github.com/mozilla/glean/pull/2861)) + * Default-enable `delayPingLifetimeIo` ([#2863](https://github.com/mozilla/glean/issues/2863)) + * Preparing Glean to be able to remove `service-glean` from Android Components ([#2891](https://github.com/mozilla/glean/pull/2891)) + * Gradle Plugin: Support for using an external Python environment ([#2889](https://github.com/mozilla/glean/pull/2889)) +* Rust + * New Metric Types: `labeled_custom_distribution`, `labeled_memory_distribution`, and `labeled_timing_distribution` ([bug 1657947](https://bugzilla.mozilla.org/show_bug.cgi?id=1657947)) # v60.3.0 (2024-05-31) @@ -35,6 +53,15 @@ * Python * Replace use of deprecated functionality (and make installs work on Python 3.12) ([#2820](https://github.com/mozilla/glean/pull/2820)) +# v60.0.1 (2024-05-31) + +[Full changelog](https://github.com/mozilla/glean/compare/v60.0.0...v60.0.1) + +* Android + * Allow configuring `delayPingLifetimeIo` in Kotlin and auto-flush this data after 1000 writes. + It is also auto-flushed on background. ([#2851](https://github.com/mozilla/glean/pull/2851)) + (Backported changes) + # v60.0.0 (2024-04-22) [Full changelog](https://github.com/mozilla/glean/compare/v59.0.0...v60.0.0) diff --git a/Cargo.lock b/Cargo.lock index fe2c3c3137..ec9b6f2a06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,7 +318,7 @@ dependencies = [ [[package]] name = "glean" -version = "60.3.0" +version = "60.4.0" dependencies = [ "crossbeam-channel", "env_logger", @@ -336,7 +336,7 @@ dependencies = [ [[package]] name = "glean-build" -version = "14.0.1" +version = "14.3.0" dependencies = [ "tempfile", "xshell-venv", @@ -359,7 +359,7 @@ dependencies = [ [[package]] name = "glean-core" -version = "60.3.0" +version = "60.4.0" dependencies = [ "android_logger", "bincode", diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index d34ac131db..e35ea224d8 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -4951,9 +4951,9 @@ SOFTWARE. The following text applies to code linked from these dependencies: -* [glean-core 60.3.0]( https://github.com/mozilla/glean ) -* [glean-build 14.0.1]( https://github.com/mozilla/glean ) -* [glean 60.3.0]( https://github.com/mozilla/glean ) +* [glean-core 60.4.0]( https://github.com/mozilla/glean ) +* [glean-build 14.3.0]( https://github.com/mozilla/glean ) +* [glean 60.4.0]( https://github.com/mozilla/glean ) * [zeitstempel 0.1.1]( https://github.com/badboy/zeitstempel ) ``` diff --git a/Makefile b/Makefile index 7fbf2cb4d7..c135e355e6 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ docs-python: build-python ## Build the Python documentation .PHONY: docs docs-rust docs-swift docs-metrics: setup-python ## Build the internal metrics documentation - $(GLEAN_PYENV)/bin/pip install glean_parser~=14.0 + $(GLEAN_PYENV)/bin/pip install glean_parser~=14.3 $(GLEAN_PYENV)/bin/glean_parser translate --allow-reserved \ -f markdown \ -o ./docs/user/user/collected-metrics \ diff --git a/docs/dev/SUMMARY.md b/docs/dev/SUMMARY.md index 873b6bfbb4..23b729d0ea 100644 --- a/docs/dev/SUMMARY.md +++ b/docs/dev/SUMMARY.md @@ -10,6 +10,7 @@ - [Android bindings](android/index.md) - [Setup Build Environment](android/setup-android-build-environment.md) - [Android SDK/NDK versions](android/sdk-ndk-versions.md) + - [Logging](android/logging.md) - [Development with android-components](android/development-with-android-components.md) - [Locally-published components in Fenix](android/locally-published-components-in-fenix.md) - [Substituting glean_parser](android/glean-parser-substitution.md) diff --git a/docs/dev/android/logging.md b/docs/dev/android/logging.md new file mode 100644 index 0000000000..075b1822f8 --- /dev/null +++ b/docs/dev/android/logging.md @@ -0,0 +1,12 @@ +# Logging + +Logs from `glean-core` are only passed through to the Android logging framework when `Glean.initialize` is called for the first time. +This means any logging that might happen before that, e.g. from early metric collection will not be collected. + +If these logs are needed for debugging add the following initializer to `Glean.kt`: + +```kotlin +init { + gleanEnableLogging() +} +``` diff --git a/docs/user/SUMMARY.md b/docs/user/SUMMARY.md index baa9e932ae..101433b907 100644 --- a/docs/user/SUMMARY.md +++ b/docs/user/SUMMARY.md @@ -70,12 +70,15 @@ - [String List](reference/metrics/string_list.md) - [Timespan](reference/metrics/timespan.md) - [Timing Distribution](reference/metrics/timing_distribution.md) + - [Labeled Timing Distributions](reference/metrics/labeled_timing_distributions.md) - [Memory Distribution](reference/metrics/memory_distribution.md) + - [Labeled Memory Distributions](reference/metrics/labeled_memory_distributions.md) - [UUID](reference/metrics/uuid.md) - [URL](reference/metrics/url.md) - [Datetime](reference/metrics/datetime.md) - [Event](reference/metrics/event.md) - [Custom Distribution](reference/metrics/custom_distribution.md) + - [Labeled Custom Distributions](reference/metrics/labeled_custom_distributions.md) - [Quantity](reference/metrics/quantity.md) - [Rate](reference/metrics/rate.md) - [Text](reference/metrics/text.md) diff --git a/docs/user/appendix/twig.md b/docs/user/appendix/twig.md index 5321fefabe..e57406b581 100644 --- a/docs/user/appendix/twig.md +++ b/docs/user/appendix/twig.md @@ -67,3 +67,4 @@ They could be release notes, documentation, hopes, dreams, or whatever: so long * 2022-02-25: [Your personal Glean data pipeline](https://blog.mozilla.org/data/2022/02/25/this-week-in-glean-your-personal-glean-data-pipeline/) * 2022-10-27: [Page Load Data, Three Ways (Or, How Expensive Are Events?)](https://blog.mozilla.org/data/2022/10/27/this-week-in-glean-page-load-data-three-ways-or-how-expensive-are-events/) * 2023-05-25: [Reading “The Manager’s Path” by Camille Fournier](https://blog.mozilla.org/data/2023/05/25/this-week-in-data-reading-the-managers-path-by-camille-fournier/) +* 2024-06-27: [Cosmic Rays From Outer Space! (What Comes Next?)](https://blog.mozilla.org/data/2024/06/27/this-week-in-data-cosmic-rays-from-outer-space-what-comes-next/) diff --git a/docs/user/language-bindings/android/android-build-configuration-options.md b/docs/user/language-bindings/android/android-build-configuration-options.md index 0adb3fb23f..c8ac287b5f 100644 --- a/docs/user/language-bindings/android/android-build-configuration-options.md +++ b/docs/user/language-bindings/android/android-build-configuration-options.md @@ -80,3 +80,13 @@ ext.gleanExpireByVersion = 25 Different products have different ways to compute the product version at build-time. For this reason the Glean Gradle plugin cannot provide an automated way to detect the product major version at build time. When using the expiration by version feature in Android, products must provide the major version by themselves. + +## `gleanPythonEnvDir` + +By default, the Glean Gradle plugin will manage its own Python virtualenv in `$gradleUserHomeDir/glean` to install `glean_parser`. By specifying a path in `ext.gleanPythonEnvDir` you can reuse an existing Python virtualenv. + +```groovy +ext.gleanPythonEnvDir = "$buildDir/externallyManagedVenv" +``` + +`glean_parser` must be available in that virtualenv, the Gradle plugin will make no attempt at installing it. diff --git a/docs/user/language-bindings/android/android-offline-builds.md b/docs/user/language-bindings/android/android-offline-builds.md index 865b2b596b..95134fdc70 100644 --- a/docs/user/language-bindings/android/android-offline-builds.md +++ b/docs/user/language-bindings/android/android-offline-builds.md @@ -6,7 +6,15 @@ The Glean Kotlin SDK uses a Python script, [`glean_parser`](https://github.com/m For offline builds, the Python environment, and packages of `glean_parser` and its dependencies must be provided prior to building the Glean-using application. -To build a Glean-using application in offline mode, do the following: +To build a Glean-using application in offline mode, you can either: + +### Provide an externally-managed virtualenv + +Set `ext.gleanPythonEnvDir` to your existing virtualenv before applying the plugin, see [`gleanPythonEnvDir`](./android-build-configuration-options.md#gleanpythonenvdir). + +### Provide a Python interpreter and the required wheels + +In this mode Glean will setup its own virtualenv in `$gradleUserHomeDir/glean`, controlled by the `GLEAN_PYTHON` and `GLEAN_PYTHON_WHEELS_DIR` environment variables. - Install Python 3.8 or later and ensure it's on the `PATH`. diff --git a/docs/user/reference/general/initializing.md b/docs/user/reference/general/initializing.md index 2ca34af38c..d0aa3306d2 100644 --- a/docs/user/reference/general/initializing.md +++ b/docs/user/reference/general/initializing.md @@ -56,22 +56,25 @@ May only be called once. Subsequent calls to `initialize` are no-op. The available `initialize` configuration options may vary depending on the SDK. Below are listed the configuration options available on most SDKs. - -- `applicationId`: 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`: The user preference on whether or not data upload is enabled. -- `appChannel`: The application's release channel. When present, the `app_channel` will be reported in all ping's [`client_info`](../../user/pings/index.html#the-client_info-section) section. -- `appBuild`: 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`](../../user/pings/index.html#the-client_info-section) section. -- `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`](../../user/pings/index.html#the-client_info-section) section. -- `serverEndpoint`: The server pings are sent to. Defaults to `https://incoming.telemetry.mozilla.org`. -- `maxEvents`: The maximum number of events the Glean storage will hold on to before submitting the 'events' ping. Defaults to 1 for Glean.js, 500 for all other SDKs. Refer to the [`events` ping documentation](../../user/pings/events.md) 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](#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](https://docs.rs/log/latest/log/) for more information. -- `rateLimit`: Optional. - Specifies the maximum number of pings that can be uploaded per interval of a specified number of seconds. - Default is to use the SDK default (presently 15 pings per 60s interval). -- `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. +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`](../../user/pings/index.html#the-client_info-section) 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`](../../user/pings/index.html#the-client_info-section) 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`](../../user/pings/index.html#the-client_info-section) 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](../../user/pings/events.md) 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](#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](https://docs.rs/log/latest/log/) 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](#reference) section. @@ -112,6 +115,7 @@ class SampleApplication : Application() { // 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 ) } @@ -179,6 +183,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // 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 ) } @@ -202,7 +207,7 @@ The main control for the Glean Python SDK is on the `glean.Glean` singleton. ```python -from glean import Glean +from glean import Glean, Configuration Glean.initialize( application_id="my-app-id", @@ -210,6 +215,7 @@ Glean.initialize( # 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(), ) ``` @@ -505,4 +511,4 @@ describe("myTestSuite", () => { - [Swift API docs](../../../swift/Classes/Glean.html#/s:5GleanAAC10initialize13uploadEnabled13configuration9buildInfoySb_AA13ConfigurationVAA05BuildG0VtF) - [Python API docs](../../../python/glean/index.html#glean.Glean.initialize) - [Rust API docs](../../../docs/glean/fn.initialize.html) -- [JavaScript API docs](https://mozilla.github.io/glean.js/functions/core_glean.default.initialize.html) +- [JavaScript API docs](https://mozilla.github.io/glean.js/getting_started/setup/#initializing-gleanjs) diff --git a/docs/user/reference/general/shutdown.md b/docs/user/reference/general/shutdown.md index f431c81318..528b971e30 100644 --- a/docs/user/reference/general/shutdown.md +++ b/docs/user/reference/general/shutdown.md @@ -74,4 +74,3 @@ is explicitly using the `webext` target. ## Reference * [Rust API docs](../../../docs/glean/fn.shutdown.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_glean.default.html#shutdown) diff --git a/docs/user/reference/general/toggling-upload-status.md b/docs/user/reference/general/toggling-upload-status.md index ccd852bbfc..f949a462a0 100644 --- a/docs/user/reference/general/toggling-upload-status.md +++ b/docs/user/reference/general/toggling-upload-status.md @@ -131,4 +131,4 @@ uploadSwitch.addEventListener("change", event => { * [Swift API docs](../../../swift/Classes/Glean.html#/s:5GleanAAC16setUploadEnabledyySbF) * [Python API docs](../../../python/glean/index.html#glean.Glean.set_upload_enabled) * [Rust API docs](../../../docs/glean/fn.set_upload_enabled.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_glean.default.html#setUploadEnabled) +* [JavaScript API docs](https://mozilla.github.io/glean.js/reference/uploaders/#uploadenabled) diff --git a/docs/user/reference/metrics/boolean.md b/docs/user/reference/metrics/boolean.md index 25ed6f7c7d..cd1d1bb165 100644 --- a/docs/user/reference/metrics/boolean.md +++ b/docs/user/reference/metrics/boolean.md @@ -289,4 +289,3 @@ N/A * [Swift API docs](../../../swift/Classes/BooleanMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.BooleanMetric) * [Rust API docs](../../../docs/glean/private/boolean/struct.BooleanMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_boolean.default.html) diff --git a/docs/user/reference/metrics/counter.md b/docs/user/reference/metrics/counter.md index d343b5395d..abfa3d9553 100644 --- a/docs/user/reference/metrics/counter.md +++ b/docs/user/reference/metrics/counter.md @@ -336,4 +336,3 @@ N/A * [Swift API docs](../../../swift/Classes/CounterMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.CounterMetric) * [Rust API docs](../../../docs/glean/private/counter/struct.CounterMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_counter.default.html) diff --git a/docs/user/reference/metrics/custom_distribution.md b/docs/user/reference/metrics/custom_distribution.md index 10ee501e06..fd1ad29cae 100644 --- a/docs/user/reference/metrics/custom_distribution.md +++ b/docs/user/reference/metrics/custom_distribution.md @@ -145,7 +145,7 @@ This API is not currently exposed in Firefox Desktop, see [Bug 1884183](https:// ### `testGetValue` -Gets the recorded value for a given counter metric. +Gets the recorded value for a given custom distribution metric. Returns a struct with counts per buckets and total sum if data is stored. Returns a language-specific empty/null value if no data is stored. Has an optional argument to specify the name of the ping you wish to retrieve data from, except diff --git a/docs/user/reference/metrics/datetime.md b/docs/user/reference/metrics/datetime.md index df103a0729..f6d88458a9 100644 --- a/docs/user/reference/metrics/datetime.md +++ b/docs/user/reference/metrics/datetime.md @@ -417,4 +417,3 @@ Carefully consider the required resolution for recording your metric, and choose * [Swift API docs](../../../swift/Classes/DatetimeMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.DatetimeMetricType) * [Rust API docs](../../../docs/glean/private/struct.DatetimeMetric.html) -* [Datetime API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_datetime.default.html) diff --git a/docs/user/reference/metrics/event.md b/docs/user/reference/metrics/event.md index 06d61ec531..9f4bcd3fbe 100644 --- a/docs/user/reference/metrics/event.md +++ b/docs/user/reference/metrics/event.md @@ -403,4 +403,3 @@ Each extra key contains additional metadata: * [Swift API docs](../../../swift/Classes/EventMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.EventMetricType) * [Rust API docs](../../../docs/glean/private/event/struct.EventMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_event.default.html) diff --git a/docs/user/reference/metrics/labeled_booleans.md b/docs/user/reference/metrics/labeled_booleans.md index fbb0820bc6..acb56b2ee0 100644 --- a/docs/user/reference/metrics/labeled_booleans.md +++ b/docs/user/reference/metrics/labeled_booleans.md @@ -318,4 +318,3 @@ accessibility: * Swift API docs: [`LabeledMetricType`](../../../swift/Classes/LabeledMetricType.html), [`BooleanMetricType`](../../../swift/Classes/BooleanMetricType.html) * Python API docs: [`LabeledBooleanMetricType`](../../../python/glean/metrics/labeled.html#glean.metrics.labeled.LabeledBooleanMetricType), [`BooleanMetricType`](../../../python/glean/metrics/index.html#glean.metrics.BooleanMetric) * Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`BooleanMetricType`](../../../docs/glean/private/struct.BooleanMetric.html) -* JavaScript API docs: [`LabeledMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_labeled.default.html), [`BooleanMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_boolean.default.html) diff --git a/docs/user/reference/metrics/labeled_counters.md b/docs/user/reference/metrics/labeled_counters.md index f89941f09f..85d69ad228 100644 --- a/docs/user/reference/metrics/labeled_counters.md +++ b/docs/user/reference/metrics/labeled_counters.md @@ -334,4 +334,3 @@ accessibility: * Swift API docs: [`LabeledMetricType`](../../../swift/Classes/LabeledMetricType.html), [`CounterMetricType`](../../../swift/Classes/CounterMetricType.html) * Python API docs: [`LabeledCounterMetricType`](../../../python/glean/metrics/labeled.html#glean.metrics.labeled.LabeledCounterMetricType), [`CounterMetricType`](../../../python/glean/metrics/index.html#glean.metrics.CounterMetric) * Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`CounterMetricType`](../../../docs/glean/private/struct.CounterMetric.html) -* JavaScript API docs: [`LabeledMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_labeled.default.html), [`CounterMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_counter.default.html) diff --git a/docs/user/reference/metrics/labeled_custom_distributions.md b/docs/user/reference/metrics/labeled_custom_distributions.md new file mode 100644 index 0000000000..a97e5a324d --- /dev/null +++ b/docs/user/reference/metrics/labeled_custom_distributions.md @@ -0,0 +1,206 @@ +# Labeled Custom Distributions + +Labeled custom distributions are used to record different related distributions of arbitrary values. + +If your data is timing or memory based and you don't need direct control over histogram buckets, +consider instead: + +* [Labeled Timing Distributions](labeled_timing_distributions.md) +* [Labeled Memory Distributions](labeled_memory_distributions.md) + +## Recording API + +### `accumulateSamples` + +Accumulate the provided samples in the metric. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::network; + +network::http3_late_ack_ratio + .get("ack") + .accumulateSamples(vec![(stats.late_ack * 10000) / stats.packets_tx]); +network::http3_late_ack_ratio + .get("pto") + .accumulateSamples(vec![(stats.pto_ack * 10000) / stats.packets_tx]); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded Errors + +* [`invalid_value`](../../user/metrics/error-reporting.md): if recording any negative samples +{{#include ../../_includes/label-errors.md}} + +### `accumulateSingleSample` + +Accumulates one sample and appends it to the metric. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::network; + +network::http3_late_ack_ratio + .get("ack") + .accumulateSingleSample((stats.late_ack * 10000) / stats.packets_tx); +network::http3_late_ack_ratio + .get("pto") + .accumulateSingleSample((stats.pto_ack * 10000) / stats.packets_tx); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded Errors + +* [`invalid_value`](../../user/metrics/error-reporting.md): if recording a negative sample +{{#include ../../_includes/label-errors.md}} + +## Testing API + +### `testGetValue` + +Gets the recorded value for a given label in a labeled custom distribution metric. +Returns a struct with counts per buckets and total sum if data is stored. +Returns a language-specific empty/null value if no data is stored. +Has an optional argument to specify the name of the ping you wish to retrieve data from, except +in Rust where it's required. `None` or no argument will default to the first value found for `send_in_pings`. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::network; + +// Assert the sum of all samples is 42. +assert_eq!(42, network::http3_late_ack_ratio.get("ack").test_get_value(None).unwrap().sum); + +// Assert there's only the one sample +assert_eq!(1, network::http3_late_ack_ratio.get("ack").test_get_value(None).unwrap().count); + +// Buckets are indexed by their lower bound. +assert_eq!(1, etwork::http3_late_ack_ratio.get("ack").test_get_value(None).unwrap().values[41]); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +### `testGetNumRecordedErrors` + +Gets the number of errors recorded for a given labeled custom distribution metric in total. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean::ErrorType; +use glean_metrics::network; + +// Assert there were no negative values instrumented. +assert_eq!( + 0, + network::http3_late_ack_ratio.test_get_num_recorded_errors( + ErrorType::InvalidValue, + None + ) +); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +## Metric parameters + +Example labeled custom distribution metric definition: + +```YAML +network: + http3_late_ack_ratio: + type: labeled_custom_distribution + description: > + HTTP3: The ratio of spurious retransmissions per packets sent, + represented as an integer permdecimille: + `(spurious_retransmission / packet sent * 10000)` + range_min: 1 + range_max: 2000 + bucket_count 100 + histogram_type: exponential + bugs: + - https://bugzilla.mozilla.org/000000 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=000000#c3 + notification_emails: + - me@mozilla.com + expires: 175 + labels: + - ack + - pto +``` + +### Extra metric parameters + +#### `range_min`, `range_max`, `bucket_count`, and `histogram_type` (Required) + +Labeled custom distributions have the following required parameters: + +- `range_min`: (Integer) The minimum value of the first bucket +- `range_max`: (Integer) The minimum value of the last bucket +- `bucket_count`: (Integer) The number of buckets +- `histogram_type`: + - `linear`: The buckets are evenly spaced + - `exponential`: The buckets follow a natural logarithmic distribution + +{{#include ../../_includes/labels-parameter.md}} + +## Data questions + +* What is the distribution of retransmission ratios per connection? +* What is the distribution of how long it takes to load extensions' content scripts, by addon id? + +## Limits + +* The maximum value of `bucket_count` is 100. +* Only non-negative integer values may be recorded (`>=0`). +{{#include ../../_includes/label-limits.md}} + +## Reference + +* Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`CustomDistributionMetricType`](../../../docs/glean/private/struct.CustomDistributionMetric.html) diff --git a/docs/user/reference/metrics/labeled_memory_distributions.md b/docs/user/reference/metrics/labeled_memory_distributions.md new file mode 100644 index 0000000000..da291b6f37 --- /dev/null +++ b/docs/user/reference/metrics/labeled_memory_distributions.md @@ -0,0 +1,165 @@ +# Labeled Memory Distributions + +Labeled memory distributions are used to record different related distributions of memory sizes. + +See [the Memory Distribution reference](memory_distribution.md) for details on bucket distribution, +and a histogram simulator. + +## Recording API + +### `accumulate` + +Accumulate the provided sample in the metric. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::network; + +network::http_upload_bandwidth + .get(http_version) + .accumulate(self.request_size * 8.0 / 1048576.0 / send_time.as_secs()); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded Errors + +* [`invalid_value`](../../user/metrics/error-reporting.md): if recording a memory size that is negative or over 1 TB. +{{#include ../../_includes/label-errors.md}} + +## Testing API + +### `testGetValue` + +Gets the recorded value for a given label in a labeled memory distribution metric. +Returns a struct with counts per buckets and total sum if data is stored. +Returns a language-specific empty/null value if no data is stored. +Has an optional argument to specify the name of the ping you wish to retrieve data from, except +in Rust where it's required. `None` or no argument will default to the first value found for `send_in_pings`. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::network; + +// Assert the sum of all HTTP2 samples is 42MBps. +assert_eq!(42, network::http_upload_bandwidth.get("h2").test_get_value(None).unwrap().sum); + +// Assert there's only the one sample +assert_eq!(1, network::http_upload_badwidth.get("h2").test_get_value(None).unwrap().count); + +// Buckets are indexed by their lower bound. +assert_eq!(1, network::http_upload_bandwidth.get("h2").test_get_value(None).unwrap().values[41]); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +### `testGetNumRecordedErrors` + +Gets the number of errors recorded for a given labeled custom distribution metric in total. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean::ErrorType; +use glean_metrics::network; + +// Assert there were no negative or overlarge values instrumented. +assert_eq!( + 0, + network::http_upload_bandwidth.test_get_num_recorded_errors( + ErrorType::InvalidValue, + None + ) +); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +## Metric parameters + +Example labeled memory distribution metric definition: + +```YAML +network: + http_upload_bandwidth: + type: labeled_memory_distribution + description: > + The upload bandwidth for requests larger than 10MB, + per HTTP protocol version. + memory_unit: megabyte + bugs: + - https://bugzilla.mozilla.org/000000 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=000000#c3 + notification_emails: + - me@mozilla.com + expires: 175 + labels: + - h3 + - h2 + - http/1.0 + - http/1.1 +``` + +### Extra metric parameters + +#### `memory_unit` + +Memory distributions have an optional `memory_unit` parameter, +which specifies the unit the incoming memory size values are recorded in. + +The allowed values for `memory_unit` are: + +* `byte` (default) +* `kilobyte` (`= 2^10 = 1,024 bytes`) +* `megabyte` (`= 2^20 = 1,048,576 bytes`) +* `gigabyte` (`= 2^30 = 1,073,741,824 bytes`) + +{{#include ../../_includes/labels-parameter.md}} + +## Data questions + +* What is the distribution of upload bandwidth rates per HTTP protocol version? +* What is the distribution of bytes received per DOM network API? + +## Limits + +* The maximum memory size that can be recorded is 1 Terabyte (240 bytes). + Larger sizes will be truncated to 1 Terabyte. +{{#include ../../_includes/label-limits.md}} + +## Reference + +* Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`MemoryDistributionMetricType`](../../../docs/glean/private/struct.MemoryDistributionMetric.html) diff --git a/docs/user/reference/metrics/labeled_strings.md b/docs/user/reference/metrics/labeled_strings.md index 15b94ca267..850b4f7b5e 100644 --- a/docs/user/reference/metrics/labeled_strings.md +++ b/docs/user/reference/metrics/labeled_strings.md @@ -307,4 +307,3 @@ login: * Swift API docs: [`LabeledMetricType`](../../../swift/Classes/LabeledMetricType.html), [`StringMetricType`](../../../swift/Classes/StringMetricType.html) * Python API docs: [`LabeledStringMetricType`](../../../python/glean/metrics/labeled.html#glean.metrics.labeled.LabeledStringMetricType), [`StringMetricType`](../../../python/glean/metrics/index.html#glean.metrics.StringMetricType) * Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`StringMetricType`](../../../docs/glean/private/struct.StringMetric.html) -* JavaScript API docs: [`LabeledMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_labeled.default.html), [`StringMetricType`](https://mozilla.github.io/glean.js/classes/core_metrics_types_string.default.html) diff --git a/docs/user/reference/metrics/labeled_timing_distributions.md b/docs/user/reference/metrics/labeled_timing_distributions.md new file mode 100644 index 0000000000..9dcafa546a --- /dev/null +++ b/docs/user/reference/metrics/labeled_timing_distributions.md @@ -0,0 +1,331 @@ +# Labeled Timing Distributions + +Labeled timing distributions are used to record different related distributions of time measurements. + +See [the Timing Distribution reference](timing_distribution.md) for details on bucket distribution, +specifics about how Glean records time, and a histogram simulator. + +## Recording API + +### `start` + +Start tracking time for the provided metric for the given label. +Multiple timers for multiple labels can run simultaneously. + +Returns a unique `TimerId` for the new timer. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +self.start = devtools::cold_toolbox_open_delay + .get(toolbox_id) + .start(); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded Errors + +{{#include ../../_includes/label-errors.md}} + +### `stopAndAccumulate` + +Stops tracking time for the provided timer from the metric for the given label. + +Adds a count to the corresponding bucket in the label's timing distribution. + +Do not use the provided `TimerId` after passing it to this method. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +devtools::cold_toolbox_open_delay + .get(toolbox_id) + .stop_and_accumulate(self.start); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded errors + +* [`invalid_state`](../../user/metrics/error-reporting.md): If a non-existing, cancelled, or already-stopped timer is stopped again. +{{#include ../../_includes/label-errors.md}} + +### `cancel` + +Aborts a previous `start` call, consuming the supplied timer id. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +devtools::cold_toolbox_open_delay + .get(toolbox_id) + .cancel(self.start); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded errors + +{{#include ../../_includes/label-errors.md}} + +### `accumulateSamples` + +Accumulates the provided, signed samples in the metric for a given label. +Where possible, have Glean do the timing for you and don't use methods like this one. +If you are doing timing yourself, +ensure your time source is monotonic and behaves consistently across platforms. + +This is required so that the platform-specific code can provide us with +64 bit signed integers if no `u64` comparable type is available. This +will take care of filtering and reporting errors for any provided negative +sample. + +Please note that this assumes that the provided samples are already in +the "unit" declared by the instance of the metric type (e.g. if the +instance this method was called on is using `TimeUnit::Second`, then +`samples` are assumed to be in that unit). + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +devtools::cold_toolbox_open_delay + .get(toolbox_id) + .accumulate_samples(samples); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded errors + +* [`invalid_value`](../../user/metrics/error-reporting.md): If recording a negative sample. +* [`invalid_overflow`](../../user/metrics/error-reporting.md): If recording a sample longer than the maximum for the given `time_unit`. +{{#include ../../_includes/label-errors.md}} + +### `accumulateSingleSample` + +Accumulates a single signed sample and appends it to the metric for the provided label. +Prefer `start()` and `stopAndAccumulate()` where possible, +but if you must record time externally please prefer this method for individual samples +(avoids having to allocate and pass collections). + +A signed value is required so that the platform-specific code can provide +us with a 64 bit signed integer if no `u64` comparable type is available. +This will take care of filtering and reporting errors for a negative +sample. + +Please note that this assumes that the provided sample is already in +the "unit" declared by the instance of the metric type (e.g. if the +instance this method was called on is using `TimeUnit::Second`, then +`sample` is assumed to be in that unit). + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +devtools::cold_toolbox_open_delay + .get(toolbox_id) + .accumulate_single_sample(sample); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +#### Recorded errors + +* [`invalid_value`](../../user/metrics/error-reporting.md): If recording a negative sample. +* [`invalid_overflow`](../../user/metrics/error-reporting.md): If recording a sample longer than the maximum for the given `time_unit`. +{{#include ../../_includes/label-errors.md}} + + +## Testing API + +### `testGetValue` + +Gets the recorded value for a given label in a labeled timing distribution metric. +Returns a struct with counts per buckets and total sum if data is stored. +Returns a language-specific empty/null value if no data is stored. +Has an optional argument to specify the name of the ping you wish to retrieve data from, except +in Rust where it's required. `None` or no argument will default to the first value found for `send_in_pings`. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean_metrics::devtools; + +// Get the current snapshot of stored values. +let snapshot = devtools::cold_toolbox_open_delay.get("webconsole").test_get_value(None).unwrap(); + +// Usually you don't know the exact timing values, +// but you do know how many samples there are: +assert_eq!(2, snapshot.count); +// ...and the lower bound of how long they all took: +assert_ge!(400, snapshot.sum); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +### `testGetNumRecordedErrors` + +Gets the number of errors recorded for a given labeled timing distribution metric in total. + +{{#include ../../../shared/tab_header.md}} + +
+
+
+
+
+ +```Rust +use glean::ErrorType; +use glean_metrics::network; + +// Assert there were no negative values instrumented. +assert_eq!( + 0, + devtools::cold_toolbox_open_delay.test_get_num_recorded_errors( + ErrorType::InvalidValue, + None + ) +); +``` + +
+
+
+ +{{#include ../../../shared/tab_footer.md}} + +## Metric parameters + +Example labeled timing distribution metric definition: + +```YAML +devtools: + cold_toolbox_open_delay: + type: labeled_timing_distribution + description: > + Time taken to open the first DevTools toolbox, per tool being opened. + time_unit: millisecond + bugs: + - https://bugzilla.mozilla.org/000000 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=000000#c3 + notification_emails: + - me@mozilla.com + expires: 175 + labels: + - inspector + - webconsole + - jsdebugger + ... +``` + +### Extra metric parameters + +#### `time_unit` + +Labeled timing distributions have an optional `time_unit` +parameter to specify the smallest unit of resolution that it will record. +The allowed values for `time_unit` are: + +* `nanosecond` (default) +* `microsecond` +* `millisecond` +* `second` +* `minute` +* `hour` +* `day` + +{{#include ../../_includes/labels-parameter.md}} + +## Data questions + +* What is the distribution of initial load times of devtools toolboxes, per tool? +* What is the distribution of how long it takes to load extensions' content scripts, by addon id? + +## Limits + +* Timings are recorded in nanoseconds + * In Rust, [`time::precise_time_ns()`](https://docs.rs/time/0.1.42/time/fn.precise_time_ns.html) is used. +* The maximum timing value that will be recorded depends on the `time_unit` parameter: + + - `nanosecond`: 1ns <= x <= 10 minutes + - `microsecond`: 1μs <= x <= ~6.94 days + - `millisecond`: 1ms <= x <= ~19 years + + Longer times will be truncated to the maximum value and an error will be recorded. +{{#include ../../_includes/label-limits.md}} + +## Reference + +* Rust API docs: [`LabeledMetric`](../../../docs/glean/private/struct.LabeledMetric.html), [`TimingDistributionMetricType`](../../../docs/glean/private/struct.TimingDistributionMetric.html) diff --git a/docs/user/reference/metrics/quantity.md b/docs/user/reference/metrics/quantity.md index eb73a814ba..1de2660b61 100644 --- a/docs/user/reference/metrics/quantity.md +++ b/docs/user/reference/metrics/quantity.md @@ -316,4 +316,3 @@ Quantities have the required `unit` parameter, which is a free-form string for d * [Swift API docs](../../../swift/Classes/QuantityMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.QuantityMetric) * [Rust API docs](../../../docs/glean/private/quantity/struct.QuantityMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_quantity.default.html#set) diff --git a/docs/user/reference/metrics/string.md b/docs/user/reference/metrics/string.md index d9b034be6c..12d1121bb7 100644 --- a/docs/user/reference/metrics/string.md +++ b/docs/user/reference/metrics/string.md @@ -121,7 +121,8 @@ Glean.searchDefault.name.set("wikipedia"); #### Limits -* Fixed maximum string length: 100. Longer strings are truncated. This is measured in the number of bytes when the string is encoded in UTF-8. +* Fixed maximum string length: 255. Longer strings are truncated. This is measured in the number of bytes when the string is encoded in UTF-8. + * Prior to Glean v60.4.0 the limit was 100 bytes. ## Testing API @@ -356,4 +357,3 @@ N/A * [Swift API docs](../../../swift/Classes/StringMetricType.html) * [Python API docs](../../../python/glean/metrics/string.html) * [Rust API docs](../../../docs/glean/private/struct.StringMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_string.default.html#set) diff --git a/docs/user/reference/metrics/timespan.md b/docs/user/reference/metrics/timespan.md index a1c907edd5..f80a5a77bd 100644 --- a/docs/user/reference/metrics/timespan.md +++ b/docs/user/reference/metrics/timespan.md @@ -685,4 +685,3 @@ and use the largest possible value that will provide useful information so as to * [Swift API docs](../../../swift/Classes/TimespanMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.TimespanMetricType) * [Rust API docs](../../../docs/glean/private/struct.TimespanMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_timespan.default.html) diff --git a/docs/user/reference/metrics/url.md b/docs/user/reference/metrics/url.md index 4a00745cd7..2dd6fa42f8 100644 --- a/docs/user/reference/metrics/url.md +++ b/docs/user/reference/metrics/url.md @@ -355,7 +355,3 @@ N/A ## Data questions * What is the base URL used to build the search query for the search engine? - -## Reference - -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_url.default.html) diff --git a/docs/user/reference/metrics/uuid.md b/docs/user/reference/metrics/uuid.md index 8a4e602d90..1d84b4d421 100644 --- a/docs/user/reference/metrics/uuid.md +++ b/docs/user/reference/metrics/uuid.md @@ -422,4 +422,3 @@ N/A * [Swift API docs](../../../swift/Classes/UuidMetricType.html) * [Python API docs](../../../python/glean/metrics/index.html#glean.metrics.UuidMetricType) * [Rust API docs](../../../docs/glean/private/uuid/struct.UuidMetric.html) -* [JavaScript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_uuid.default.html#set) diff --git a/docs/user/user/adding-glean-to-your-project/enable-data-ingestion.md b/docs/user/user/adding-glean-to-your-project/enable-data-ingestion.md index 2efb1cf666..fa2e4d8494 100644 --- a/docs/user/user/adding-glean-to-your-project/enable-data-ingestion.md +++ b/docs/user/user/adding-glean-to-your-project/enable-data-ingestion.md @@ -40,6 +40,6 @@ and be scraped for metrics as a dependency of another product. This will result in your library being added to [probe scraper](https://github.com/mozilla/probe-scraper)'s [`repositories.yaml`](https://github.com/mozilla/probe-scraper/blob/main/repositories.yaml). -[dataeng-bug]: https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_ignored=0&bug_severity=--&bug_status=NEW&bug_type=task&cf_accessibility_severity=---&cf_fx_iteration=---&cf_fx_points=---&cf_status_firefox121=---&cf_status_firefox122=---&cf_status_firefox123=---&cf_tracking_firefox121=---&cf_tracking_firefox122=---&cf_tracking_firefox123=---&cf_tracking_firefox_relnote=---&comment=%2523%20To%20be%20filled%20by%20the%20requester%0D%0A%0D%0A%2A%2AApplication%20ID%5C%2A%2A%2A%3A%20my.app_id%0D%0A%2A%2AApplication%20Canonical%20Name%2A%2A%3A%20My%20Application%0D%0A%2A%2ADescription%2A%2A%3A%20Brief%20description%20of%20your%20application%0D%0A%2A%2AData-review%20response%20link%2A%2A%3A%20The%20link%20to%20the%20data%20response%20to%20the%20data%20collection%20request%20for%20adding%20Glean%20to%20your%20project.%0D%0A%2A%2ARepository%20URL%2A%2A%3A%20https%3A%2F%2Fgithub.com%2Fmozilla%2Fmy_app_name%0D%0A%2A%2ALocations%20of%20%60metrics.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20%20-%20src%2Fmetrics.yaml%0D%0A%0D%0A%2A%2ALocations%20of%20%60pings.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20-%20src%2Fpings.yaml%0D%0A%0D%0A%2A%2ADependencies%5C%2A%5C%2A%2A%2A%3A%0D%0A%20-%20glean-core%0D%0A%0D%0A%2A%2ARetention%20Days%5C%2A%5C%2A%5C%2A%2A%2A%3A%20N%0D%0A%0D%0A%2523%2523%20Notes%20and%20guidelines%0D%0A%0D%0A%5C%2A%20This%20is%20the%20identifier%20used%20to%20initialize%20Glean%20%28or%20the%20id%20used%20on%20the%20store%20on%20Android%20and%20Apple%20devices%29.%0D%0A%0D%0A%5C%2A%5C%2A%20Dependencies%20can%20be%20found%20%5Bin%20the%20Glean%20repositories%5D%28https%3A%2F%2Fprobeinfo.telemetry.mozilla.org%2Fv2%2Fglean%2Flibrary-variants%29.%20Each%20dependency%20must%20be%20listed%20explicitly.%20For%20example%2C%20the%20default%20Glean%20probes%20will%20only%20be%20included%20if%20glean%20itself%20is%20a%20dependency.%0D%0A%0D%0A%5C%2A%5C%2A%5C%2A%20Number%20of%20days%20that%20raw%20data%20will%20be%20retained.%20A%20good%20default%20is%20400.%20We%20can%20change%20this%20later%20to%20accommodate%20longer%20retention%20periods%2C%20though%20we%20cannot%20recover%20data%20that%20is%20past%20the%20retention%20period%20%28for%20example%2C%20we%20cannot%20recover%20data%20that%20is%20200%20days%20old%20if%20your%20retention%20period%20is%20180%20days%29.%0D%0A%0D%0A%2523%2523%20Need%20additional%20help%253F%0D%0AIf%20you%20need%20new%20dependencies%2C%20please%20file%20new%20bugs%20for%20them%2C%20separately%20from%20this%20one.%20For%20any%20questions%2C%20ask%20in%20the%20%2523glean%20channel.%0D%0A%0D%0A%2523%20To%20be%20filled%20by%20the%20Glean%20team%0D%0A%5B%2A%2AApplication%20friendly%20name%2A%2A%5D%28https%3A%2F%2Fmozilla.github.io%2Fprobe-scraper%2F%2523tag%2Fapplication%29%3A%20my_app_name&component=Glean%20Platform&contenttypemethod=list&contenttypeselection=text%2Fplain&defined_groups=1&filed_via=standard_form&flag_type-4=X&flag_type-607=X&flag_type-803=X&flag_type-864=X&flag_type-936=X&needinfo_role=other&needinfo_type=needinfo_from&op_sys=Unspecified&priority=--&product=Data%20Platform%20and%20Tools&rep_platform=Unspecified&short_desc=Enable%20new%20Glean%20App%20%60my.app_id%60&target_milestone=---&version=unspecified +[dataeng-bug]: https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_ignored=0&bug_severity=--&bug_status=NEW&bug_type=task&cf_accessibility_severity=---&cf_fx_iteration=---&cf_fx_points=---&cf_status_firefox128=---&cf_status_firefox129=---&cf_status_firefox130=---&cf_tracking_firefox128=---&cf_tracking_firefox129=---&cf_tracking_firefox130=---&cf_tracking_firefox_relnote=---&comment=%2523%20To%20be%20filled%20by%20the%20requester%0D%0A%0D%0A%2A%2AApplication%20ID%5C%2A%2A%2A%3A%20my.app_id%0D%0A%2A%2AApplication%20Canonical%20Name%2A%2A%3A%20My%20Application%0D%0A%2A%2ADescription%2A%2A%3A%20Brief%20description%20of%20your%20application%0D%0A%2A%2AData-review%20response%20link%2A%2A%3A%20The%20link%20to%20the%20data%20response%20to%20the%20data%20collection%20request%20for%20adding%20Glean%20to%20your%20project.%0D%0A%2A%2ARepository%20URL%2A%2A%3A%20https%3A%2F%2Fgithub.com%2Fmozilla%2Fmy_app_name%0D%0A%2A%2ALocations%20of%20%60metrics.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20%20-%20src%2Fmetrics.yaml%0D%0A%0D%0A%2A%2ALocations%20of%20%60pings.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20-%20src%2Fpings.yaml%0D%0A%0D%0A%2A%2ADependencies%5C%2A%5C%2A%2A%2A%3A%0D%0A%20-%20glean-core%0D%0A%0D%0A%2A%2ARetention%20Days%5C%2A%5C%2A%5C%2A%2A%2A%3A%20N%0D%0A%0D%0A%2A%2AData%20access%20restrictions%5C%2A%5C%2A%5C%2A%5C%2A%2A%2A%3A%20No%2FYes%0D%0A%0D%0A%2523%2523%20Notes%20and%20guidelines%0D%0A%0D%0A%5C%2A%20This%20is%20the%20identifier%20used%20to%20initialize%20Glean%20%28or%20the%20id%20used%20on%20the%20store%20on%20Android%20and%20Apple%20devices%29.%0D%0A%0D%0A%5C%2A%5C%2A%20Dependencies%20can%20be%20found%20%5Bin%20the%20Glean%20repositories%5D%28https%3A%2F%2Fprobeinfo.telemetry.mozilla.org%2Fv2%2Fglean%2Flibrary-variants%29.%20Each%20dependency%20must%20be%20listed%20explicitly.%20For%20example%2C%20the%20default%20Glean%20probes%20will%20only%20be%20included%20if%20glean%20itself%20is%20a%20dependency.%0D%0A%0D%0A%5C%2A%5C%2A%5C%2A%20Number%20of%20days%20that%20raw%20data%20will%20be%20retained.%20A%20good%20default%20is%20400.%20We%20can%20change%20this%20later%20to%20accommodate%20longer%20retention%20periods%2C%20though%20we%20cannot%20recover%20data%20that%20is%20past%20the%20retention%20period%20%28for%20example%2C%20we%20cannot%20recover%20data%20that%20is%20200%20days%20old%20if%20your%20retention%20period%20is%20180%20days%29.%0D%0A%0D%0A%5C%2A%5C%2A%5C%2A%5C%2A%20Depending%20on%20classification%2C%20access%20to%20data%20might%20need%20to%20be%20restricted%20in%20BigQuery.%20If%20this%20is%20the%20case%20and%20you%20know%20that%20should%20be%20used%20-%20note%20it.%0D%0A%0D%0A%2523%2523%20Need%20additional%20help%253F%0D%0AIf%20you%20need%20new%20dependencies%2C%20please%20file%20new%20bugs%20for%20them%2C%20separately%20from%20this%20one.%20For%20any%20questions%2C%20ask%20in%20the%20%2523glean%20channel.%0D%0A%0D%0A%2523%20To%20be%20filled%20by%20the%20Glean%20team%0D%0A%5B%2A%2AApplication%20friendly%20name%2A%2A%5D%28https%3A%2F%2Fmozilla.github.io%2Fprobe-scraper%2F%2523tag%2Fapplication%29%3A%20my_app_name&component=Glean%20Platform&contenttypemethod=list&contenttypeselection=text%2Fplain&defined_groups=1&filed_via=standard_form&flag_type-4=X&flag_type-607=X&flag_type-803=X&flag_type-864=X&flag_type-936=X&needinfo_role=other&needinfo_type=needinfo_from&op_sys=Unspecified&priority=--&product=Data%20Platform%20and%20Tools&rep_platform=Unspecified&short_desc=Enable%20new%20Glean%20App%20%60my.app_id%60&target_milestone=---&version=unspecified [dataeng-bug-libraries]: https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_ignored=0&bug_severity=--&bug_status=NEW&bug_type=task&cf_accessibility_severity=---&cf_fx_iteration=---&cf_fx_points=---&cf_status_firefox119=---&cf_status_firefox120=---&cf_status_firefox121=---&cf_tracking_firefox119=---&cf_tracking_firefox120=---&cf_tracking_firefox121=---&cf_tracking_firefox_relnote=---&comment=%23%20To%20be%20filled%20by%20the%20requester%0D%0A%0D%0A%2A%2ALibrary%20Canonical%20Name%2A%2A%3A%20My%20library%0D%0A%2A%2ADescription%2A%2A%3A%20Brief%20description%20of%20your%20library%0D%0A%2A%2AData-review%20response%20link%2A%2A%3A%20The%20link%20to%20the%20data%20response%20to%20the%20data%20collection%20request%20for%20adding%20Glean%20to%20your%20project%5C%2A.%0D%0A%2A%2ARepository%20URL%2A%2A%3A%20https%3A%2F%2Fgithub.com%2Fmozilla%2Fmy_app_name%0D%0A%2A%2ALocations%20of%20%60metrics.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20%20-%20src%2Fmetrics.yaml%0D%0A%0D%0A%2A%2ALocations%20of%20%60pings.yaml%60%20files%20%28can%20be%20many%29%3A%2A%2A%0D%0A%20-%20src%2Fpings.yaml%0D%0A%0D%0A%2A%2AData%20owners%3A%2A%2A%0D%0A-%20you%3F%0D%0A-%20anyone%20else%3F%0D%0A%0D%0A%2A%2ADependency%20of%2A%2A%3A%0D%0A%20-%20Fenix%0D%0A%0D%0A%23%23%20Notes%0D%0A%0D%0A%5C%2A%20These%20data-review%20requests%20need%20to%20be%20for%20the%20final%20product%20embedding%20the%20library%20and%20sending%20the%20data.%0D%0A%0D%0A%23%23%20Need%20additional%20help%3F%0D%0A%0D%0AFor%20any%20questions%2C%20ask%20in%20the%20%23glean%20channel.&component=Glean%20Platform&contenttypemethod=list&contenttypeselection=text%2Fplain&defined_groups=1&filed_via=standard_form&flag_type-4=X&flag_type-607=X&flag_type-800=X&flag_type-803=X&flag_type-864=X&flag_type-936=X&form_name=enter_bug&maketemplate=Remember%20values%20as%20bookmarkable%20template&op_sys=Unspecified&priority=--&product=Data%20Platform%20and%20Tools&rep_platform=Unspecified&short_desc=Enable%20new%20Glean%20library%20%60library-name%60&target_milestone=---&version=unspecified diff --git a/glean-core/Cargo.toml b/glean-core/Cargo.toml index ffcf7faff3..0e631d7998 100644 --- a/glean-core/Cargo.toml +++ b/glean-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glean-core" -version = "60.3.0" +version = "60.4.0" authors = ["Jan-Erik Rediger ", "The Glean Team "] description = "A modern Telemetry library" repository = "https://github.com/mozilla/glean" @@ -21,7 +21,7 @@ include = [ rust-version = "1.66" [package.metadata.glean] -glean-parser = "14.0.1" +glean-parser = "14.3.0" [badges] circle-ci = { repository = "mozilla/glean", branch = "main" } diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt index 3b77cee4e7..c45d6e71d8 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt @@ -149,10 +149,6 @@ open class GleanInternalAPI internal constructor() { internal var isCustomDataPath: Boolean = false - init { - gleanEnableLogging() - } - /** * Initialize the Glean SDK. * @@ -185,6 +181,8 @@ open class GleanInternalAPI internal constructor() { configuration: Configuration = Configuration(), buildInfo: BuildInfo, ) { + gleanEnableLogging() + configuration.dataPath?.let { safeDataPath -> // When the `dataPath` is provided, we need to make sure: // 1. The database path provided is not `glean_data`. @@ -542,7 +540,7 @@ open class GleanInternalAPI internal constructor() { * API synchronously. */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) - internal fun enableTestingMode() { + fun enableTestingMode() { this.setTestingMode(true) } @@ -551,7 +549,7 @@ open class GleanInternalAPI internal constructor() { * This can be called by tests to change test mode on-the-fly. */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) - internal fun setTestingMode(enabled: Boolean) { + fun setTestingMode(enabled: Boolean) { this.testingMode = enabled gleanSetTestMode(enabled) Dispatchers.API.setTestingMode(enabled) @@ -572,7 +570,7 @@ open class GleanInternalAPI internal constructor() { * @param uploadEnabled whether upload is enabled */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) - internal fun resetGlean( + fun resetGlean( context: Context, config: Configuration, clearStores: Boolean, @@ -628,7 +626,7 @@ open class GleanInternalAPI internal constructor() { * @param port the local address to send pings to */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) - internal fun testSetLocalEndpoint(port: Int) { + fun testSetLocalEndpoint(port: Int) { Glean.enableTestingMode() isSendingToTestEndpoint = true @@ -646,7 +644,7 @@ open class GleanInternalAPI internal constructor() { * @param dataPath The path to the data folder. Must be set if `clearStores` is `true`. */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) - internal fun testDestroyGleanHandle(clearStores: Boolean = false, dataPath: String? = null) { + fun testDestroyGleanHandle(clearStores: Boolean = false, dataPath: String? = null) { // If it was initialized this also clears the directory gleanTestDestroyGlean(clearStores, dataPath) diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/config/Configuration.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/config/Configuration.kt index 9a40bb7532..4be7c5e2a3 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/config/Configuration.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/config/Configuration.kt @@ -20,7 +20,7 @@ import mozilla.telemetry.glean.net.PingUploader * @property dataPath An optional [String] that specifies where to store data locally on the device. * This should ONLY be used when setting up Glean on a non-main process. * @property logLevel An optional [LevelFilter] that controls how verbose the internal logging is. - * @property enableEventTimestamps (Experimental) Whether to add a wallclock timestamp to all events. + * @property enableEventTimestamps Whether to add a wallclock timestamp to all events. * @property experimentationId An experimentation identifier derived by the application * to be sent with all pings. * @property enableInternalPings Whether to enable internal pings. @@ -39,7 +39,7 @@ data class Configuration @JvmOverloads constructor( val enableEventTimestamps: Boolean = true, val experimentationId: String? = null, val enableInternalPings: Boolean = true, - val delayPingLifetimeIo: Boolean = false, + val delayPingLifetimeIo: Boolean = true, ) { companion object { /** diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/Aliases.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/Aliases.kt index 3e1afce5c8..0d14644aed 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/Aliases.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/Aliases.kt @@ -55,3 +55,13 @@ typealias RecordedExperiment = mozilla.telemetry.glean.internal.RecordedExperime * A rate value as given by its numerator and denominator. */ typealias Rate = mozilla.telemetry.glean.internal.Rate + +/** + * The set of data needed to construct labeled metric types. + */ +typealias LabeledMetricData = mozilla.telemetry.glean.internal.LabeledMetricData + +/** + * The set of data specifically needed to construct simple labeled metric types. + */ +typealias CommonLabeledMetricData = mozilla.telemetry.glean.internal.LabeledMetricData.Common diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/HistogramBase.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/HistogramBase.kt index 8dbc02c2f1..87f8d93d60 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/HistogramBase.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/HistogramBase.kt @@ -22,3 +22,9 @@ interface HistogramBase { */ fun accumulateSamples(samples: List) } + +// glean_parser template currently expects `HistogramMetricBase` as the name +// and since this alias was defined in `service-glean` in android-components, +// we need to keep the alias until the parser template is updated also. +// See Bug 1906941 for more information. +typealias HistogramMetricBase = mozilla.telemetry.glean.private.HistogramBase diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/LabeledMetricType.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/LabeledMetricType.kt index 20940f8f8a..cd32c82b2c 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/LabeledMetricType.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/LabeledMetricType.kt @@ -36,12 +36,14 @@ class LabeledMetricType( private val inner: Any init { - val meta = CommonMetricData( - category = category, - name = name, - sendInPings = sendInPings, - disabled = disabled, - lifetime = lifetime, + val meta = CommonLabeledMetricData( + cmd = CommonMetricData( + category = category, + name = name, + sendInPings = sendInPings, + disabled = disabled, + lifetime = lifetime, + ), ) this.inner = when (subMetric) { diff --git a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/StringMetricTypeTest.kt b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/StringMetricTypeTest.kt index 05113e0d4f..0af5f76630 100644 --- a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/StringMetricTypeTest.kt +++ b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/StringMetricTypeTest.kt @@ -124,7 +124,7 @@ class StringMetricTypeTest { ), ) - stringMetric.set("0123456789".repeat(11)) + stringMetric.set("0123456789".repeat(26)) assertEquals(1, stringMetric.testGetNumRecordedErrors(ErrorType.INVALID_OVERFLOW)) } diff --git a/glean-core/android/src/test/java/mozilla/telemetry/glean/scheduler/MetricsPingSchedulerTest.kt b/glean-core/android/src/test/java/mozilla/telemetry/glean/scheduler/MetricsPingSchedulerTest.kt index b75fcd6856..df477f1310 100644 --- a/glean-core/android/src/test/java/mozilla/telemetry/glean/scheduler/MetricsPingSchedulerTest.kt +++ b/glean-core/android/src/test/java/mozilla/telemetry/glean/scheduler/MetricsPingSchedulerTest.kt @@ -280,6 +280,19 @@ class MetricsPingSchedulerTest { // Setup a test server and make Glean point to it. val server = getMockWebServer() + // Trick Glean into not sending the overdue ping which will have `glean.databse` metrics. + val fakeNow = Calendar.getInstance() + fakeNow.clear() + @Suppress("MagicNumber") // it's a fixed date only used in tests. + fakeNow.set(2015, 6, 11, 2, 0, 0) + SystemClock.setCurrentTimeMillis(fakeNow.timeInMillis) + + // Set the last sent date to yesterday. + val buildInfo = BuildInfo(versionCode = "0.0.1", versionName = "0.0.1", buildDate = Calendar.getInstance()) + val mps = MetricsPingScheduler(context, buildInfo) + + mps.updateSentDate(getISOTimeString(fakeNow, truncateTo = TimeUnit.DAY)) + resetGlean( context, Configuration( diff --git a/glean-core/build/Cargo.toml b/glean-core/build/Cargo.toml index 1b4b9796d4..6089b0d00c 100644 --- a/glean-core/build/Cargo.toml +++ b/glean-core/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glean-build" -version = "14.0.1" +version = "14.3.0" edition = "2021" description = "Glean SDK Rust build helper" repository = "https://github.com/mozilla/glean" diff --git a/glean-core/build/src/lib.rs b/glean-core/build/src/lib.rs index 0b3fc17f5e..23f6841135 100644 --- a/glean-core/build/src/lib.rs +++ b/glean-core/build/src/lib.rs @@ -39,7 +39,7 @@ use std::env; use xshell_venv::{Result, Shell, VirtualEnv}; -const GLEAN_PARSER_VERSION: &str = "14.0.1"; +const GLEAN_PARSER_VERSION: &str = "14.3.0"; /// A Glean Rust bindings generator. pub struct Builder { diff --git a/glean-core/ios/Glean/Config/Configuration.swift b/glean-core/ios/Glean/Config/Configuration.swift index 47cc29024f..93f6828dbd 100644 --- a/glean-core/ios/Glean/Config/Configuration.swift +++ b/glean-core/ios/Glean/Config/Configuration.swift @@ -28,7 +28,7 @@ public struct Configuration { /// * dataPath an optional String that specifies where to store data locally on the device. /// This should ONLY be used when setting up Glean on a non-main process. /// * logLevel an optional log level that controls how verbose the internal logging is. - /// * enableEventTimestamps (Experimental) whether to add a wallclock timestamp to all events + /// * enableEventTimestamps whether to add a wallclock timestamp to all events /// * experimentationId An experimentation identifier derived by the application /// to be sent with all pings. /// * enableInternalPings Whether to enable internal pings. diff --git a/glean-core/ios/Glean/Metrics/LabeledMetric.swift b/glean-core/ios/Glean/Metrics/LabeledMetric.swift index ff148b9663..040c2dda3d 100644 --- a/glean-core/ios/Glean/Metrics/LabeledMetric.swift +++ b/glean-core/ios/Glean/Metrics/LabeledMetric.swift @@ -46,11 +46,11 @@ public class LabeledMetricType { switch subMetric { case is CounterMetricType: - self.inner = LabeledCounter(meta, labels) + self.inner = LabeledCounter(.common(cmd: meta), labels) case is BooleanMetricType: - self.inner = LabeledBoolean(meta, labels) + self.inner = LabeledBoolean(.common(cmd: meta), labels) case is StringMetricType: - self.inner = LabeledString(meta, labels) + self.inner = LabeledString(.common(cmd: meta), labels) default: throw "Can not create a labeled version of this metric type" } diff --git a/glean-core/ios/Glean/Metrics/ObjectMetric.swift b/glean-core/ios/Glean/Metrics/ObjectMetric.swift index 841f488a79..103e7b9466 100644 --- a/glean-core/ios/Glean/Metrics/ObjectMetric.swift +++ b/glean-core/ios/Glean/Metrics/ObjectMetric.swift @@ -13,7 +13,7 @@ extension Array: ObjectSerialize where Element: Codable { public func intoSerializedObject() -> String { let jsonEncoder = JSONEncoder() let jsonData = try! jsonEncoder.encode(self) - let json = String(data: jsonData, encoding: String.Encoding.utf8)! + let json = String(decoding: jsonData, as: UTF8.self) return json } } diff --git a/glean-core/ios/GleanTests/Metrics/StringMetricTests.swift b/glean-core/ios/GleanTests/Metrics/StringMetricTests.swift index 4042932843..25385478ae 100644 --- a/glean-core/ios/GleanTests/Metrics/StringMetricTests.swift +++ b/glean-core/ios/GleanTests/Metrics/StringMetricTests.swift @@ -88,7 +88,7 @@ class StringMetricTests: XCTestCase { disabled: false )) - stringMetric.set(String(repeating: "0123456789", count: 11)) + stringMetric.set(String(repeating: "0123456789", count: 26)) XCTAssertEqual(1, stringMetric.testGetNumRecordedErrors(.invalidOverflow)) } diff --git a/glean-core/ios/GleanTests/TestUtils.swift b/glean-core/ios/GleanTests/TestUtils.swift index 9568363a7b..ead1f6fbb1 100644 --- a/glean-core/ios/GleanTests/TestUtils.swift +++ b/glean-core/ios/GleanTests/TestUtils.swift @@ -104,9 +104,7 @@ func tearDownStubs() { func JSONStringify(_ json: Any) -> String { do { let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) - if let string = String(data: data, encoding: String.Encoding.utf8) { - return string - } + return String(decoding: data, as: UTF8.self) } catch { print(error) } diff --git a/glean-core/ios/sdk_generator.sh b/glean-core/ios/sdk_generator.sh index 15b2f194e0..9df4877759 100755 --- a/glean-core/ios/sdk_generator.sh +++ b/glean-core/ios/sdk_generator.sh @@ -25,7 +25,7 @@ set -e -GLEAN_PARSER_VERSION=14.0 +GLEAN_PARSER_VERSION=14.3 # CMDNAME is used in the usage text below. # shellcheck disable=SC2034 diff --git a/glean-core/metrics.yaml b/glean-core/metrics.yaml index 18040bc4ff..212cf2e88e 100644 --- a/glean-core/metrics.yaml +++ b/glean-core/metrics.yaml @@ -720,6 +720,22 @@ glean.database: - glean-team@mozilla.com expires: never + write_time: + type: timing_distribution + time_unit: microsecond + description: | + The time it takes for a write-commit for the Glean database. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1896193 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1896193#c4 + data_sensitivity: + - technical + notification_emails: + - glean-team@mozilla.com + - jrediger@mozilla.com + expires: never + glean.validation: foreground_count: type: counter diff --git a/glean-core/python/glean/__init__.py b/glean-core/python/glean/__init__.py index b7c961675f..4a34816964 100644 --- a/glean-core/python/glean/__init__.py +++ b/glean-core/python/glean/__init__.py @@ -30,7 +30,7 @@ __email__ = "glean-team@mozilla.com" -GLEAN_PARSER_VERSION = "14.0.1" +GLEAN_PARSER_VERSION = "14.3.0" parser_version = VersionInfo.parse(GLEAN_PARSER_VERSION) parser_version_next_major = parser_version.bump_major() diff --git a/glean-core/python/glean/_loader.py b/glean-core/python/glean/_loader.py index 54ba3f4f80..6ca3cba7e7 100644 --- a/glean-core/python/glean/_loader.py +++ b/glean-core/python/glean/_loader.py @@ -280,7 +280,12 @@ def _get_metric_objects( if "dynamic_label" not in args: args["dynamic_label"] = None meta_args, rest = _split_ctor_args(args) - glean_metric = metric_type(metrics.CommonMetricData(**meta_args), **rest) + if getattr(metric, "labeled", False): + glean_metric = metric_type( + metrics.LabeledMetricData.COMMON(metrics.CommonMetricData(**meta_args)), **rest + ) + else: + glean_metric = metric_type(metrics.CommonMetricData(**meta_args), **rest) glean_metric.__doc__ = metric.description diff --git a/glean-core/python/glean/config.py b/glean-core/python/glean/config.py index 530a46dd15..b11fb8625e 100644 --- a/glean-core/python/glean/config.py +++ b/glean-core/python/glean/config.py @@ -48,8 +48,8 @@ def __init__( implementation. Defaults to `glean.net.HttpClientUploader`. allow_multiprocessing (bool): When True (default), use a subprocess to offload some work (such as ping uploading). - enable_event_timestamps (bool): (Experimental) Whether to add a - wallclock timestamp to all events. Default: `True`. + enable_event_timestamps (bool): Whether to add a wallclock timestamp + to all events. Default: `True`. experimentation_id (string): An experimentation identifier derived by the application to be sent with all pings. Default: None. enable_internal_pings (bool): Whether to enable internal pings. Default: `True`. @@ -98,7 +98,7 @@ def max_events(self) -> int: @property def enable_event_timestamps(self) -> bool: - """(Experimental) Whether to add a wallclock timestamp to all events.""" + """Whether to add a wallclock timestamp to all events.""" return self._enable_event_timestamps @property diff --git a/glean-core/python/glean/metrics/__init__.py b/glean-core/python/glean/metrics/__init__.py index 19cc6a39ab..896e3d8ffc 100644 --- a/glean-core/python/glean/metrics/__init__.py +++ b/glean-core/python/glean/metrics/__init__.py @@ -9,6 +9,7 @@ # Re-export utilities from .._uniffi import CommonMetricData +from .._uniffi import LabeledMetricData from .._uniffi import Lifetime from .._uniffi import MemoryUnit from .._uniffi import TimerId @@ -48,6 +49,7 @@ "EventMetricType", "LabeledBooleanMetricType", "LabeledCounterMetricType", + "LabeledMetricData", "LabeledStringMetricType", "Lifetime", "MemoryDistributionMetricType", diff --git a/glean-core/python/glean/metrics/labeled.py b/glean-core/python/glean/metrics/labeled.py index f8bd497ae3..f918e8bfc4 100644 --- a/glean-core/python/glean/metrics/labeled.py +++ b/glean-core/python/glean/metrics/labeled.py @@ -6,9 +6,9 @@ from typing import Any, Optional, Set, Type -from .._uniffi import CommonMetricData from .._uniffi import LabeledBoolean from .._uniffi import LabeledCounter +from .._uniffi import LabeledMetricData from .._uniffi import LabeledString from ..testing import ErrorType @@ -37,10 +37,10 @@ class LabeledMetricBase: def __init__( self, - common_metric_data: CommonMetricData, + labeled_metric_data: LabeledMetricData, labels: Optional[Set[str]] = None, ): - self._inner = self._ctor(common_metric_data, labels) + self._inner = self._ctor(labeled_metric_data, labels) def __getitem__(self, item: str) -> Any: """ diff --git a/glean-core/python/requirements_dev.txt b/glean-core/python/requirements_dev.txt index b281fa5b85..bba7d59340 100644 --- a/glean-core/python/requirements_dev.txt +++ b/glean-core/python/requirements_dev.txt @@ -7,12 +7,12 @@ pip pytest-localserver==0.8.0 MarkupSafe==2.0.1 pytest-runner==5.3.2 -pytest==8.2.0 -ruff==0.4.3 +pytest==8.2.2 +ruff==0.5.0 semver==2.13.0 setuptools-git==1.2 twine==5.0.0 types-pkg_resources==0.1.3 wheel==0.42.0 -maturin==1.5.0 +maturin==1.6.0 patchelf>=0.17; sys_platform == "linux" diff --git a/glean-core/python/tests/metrics/test_labeled.py b/glean-core/python/tests/metrics/test_labeled.py index 9ddc7e2ecb..4634b8c9e8 100644 --- a/glean-core/python/tests/metrics/test_labeled.py +++ b/glean-core/python/tests/metrics/test_labeled.py @@ -5,19 +5,21 @@ from glean import Glean from glean import __version__ as glean_version from glean import metrics -from glean.metrics import Lifetime, CommonMetricData +from glean.metrics import Lifetime, CommonMetricData, LabeledMetricData from glean.testing import ErrorType def test_labeled_counter_type(ping_schema_url): labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_counter_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -31,13 +33,15 @@ def test_labeled_counter_type(ping_schema_url): def test_labeled_boolean_type(ping_schema_url): labeled_boolean_metric = metrics.LabeledBooleanMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_boolean_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_boolean_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -51,13 +55,15 @@ def test_labeled_boolean_type(ping_schema_url): def test_labeled_string_type(ping_schema_url): labeled_string_metric = metrics.LabeledStringMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_string_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_string_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -71,13 +77,15 @@ def test_labeled_string_type(ping_schema_url): def test_other_label_with_predefined_labels(ping_schema_url): labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_counter_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ), labels=["foo", "bar", "baz"], ) @@ -97,13 +105,15 @@ def test_other_label_with_predefined_labels(ping_schema_url): def test_other_label_without_predefined_labels(ping_schema_url): labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_counter_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -120,13 +130,15 @@ def test_other_label_without_predefined_labels(ping_schema_url): def test_other_label_without_predefined_labels_before_glean_init(): labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_counter_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -150,13 +162,15 @@ def test_other_label_without_predefined_labels_before_glean_init(): def test_invalid_labels_go_to_other(): labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - category="telemetry", - lifetime=Lifetime.APPLICATION, - name="labeled_counter_metric", - send_in_pings=["metrics"], - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + dynamic_label=None, + ) ) ) @@ -186,13 +200,15 @@ def test_rapidly_recreating_labeled_metrics_does_not_crash(): """ labeled_counter_metric = metrics.LabeledCounterMetricType( - CommonMetricData( - category="telemetry", - name="labeled_nocrash", - send_in_pings=["metrics"], - lifetime=Lifetime.APPLICATION, - disabled=False, - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + category="telemetry", + name="labeled_nocrash", + send_in_pings=["metrics"], + lifetime=Lifetime.APPLICATION, + disabled=False, + dynamic_label=None, + ) ), labels=["foo"], ) diff --git a/glean-core/python/tests/metrics/test_string.py b/glean-core/python/tests/metrics/test_string.py index cf53258dba..c5bc118454 100644 --- a/glean-core/python/tests/metrics/test_string.py +++ b/glean-core/python/tests/metrics/test_string.py @@ -79,7 +79,7 @@ def test_setting_a_long_string_records_an_error(): ) ) - string_metric.set("0123456789" * 11) + string_metric.set("0123456789" * 26) assert 1 == string_metric.test_get_num_recorded_errors(testing.ErrorType.INVALID_OVERFLOW) diff --git a/glean-core/python/tests/test_network.py b/glean-core/python/tests/test_network.py index 8c2ab98568..6aba510ac4 100644 --- a/glean-core/python/tests/test_network.py +++ b/glean-core/python/tests/test_network.py @@ -13,7 +13,7 @@ from glean import Glean from glean import _builtins from glean import metrics -from glean.metrics import CounterMetricType, Lifetime, CommonMetricData +from glean.metrics import CounterMetricType, Lifetime, CommonMetricData, LabeledMetricData from glean._process_dispatcher import ProcessDispatcher from glean.net import PingUploadWorker from glean.net.http_client import HttpClientUploader @@ -26,13 +26,15 @@ def get_upload_failure_metric(): return metrics.LabeledCounterMetricType( - CommonMetricData( - disabled=False, - send_in_pings=["metrics"], - name="ping_upload_failure", - category="glean.upload", - lifetime=metrics.Lifetime.PING, - dynamic_label=None, + LabeledMetricData.COMMON( + CommonMetricData( + disabled=False, + send_in_pings=["metrics"], + name="ping_upload_failure", + category="glean.upload", + lifetime=metrics.Lifetime.PING, + dynamic_label=None, + ) ), labels=[ "status_code_4xx", diff --git a/glean-core/rlb/Cargo.toml b/glean-core/rlb/Cargo.toml index c1d4ea9c1f..42ba287a2b 100644 --- a/glean-core/rlb/Cargo.toml +++ b/glean-core/rlb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glean" -version = "60.3.0" +version = "60.4.0" authors = ["Jan-Erik Rediger ", "The Glean Team "] description = "Glean SDK Rust language bindings" repository = "https://github.com/mozilla/glean" @@ -23,7 +23,7 @@ maintenance = { status = "actively-developed" } [dependencies.glean-core] path = ".." -version = "60.3.0" +version = "60.4.0" [dependencies] inherent = "1" diff --git a/glean-core/rlb/examples/delayed-ping-data.rs b/glean-core/rlb/examples/delayed-ping-data.rs new file mode 100644 index 0000000000..d683bf4afa --- /dev/null +++ b/glean-core/rlb/examples/delayed-ping-data.rs @@ -0,0 +1,130 @@ +// 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 https://mozilla.org/MPL/2.0/. + +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::{env, process}; + +use once_cell::sync::Lazy; + +use flate2::read::GzDecoder; +use glean::net; +use glean::{private::PingType, ClientInfoMetrics, ConfigurationBuilder}; + +pub mod glean_metrics { + use glean::{private::CounterMetric, CommonMetricData, Lifetime}; + + #[allow(non_upper_case_globals)] + pub static sample_counter: once_cell::sync::Lazy = + once_cell::sync::Lazy::new(|| { + CounterMetric::new(CommonMetricData { + name: "sample_counter".into(), + category: "test.metrics".into(), + send_in_pings: vec!["prototype".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }) + }); +} + +#[derive(Debug)] +struct MovingUploader(PathBuf); + +impl MovingUploader { + fn new(mut path: PathBuf) -> Self { + path.push("sent_pings"); + std::fs::create_dir_all(&path).unwrap(); + Self(path) + } +} + +impl net::PingUploader for MovingUploader { + fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { + let net::PingUploadRequest { + body, url, headers, .. + } = upload_request; + let mut gzip_decoder = GzDecoder::new(&body[..]); + let mut s = String::with_capacity(body.len()); + + let data = gzip_decoder + .read_to_string(&mut s) + .ok() + .map(|_| &s[..]) + .or_else(|| std::str::from_utf8(&body).ok()) + .unwrap(); + + let docid = url.rsplit('/').next().unwrap(); + let out_path = self.0.join(format!("{docid}.json")); + let mut fp = File::create(out_path).unwrap(); + + // pseudo-JSON, let's hope this works. + writeln!(fp, "{{").unwrap(); + writeln!(fp, " \"url\": {url},").unwrap(); + for (key, val) in headers { + writeln!(fp, " \"{key}\": \"{val}\",").unwrap(); + } + writeln!(fp, "}}").unwrap(); + writeln!(fp, "{data}").unwrap(); + + net::UploadResult::http_status(200) + } +} + +#[allow(non_upper_case_globals)] +pub static PrototypePing: Lazy = + Lazy::new(|| PingType::new("prototype", true, true, false, true, true, vec![], vec![])); + +fn main() { + env_logger::init(); + + let mut args = env::args().skip(1); + + let data_path = PathBuf::from(args.next().expect("need data path")); + let state = args.next().unwrap_or_default(); + + let uploader = MovingUploader::new(data_path.clone()); + let cfg = ConfigurationBuilder::new(true, data_path, "glean.pingflush") + .with_server_endpoint("invalid-test-host") + .with_use_core_mps(false) + .with_uploader(uploader) + .with_delay_ping_lifetime_io(true) + .build(); + + 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); + + // Wait for init to finish, + // otherwise we might be to quick with calling `shutdown`. + let _ = glean_metrics::sample_counter.test_get_value(None); + + match &*state { + "accumulate_one_and_pretend_crash" => { + log::debug!("incrementing by 1. exiting without shutdown."); + glean_metrics::sample_counter.add(1) + } + "accumulate_ten_and_orderly_shutdown" => { + log::debug!("incrementing by 10, waiting, shutdown. should trigger a flush."); + glean_metrics::sample_counter.add(10); + glean::shutdown(); + } + "submit_ping" => { + log::info!("submitting PrototypePing"); + PrototypePing.submit(None); + + glean::shutdown(); + } + _ => { + eprintln!("unknown argument: {state}"); + process::exit(1); + } + } +} diff --git a/glean-core/rlb/src/configuration.rs b/glean-core/rlb/src/configuration.rs index ecce5070cd..1f786254e1 100644 --- a/glean-core/rlb/src/configuration.rs +++ b/glean-core/rlb/src/configuration.rs @@ -41,7 +41,7 @@ pub struct Configuration { pub log_level: Option, /// The rate pings may be uploaded before they are throttled. pub rate_limit: Option, - /// (Experimental) Whether to add a wallclock timestamp to all events. + /// Whether to add a wallclock timestamp to all events. pub enable_event_timestamps: bool, /// An experimentation identifier derived by the application to be sent with all pings, it should /// be noted that this has an underlying StringMetric and so should conform to the limitations that @@ -93,7 +93,7 @@ pub struct Builder { /// Optional: The internal ping upload rate limit. /// Default: `None` pub rate_limit: Option, - /// (Experimental) Whether to add a wallclock timestamp to all events. + /// Whether to add a wallclock timestamp to all events. pub enable_event_timestamps: bool, /// An experimentation identifier derived by the application to be sent with all pings, it should /// be noted that this has an underlying StringMetric and so should conform to the limitations that diff --git a/glean-core/rlb/src/lib.rs b/glean-core/rlb/src/lib.rs index 3c9ab0cf53..34b3670526 100644 --- a/glean-core/rlb/src/lib.rs +++ b/glean-core/rlb/src/lib.rs @@ -36,8 +36,8 @@ pub use configuration::{Builder as ConfigurationBuilder, Configuration}; pub use core_metrics::ClientInfoMetrics; pub use glean_core::{ metrics::{Datetime, DistributionData, MemoryUnit, Rate, RecordedEvent, TimeUnit, TimerId}, - traits, CommonMetricData, Error, ErrorType, Glean, HistogramType, Lifetime, PingRateLimit, - RecordedExperiment, Result, + traits, CommonMetricData, Error, ErrorType, Glean, HistogramType, LabeledMetricData, Lifetime, + PingRateLimit, RecordedExperiment, Result, }; mod configuration; diff --git a/glean-core/rlb/src/test.rs b/glean-core/rlb/src/test.rs index e902d99ee1..3cc93151ef 100644 --- a/glean-core/rlb/src/test.rs +++ b/glean-core/rlb/src/test.rs @@ -1443,9 +1443,6 @@ fn pings_ride_along_builtin_pings() { } } - let _ride_along_ping = - private::PingType::new("ride-along", true, true, true, true, true, vec![], vec![]); - // Create a custom configuration to use a fake uploader. let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf(); @@ -1460,6 +1457,10 @@ fn pings_ride_along_builtin_pings() { let _t = new_glean(Some(cfg), true); + let reasons = vec!["active".to_string()]; + let _ride_along_ping = + private::PingType::new("ride-along", true, true, true, true, true, vec![], reasons); + // Simulate becoming active. handle_client_active(); diff --git a/glean-core/rlb/tests/persist_ping_lifetime.rs b/glean-core/rlb/tests/persist_ping_lifetime.rs deleted file mode 100644 index f73673f46f..0000000000 --- a/glean-core/rlb/tests/persist_ping_lifetime.rs +++ /dev/null @@ -1,89 +0,0 @@ -// 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 https://mozilla.org/MPL/2.0/. - -//! This integration test should model how the RLB is used when embedded in another Rust application -//! (e.g. FOG/Firefox Desktop). -//! -//! We write a single test scenario per file to avoid any state keeping across runs -//! (different files run as different processes). - -mod common; - -use glean::{ClientInfoMetrics, Configuration, ConfigurationBuilder}; -use std::path::PathBuf; - -/// Some user metrics. -mod metrics { - use glean::private::*; - use glean::Lifetime; - use glean_core::CommonMetricData; - use once_cell::sync::Lazy; - - #[allow(non_upper_case_globals)] - pub static boo: Lazy = Lazy::new(|| { - BooleanMetric::new(CommonMetricData { - name: "boo".into(), - category: "sample".into(), - send_in_pings: vec!["validation".into()], - lifetime: Lifetime::Ping, - disabled: false, - ..Default::default() - }) - }); -} - -fn cfg_new(tmpname: PathBuf) -> Configuration { - ConfigurationBuilder::new(true, tmpname, "firefox-desktop") - .with_server_endpoint("invalid-test-host") - .with_delay_ping_lifetime_io(true) - .build() -} - -/// Test scenario: Are ping-lifetime data persisted on shutdown when delayed? -/// -/// delay_ping_lifetime_io: true has Glean put "ping"-lifetime data in-memory -/// instead of the db. Ensure that, on orderly shutdowns, we correctly persist -/// these in-memory data to the db. -#[test] -fn delayed_ping_data() { - common::enable_test_logging(); - - metrics::boo.set(true); - - // Create a custom configuration to delay ping-lifetime io - let dir = tempfile::tempdir().unwrap(); - let tmpname = dir.path().to_path_buf(); - - common::initialize(cfg_new(tmpname.clone())); - - assert!( - metrics::boo.test_get_value(None).unwrap(), - "Data should be present. Doesn't mean it's persisted, though." - ); - - glean::test_reset_glean( - cfg_new(tmpname.clone()), - ClientInfoMetrics::unknown(), - false, - ); - - assert_eq!( - None, - metrics::boo.test_get_value(None), - "Data should not have made it to disk on unclean shutdown." - ); - metrics::boo.set(true); // Let's try again - - // This time, let's shut down cleanly - glean::shutdown(); - - // Now when we init, we should get the persisted data - glean::test_reset_glean(cfg_new(tmpname), ClientInfoMetrics::unknown(), false); - assert!( - metrics::boo.test_get_value(None).unwrap(), - "Data must be persisted between clean shutdown and init!" - ); - - glean::shutdown(); // Cleanly shut down at the end of the test. -} diff --git a/glean-core/rlb/tests/test-delayed-ping-data.sh b/glean-core/rlb/tests/test-delayed-ping-data.sh new file mode 100755 index 0000000000..84de6a69fe --- /dev/null +++ b/glean-core/rlb/tests/test-delayed-ping-data.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Test harness for testing the RLB processes from the outside. +# +# Some behavior can only be observed when properly exiting the process running Glean, +# e.g. when an uploader runs in another thread. +# On exit the threads will be killed, regardless of their state. + +# Remove the temporary data path on all exit conditions +cleanup() { + if [ -n "$datapath" ]; then + rm -r "$datapath" + fi +} +trap cleanup INT ABRT TERM EXIT + +tmp="${TMPDIR:-/tmp}" +datapath=$(mktemp -d "${tmp}/glean_ping_lifetime_flush.XXXX") + +cmd="cargo run -p glean --example delayed-ping-data -- $datapath" + +# First run "crashes" -> no increment stored +$cmd accumulate_one_and_pretend_crash +count=$(ls -1q "$datapath/sent_pings" | wc -l) +if [[ "$count" -ne 0 ]]; then + echo "test result: FAILED." + exit 101 +fi + +# Second run increments and orderly shuts down -> increment flushed to disk. +# No ping is sent. +$cmd accumulate_ten_and_orderly_shutdown +count=$(ls -1q "$datapath/sent_pings" | wc -l) +if [[ "$count" -ne 0 ]]; then + echo "test result: FAILED." + exit 101 +fi + +# Third run sends the ping. +$cmd submit_ping +count=$(ls -1q "$datapath/sent_pings" | wc -l) +if [[ "$count" -ne 1 ]]; then + echo "test result: FAILED." + exit 101 +fi + +if ! grep -q '"test.metrics.sample_counter":10' "$datapath"/sent_pings/*; then + echo "test result: FAILED." + exit 101 +fi + +echo "test result: ok." +exit 0 diff --git a/glean-core/rlb/tests/test-thread-crashing.sh b/glean-core/rlb/tests/test-thread-crashing.sh index 67a0f4099c..689d3fcf2f 100755 --- a/glean-core/rlb/tests/test-thread-crashing.sh +++ b/glean-core/rlb/tests/test-thread-crashing.sh @@ -15,7 +15,7 @@ cleanup() { trap cleanup INT ABRT TERM EXIT tmp="${TMPDIR:-/tmp}" -datapath=$(mktemp -d "${tmp}/glean_long_running.XXXX") +datapath=$(mktemp -d "${tmp}/crashing_threads.XXXX") RUSTFLAGS="-C panic=abort" \ RUST_LOG=debug \ @@ -23,10 +23,16 @@ cargo run -p glean --example crashing-threads -- "$datapath" ret=$? count=$(ls -1q "$datapath/pending_pings" | wc -l) -if [[ $ret -eq 0 ]] && [[ "$count" -eq 1 ]]; then +# We expect 2 pending pings: +# - a metrics ping +# - a prototype ping +if [[ $ret -eq 0 ]] && [[ "$count" -eq 2 ]]; then echo "test result: ok." exit 0 else + echo "Assertions:" + echo " ret - expected: 0, was: $ret" + echo " count - expected: 2, was: $count" echo "test result: FAILED." exit 101 fi diff --git a/glean-core/src/database/mod.rs b/glean-core/src/database/mod.rs index 75a068b42e..95778d8f7a 100644 --- a/glean-core/src/database/mod.rs +++ b/glean-core/src/database/mod.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use std::cell::RefCell; use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::fs; @@ -33,6 +34,19 @@ macro_rules! unwrap_or { }; } +macro_rules! measure_commit { + ($this:ident, $expr:expr) => {{ + let now = ::std::time::Instant::now(); + let res = $expr; + let elapsed = now.elapsed(); + if let Ok(elapsed) = elapsed.as_micros().try_into() { + let mut samples = $this.write_timings.borrow_mut(); + samples.push(elapsed); + } + res + }}; +} + /// cbindgen:ignore pub type Rkv = rkv::Rkv; /// cbindgen:ignore @@ -206,6 +220,10 @@ pub struct Database { /// RKV load state rkv_load_state: RkvLoadState, + + /// Times an Rkv write-commit took. + /// Re-applied as samples in a timing distribution later. + pub(crate) write_timings: RefCell>, } impl std::fmt::Debug for Database { @@ -274,6 +292,10 @@ impl Database { None }; + // We are gonna write, so we allocate some capacity upfront. + // The value was chosen at random. + let write_timings = RefCell::new(Vec::with_capacity(64)); + let db = Self { rkv, user_store, @@ -284,6 +306,7 @@ impl Database { ping_lifetime_count: AtomicUsize::new(0), file_size, rkv_load_state, + write_timings, }; db.load_ping_lifetime_data(); @@ -560,7 +583,7 @@ impl Database { let mut writer = self.rkv.write()?; self.get_store(lifetime) .put(&mut writer, final_key, &value)?; - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(()) } @@ -656,7 +679,7 @@ impl Database { bincode::serialize(&new_value).expect("IMPOSSIBLE: Serializing metric failed"); let value = rkv::Value::Blob(&encoded); store.put(&mut writer, final_key, &value)?; - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(()) } @@ -705,7 +728,7 @@ impl Database { } } - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(res?) }) } @@ -756,7 +779,7 @@ impl Database { } return Err(e.into()); } - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(()) }) } @@ -771,7 +794,7 @@ impl Database { pub fn clear_lifetime(&self, lifetime: Lifetime) { let res = self.write_with_store(lifetime, |mut writer, store| { store.clear(&mut writer)?; - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(()) }); @@ -840,7 +863,7 @@ impl Database { // to ping_lifetime_data. store.put(&mut writer, key, &rkv::Value::Blob(&encoded))?; } - writer.commit()?; + measure_commit!(self, writer.commit())?; Ok(()) })?; } diff --git a/glean-core/src/debug.rs b/glean-core/src/debug.rs index 88f807bd88..1fd02a2b7f 100644 --- a/glean-core/src/debug.rs +++ b/glean-core/src/debug.rs @@ -17,11 +17,11 @@ //! * **Debug tagging** - Adding the X-Debug-ID header to every ping request, //! allowing these tagged pings to be sent to the ["Ping Debug Viewer"](https://mozilla.github.io/glean/book/dev/core/internal/debug-pings.html). //! This may be set by calling glean.set_debug_view_tag(value: &str) -//! or by setting the environment variable GLEAN_DEBUG_VIEW_TAG=; +//! or by setting the environment variable `GLEAN_DEBUG_VIEW_TAG=`; //! * **Source tagging** - Adding the X-Source-Tags header to every ping request, //! allowing pings to be tagged with custom labels. -//! This may be set by calling glean.set_source_tags(value: Vec) -//! or by setting the environment variable GLEAN_SOURCE_TAGS=; +//! This may be set by calling `glean.set_source_tags(value: Vec)` +//! or by setting the environment variable `GLEAN_SOURCE_TAGS=`; //! //! Bindings may implement other debugging features, e.g. sending pings on demand. diff --git a/glean-core/src/glean.udl b/glean-core/src/glean.udl index 47affec13c..a94e0d54dc 100644 --- a/glean-core/src/glean.udl +++ b/glean-core/src/glean.udl @@ -389,8 +389,16 @@ interface StringMetric { i32 test_get_num_recorded_errors(ErrorType error); }; +[Enum] +interface LabeledMetricData { + Common(CommonMetricData cmd); + CustomDistribution(CommonMetricData cmd, i64 range_min, i64 range_max, i64 bucket_count, HistogramType histogram_type); + MemoryDistribution(CommonMetricData cmd, MemoryUnit unit); + TimingDistribution(CommonMetricData cmd, TimeUnit unit); +}; + interface LabeledCounter { - constructor(CommonMetricData meta, sequence? labels); + constructor(LabeledMetricData meta, sequence? labels); CounterMetric get(string label); @@ -398,7 +406,7 @@ interface LabeledCounter { }; interface LabeledBoolean { - constructor(CommonMetricData meta, sequence? labels); + constructor(LabeledMetricData meta, sequence? labels); BooleanMetric get(string label); @@ -406,7 +414,7 @@ interface LabeledBoolean { }; interface LabeledString { - constructor(CommonMetricData meta, sequence? labels); + constructor(LabeledMetricData meta, sequence? labels); StringMetric get(string label); diff --git a/glean-core/src/internal_metrics.rs b/glean-core/src/internal_metrics.rs index 3703417466..6ea0be1e1c 100644 --- a/glean-core/src/internal_metrics.rs +++ b/glean-core/src/internal_metrics.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; -use super::{metrics::*, CommonMetricData, Lifetime}; +use super::{metrics::*, CommonMetricData, LabeledMetricData, Lifetime}; #[derive(Debug)] pub struct CoreMetrics { @@ -82,13 +82,15 @@ impl AdditionalMetrics { }), pings_submitted: LabeledMetric::::new( - CommonMetricData { - name: "pings_submitted".into(), - category: "glean.validation".into(), - send_in_pings: vec!["metrics".into(), "baseline".into()], - lifetime: Lifetime::Ping, - disabled: false, - dynamic_label: None, + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "pings_submitted".into(), + category: "glean.validation".into(), + send_in_pings: vec!["metrics".into(), "baseline".into()], + lifetime: Lifetime::Ping, + disabled: false, + dynamic_label: None, + }, }, None, ), @@ -154,13 +156,15 @@ impl UploadMetrics { pub fn new() -> UploadMetrics { UploadMetrics { ping_upload_failure: LabeledMetric::::new( - CommonMetricData { - name: "ping_upload_failure".into(), - category: "glean.upload".into(), - send_in_pings: vec!["metrics".into()], - lifetime: Lifetime::Ping, - disabled: false, - dynamic_label: None, + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "ping_upload_failure".into(), + category: "glean.upload".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Ping, + disabled: false, + dynamic_label: None, + }, }, Some(vec![ Cow::from("status_code_4xx"), @@ -264,6 +268,9 @@ pub struct DatabaseMetrics { /// RKV's load result, indicating success or relaying the detected error. pub rkv_load_error: StringMetric, + + /// The time it takes for a write-commit for the Glean database. + pub write_time: TimingDistributionMetric, } impl DatabaseMetrics { @@ -289,6 +296,18 @@ impl DatabaseMetrics { disabled: false, dynamic_label: None, }), + + write_time: TimingDistributionMetric::new( + CommonMetricData { + name: "write_time".into(), + category: "glean.database".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Ping, + disabled: false, + dynamic_label: None, + }, + TimeUnit::Microsecond, + ), } } } diff --git a/glean-core/src/lib.rs b/glean-core/src/lib.rs index 2649e59e75..ad38e83ddc 100644 --- a/glean-core/src/lib.rs +++ b/glean-core/src/lib.rs @@ -63,7 +63,9 @@ pub use crate::error::{Error, ErrorKind, Result}; pub use crate::error_recording::{test_get_num_recorded_errors, ErrorType}; pub use crate::histogram::HistogramType; pub use crate::metrics::labeled::{ - AllowLabeled, LabeledBoolean, LabeledCounter, LabeledMetric, LabeledString, + AllowLabeled, LabeledBoolean, LabeledCounter, LabeledCustomDistribution, + LabeledMemoryDistribution, LabeledMetric, LabeledMetricData, LabeledString, + LabeledTimingDistribution, }; pub use crate::metrics::{ BooleanMetric, CounterMetric, CustomDistributionMetric, Datetime, DatetimeMetric, @@ -129,7 +131,7 @@ pub struct InternalConfiguration { pub log_level: Option, /// The rate at which pings may be uploaded before they are throttled. pub rate_limit: Option, - /// (Experimental) Whether to add a wallclock timestamp to all events. + /// Whether to add a wallclock timestamp to all events. pub enable_event_timestamps: bool, /// An experimentation identifier derived by the application to be sent with all pings, it should /// be noted that this has an underlying StringMetric and so should conform to the limitations that @@ -310,7 +312,7 @@ pub trait OnGleanEvents: Send { } /// A callback handler that receives the base identifier of recorded events -/// The identifier is in the format: . +/// The identifier is in the format: `.` pub trait GleanEventListener: Send { /// Called when an event is recorded, indicating the id of the event fn on_event_recorded(&self, id: String); diff --git a/glean-core/src/lib_unit_tests.rs b/glean-core/src/lib_unit_tests.rs index 58a9b4eed9..635ba39736 100644 --- a/glean-core/src/lib_unit_tests.rs +++ b/glean-core/src/lib_unit_tests.rs @@ -864,11 +864,13 @@ fn test_set_remote_metric_configuration() { ..Default::default() }); let another_metric = LabeledString::new( - CommonMetricData { - category: "category".to_string(), - name: "labeled_string_metric".to_string(), - send_in_pings: vec!["baseline".to_string()], - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + category: "category".to_string(), + name: "labeled_string_metric".to_string(), + send_in_pings: vec!["baseline".to_string()], + ..Default::default() + }, }, Some(vec!["label1".into()]), ); diff --git a/glean-core/src/metrics/custom_distribution.rs b/glean-core/src/metrics/custom_distribution.rs index c7f3fbc56f..70b7707619 100644 --- a/glean-core/src/metrics/custom_distribution.rs +++ b/glean-core/src/metrics/custom_distribution.rs @@ -43,6 +43,30 @@ impl MetricType for CustomDistributionMetric { fn meta(&self) -> &CommonMetricDataInternal { &self.meta } + + fn with_name(&self, name: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.name = name; + Self { + meta: Arc::new(meta), + range_min: self.range_min, + range_max: self.range_max, + bucket_count: self.bucket_count, + histogram_type: self.histogram_type, + } + } + + fn with_dynamic_label(&self, label: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.dynamic_label = Some(label); + Self { + meta: Arc::new(meta), + range_min: self.range_min, + range_max: self.range_max, + bucket_count: self.bucket_count, + histogram_type: self.histogram_type, + } + } } // IMPORTANT: diff --git a/glean-core/src/metrics/labeled.rs b/glean-core/src/metrics/labeled.rs index f9f6a28880..7b8846e1d1 100644 --- a/glean-core/src/metrics/labeled.rs +++ b/glean-core/src/metrics/labeled.rs @@ -8,7 +8,11 @@ use std::sync::{Arc, Mutex}; use crate::common_metric_data::{CommonMetricData, CommonMetricDataInternal}; use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType}; -use crate::metrics::{BooleanMetric, CounterMetric, Metric, MetricType, StringMetric}; +use crate::histogram::HistogramType; +use crate::metrics::{ + BooleanMetric, CounterMetric, CustomDistributionMetric, MemoryDistributionMetric, MemoryUnit, + Metric, MetricType, StringMetric, TimeUnit, TimingDistributionMetric, +}; use crate::Glean; const MAX_LABELS: usize = 16; @@ -24,6 +28,46 @@ pub type LabeledBoolean = LabeledMetric; /// A labeled string. pub type LabeledString = LabeledMetric; +/// A labeled custom_distribution. +pub type LabeledCustomDistribution = LabeledMetric; + +/// A labeled memory_distribution. +pub type LabeledMemoryDistribution = LabeledMetric; + +/// A labeled timing_distribution. +pub type LabeledTimingDistribution = LabeledMetric; + +/// The metric data needed to construct inner submetrics. +/// +/// Different Labeled metrics require different amounts and kinds of information to +/// be constructed. +pub enum LabeledMetricData { + /// The common case: just a CMD. + #[allow(missing_docs)] + Common { cmd: CommonMetricData }, + /// The custom_distribution-specific case. + #[allow(missing_docs)] + CustomDistribution { + cmd: CommonMetricData, + range_min: i64, + range_max: i64, + bucket_count: i64, + histogram_type: HistogramType, + }, + /// The memory_distribution-specific case. + #[allow(missing_docs)] + MemoryDistribution { + cmd: CommonMetricData, + unit: MemoryUnit, + }, + /// The timing_distribution-specific case. + #[allow(missing_docs)] + TimingDistribution { + cmd: CommonMetricData, + unit: TimeUnit, + }, +} + /// A labeled metric. /// /// Labeled metrics allow to record multiple sub-metrics of the same type under different string labels. @@ -43,8 +87,10 @@ pub struct LabeledMetric { /// /// We wrap it in a private module that is inaccessible outside of this module. mod private { - use crate::{ - metrics::BooleanMetric, metrics::CounterMetric, metrics::StringMetric, CommonMetricData, + use super::LabeledMetricData; + use crate::metrics::{ + BooleanMetric, CounterMetric, CustomDistributionMetric, MemoryDistributionMetric, + StringMetric, TimingDistributionMetric, }; /// The sealed labeled trait. @@ -54,24 +100,66 @@ mod private { /// `Labeled` trait. pub trait Sealed { /// Create a new `glean_core` metric from the metadata. - fn new_inner(meta: crate::CommonMetricData) -> Self; + fn new_inner(meta: LabeledMetricData) -> Self; } impl Sealed for CounterMetric { - fn new_inner(meta: CommonMetricData) -> Self { - Self::new(meta) + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::Common { cmd } => Self::new(cmd), + _ => panic!("Incorrect construction of Labeled"), + } } } impl Sealed for BooleanMetric { - fn new_inner(meta: CommonMetricData) -> Self { - Self::new(meta) + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::Common { cmd } => Self::new(cmd), + _ => panic!("Incorrect construction of Labeled"), + } } } impl Sealed for StringMetric { - fn new_inner(meta: CommonMetricData) -> Self { - Self::new(meta) + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::Common { cmd } => Self::new(cmd), + _ => panic!("Incorrect construction of Labeled"), + } + } + } + + impl Sealed for CustomDistributionMetric { + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::CustomDistribution { + cmd, + range_min, + range_max, + bucket_count, + histogram_type, + } => Self::new(cmd, range_min, range_max, bucket_count, histogram_type), + _ => panic!("Incorrect construction of Labeled"), + } + } + } + + impl Sealed for MemoryDistributionMetric { + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::MemoryDistribution { cmd, unit } => Self::new(cmd, unit), + _ => panic!("Incorrect construction of Labeled"), + } + } + } + + impl Sealed for TimingDistributionMetric { + fn new_inner(meta: LabeledMetricData) -> Self { + match meta { + LabeledMetricData::TimingDistribution { cmd, unit } => Self::new(cmd, unit), + _ => panic!("Incorrect construction of Labeled"), + } } } } @@ -79,7 +167,7 @@ mod private { /// Trait for metrics that can be nested inside a labeled metric. pub trait AllowLabeled: MetricType { /// Create a new labeled metric. - fn new_labeled(meta: CommonMetricData) -> Self; + fn new_labeled(meta: LabeledMetricData) -> Self; } // Implement the trait for everything we marked as allowed. @@ -88,7 +176,7 @@ where T: MetricType, T: private::Sealed, { - fn new_labeled(meta: CommonMetricData) -> Self { + fn new_labeled(meta: LabeledMetricData) -> Self { T::new_inner(meta) } } @@ -100,7 +188,10 @@ where /// Creates a new labeled metric from the given metric instance and optional list of labels. /// /// See [`get`](LabeledMetric::get) for information on how static or dynamic labels are handled. - pub fn new(meta: CommonMetricData, labels: Option>>) -> LabeledMetric { + pub fn new( + meta: LabeledMetricData, + labels: Option>>, + ) -> LabeledMetric { let submetric = T::new_labeled(meta); LabeledMetric::new_inner(submetric, labels) } diff --git a/glean-core/src/metrics/memory_distribution.rs b/glean-core/src/metrics/memory_distribution.rs index 7b5e5ee192..8d04bd03c9 100644 --- a/glean-core/src/metrics/memory_distribution.rs +++ b/glean-core/src/metrics/memory_distribution.rs @@ -53,6 +53,24 @@ impl MetricType for MemoryDistributionMetric { fn meta(&self) -> &CommonMetricDataInternal { &self.meta } + + fn with_name(&self, name: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.name = name; + Self { + meta: Arc::new(meta), + memory_unit: self.memory_unit, + } + } + + fn with_dynamic_label(&self, label: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.dynamic_label = Some(label); + Self { + meta: Arc::new(meta), + memory_unit: self.memory_unit, + } + } } // IMPORTANT: diff --git a/glean-core/src/metrics/mod.rs b/glean-core/src/metrics/mod.rs index 9234fff2d1..73282c7c2a 100644 --- a/glean-core/src/metrics/mod.rs +++ b/glean-core/src/metrics/mod.rs @@ -52,7 +52,10 @@ pub use self::datetime::DatetimeMetric; pub use self::denominator::DenominatorMetric; pub use self::event::EventMetric; pub(crate) use self::experiment::ExperimentMetric; -pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledString}; +pub use self::labeled::{ + LabeledBoolean, LabeledCounter, LabeledCustomDistribution, LabeledMemoryDistribution, + LabeledMetric, LabeledString, LabeledTimingDistribution, +}; pub use self::memory_distribution::MemoryDistributionMetric; pub use self::memory_unit::MemoryUnit; pub use self::numerator::NumeratorMetric; diff --git a/glean-core/src/metrics/string.rs b/glean-core/src/metrics/string.rs index a56ffab648..c36f33d51c 100644 --- a/glean-core/src/metrics/string.rs +++ b/glean-core/src/metrics/string.rs @@ -13,7 +13,7 @@ use crate::util::truncate_string_at_boundary_with_error; use crate::CommonMetricData; use crate::Glean; -const MAX_LENGTH_VALUE: usize = 100; +const MAX_LENGTH_VALUE: usize = 255; /// A string metric. /// @@ -166,7 +166,7 @@ mod test { dynamic_label: None, }); - let sample_string = "0123456789".repeat(11); + let sample_string = "0123456789".repeat(26); metric.set_sync(&glean, sample_string.clone()); let truncated = truncate_string_at_boundary(sample_string, MAX_LENGTH_VALUE); diff --git a/glean-core/src/metrics/timing_distribution.rs b/glean-core/src/metrics/timing_distribution.rs index 28a47b1125..dc1b383ac3 100644 --- a/glean-core/src/metrics/timing_distribution.rs +++ b/glean-core/src/metrics/timing_distribution.rs @@ -85,6 +85,28 @@ impl MetricType for TimingDistributionMetric { fn meta(&self) -> &CommonMetricDataInternal { &self.meta } + + fn with_name(&self, name: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.name = name; + Self { + meta: Arc::new(meta), + time_unit: self.time_unit, + next_id: Arc::new(AtomicUsize::new(1)), + start_times: Arc::new(Mutex::new(Default::default())), + } + } + + fn with_dynamic_label(&self, label: String) -> Self { + let mut meta = (*self.meta).clone(); + meta.inner.dynamic_label = Some(label); + Self { + meta: Arc::new(meta), + time_unit: self.time_unit, + next_id: Arc::new(AtomicUsize::new(1)), + start_times: Arc::new(Mutex::new(Default::default())), + } + } } // IMPORTANT: diff --git a/glean-core/src/ping/mod.rs b/glean-core/src/ping/mod.rs index 8af4cd27f4..f6bea44a1e 100644 --- a/glean-core/src/ping/mod.rs +++ b/glean-core/src/ping/mod.rs @@ -234,8 +234,20 @@ impl PingMaker { url_path: &'a str, ) -> Option> { info!("Collecting {}", ping.name()); + let database = glean.storage(); + + // HACK: Only for metrics pings we add the ping timings. + // But we want that to persist until the next metrics ping is actually sent. + let write_samples = database.write_timings.replace(Vec::with_capacity(64)); + if !write_samples.is_empty() { + glean + .database_metrics + .write_time + .accumulate_samples_sync(glean, &write_samples); + } + + let mut metrics_data = StorageManager.snapshot_as_json(database, ping.name(), true); - let mut metrics_data = StorageManager.snapshot_as_json(glean.storage(), ping.name(), true); let events_data = glean .event_storage() .snapshot_as_json(glean, ping.name(), true); diff --git a/glean-core/src/scheduler.rs b/glean-core/src/scheduler.rs index 30fc956e25..d906de9160 100644 --- a/glean-core/src/scheduler.rs +++ b/glean-core/src/scheduler.rs @@ -202,7 +202,7 @@ fn start_scheduler( let mut now = now; loop { let dur = when.until(now); - log::info!("Scheduling for {:?} after {}, reason {:?}", dur, now, when); + log::info!("Scheduling for {} after {:?}, reason {:?}", now, dur, when); let mut timed_out = false; { match condvar.wait_timeout_while(cancelled_lock.lock().unwrap(), dur, |cancelled| !*cancelled) { diff --git a/glean-core/tests/labeled.rs b/glean-core/tests/labeled.rs index 63a7f2ccc2..cf848728fb 100644 --- a/glean-core/tests/labeled.rs +++ b/glean-core/tests/labeled.rs @@ -10,19 +10,21 @@ use serde_json::json; use glean_core::metrics::*; use glean_core::storage::StorageManager; use glean_core::{test_get_num_recorded_errors, ErrorType}; -use glean_core::{CommonMetricData, Lifetime}; +use glean_core::{CommonMetricData, HistogramType, LabeledMetricData, Lifetime}; #[test] fn can_create_labeled_counter_metric() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, Some(vec!["label1".into()]), ); @@ -48,13 +50,15 @@ fn can_create_labeled_counter_metric() { fn can_create_labeled_string_metric() { let (glean, _t) = new_glean(None); let labeled = LabeledString::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, Some(vec!["label1".into()]), ); @@ -80,13 +84,15 @@ fn can_create_labeled_string_metric() { fn can_create_labeled_bool_metric() { let (glean, _t) = new_glean(None); let labeled = LabeledBoolean::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, Some(vec!["label1".into()]), ); @@ -108,17 +114,127 @@ fn can_create_labeled_bool_metric() { ); } +#[test] +fn can_create_labeled_custom_distribution_metric() { + let (glean, _t) = new_glean(None); + let labeled = LabeledCustomDistribution::new( + LabeledMetricData::CustomDistribution { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, + range_min: 0, + range_max: 1024, + bucket_count: 1, + histogram_type: HistogramType::Linear, + }, + Some(vec!["label1".into()]), + ); + + let metric = labeled.get("label1"); + metric.accumulate_samples_sync(&glean, &[42]); + + let snapshot = StorageManager + .snapshot_as_json(glean.storage(), "store1", true) + .unwrap(); + + assert_eq!( + json!({ + "labeled_custom_distribution": { + "telemetry.labeled_metric": { "label1": { "sum": 42, "values": {"0": 1} } } + } + }), + snapshot + ); +} + +#[test] +fn can_create_labeled_memory_distribution_metric() { + let (glean, _t) = new_glean(None); + let labeled = LabeledMemoryDistribution::new( + LabeledMetricData::MemoryDistribution { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, + unit: MemoryUnit::Byte, + }, + Some(vec!["label1".into()]), + ); + + let metric = labeled.get("label1"); + metric.accumulate_samples_sync(&glean, vec![42]); + + let snapshot = StorageManager + .snapshot_as_json(glean.storage(), "store1", true) + .unwrap(); + + assert_eq!( + json!({ + "labeled_memory_distribution": { + "telemetry.labeled_metric": { "label1": { "sum": 42, "values": {"41": 1, "43": 0} } } + } + }), + snapshot + ); +} + +#[test] +fn can_create_labeled_timing_distribution_metric() { + let (glean, _t) = new_glean(None); + let labeled = LabeledTimingDistribution::new( + LabeledMetricData::TimingDistribution { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, + unit: TimeUnit::Nanosecond, + }, + Some(vec!["label1".into()]), + ); + + let metric = labeled.get("label1"); + metric.accumulate_samples_sync(&glean, &[42]); + + let snapshot = StorageManager + .snapshot_as_json(glean.storage(), "store1", true) + .unwrap(); + + assert_eq!( + json!({ + "labeled_timing_distribution": { + "telemetry.labeled_metric": { "label1": { "sum": 42, "values": {"41": 1, "45": 0} } } + } + }), + snapshot + ); +} + #[test] fn can_use_multiple_labels() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -150,19 +266,21 @@ fn can_use_multiple_labels() { fn can_record_error_for_submetric() { let (glean, _t) = new_glean(None); let labeled = LabeledString::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, Some(vec!["label1".into()]), ); let metric = labeled.get("label1"); - metric.set_sync(&glean, "01234567890".repeat(20)); + metric.set_sync(&glean, "01234567890".repeat(26)); // Make sure that the errors have been recorded assert_eq!( @@ -175,13 +293,15 @@ fn can_record_error_for_submetric() { fn labels_are_checked_against_static_list() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, Some(vec!["label1".into(), "label2".into()]), ); @@ -220,13 +340,15 @@ fn labels_are_checked_against_static_list() { fn dynamic_labels_too_long() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -255,13 +377,15 @@ fn dynamic_labels_too_long() { fn dynamic_labels_regex_mismatch() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -294,13 +418,15 @@ fn dynamic_labels_regex_mismatch() { fn dynamic_labels_regex_allowed() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -349,13 +475,15 @@ fn seen_labels_get_reloaded_from_disk() { tempdir = dir; let labeled = LabeledCounter::new( - CommonMetricData { - name: "labeled_metric".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "labeled_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -416,13 +544,15 @@ fn seen_labels_get_reloaded_from_disk() { fn caching_metrics_with_dynamic_labels() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "cached_labels".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "cached_labels".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); @@ -450,13 +580,15 @@ fn caching_metrics_with_dynamic_labels() { fn caching_metrics_with_dynamic_labels_across_pings() { let (glean, _t) = new_glean(None); let labeled = LabeledCounter::new( - CommonMetricData { - name: "cached_labels2".into(), - category: "telemetry".into(), - send_in_pings: vec!["store1".into()], - disabled: false, - lifetime: Lifetime::Ping, - ..Default::default() + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "cached_labels2".into(), + category: "telemetry".into(), + send_in_pings: vec!["store1".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }, }, None, ); diff --git a/glean-core/tests/ping.rs b/glean-core/tests/ping.rs index 42f93e08c7..8c84db9835 100644 --- a/glean-core/tests/ping.rs +++ b/glean-core/tests/ping.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use glean_core::metrics::*; use glean_core::CommonMetricData; +use glean_core::LabeledMetricData; use glean_core::Lifetime; #[test] @@ -137,13 +138,15 @@ fn test_pings_submitted_metric() { // Reconstructed here so we can test it without reaching into the library // internals. let pings_submitted = LabeledCounter::new( - CommonMetricData { - name: "pings_submitted".into(), - category: "glean.validation".into(), - send_in_pings: vec!["metrics".into(), "baseline".into()], - lifetime: Lifetime::Ping, - disabled: false, - dynamic_label: None, + LabeledMetricData::Common { + cmd: CommonMetricData { + name: "pings_submitted".into(), + category: "glean.validation".into(), + send_in_pings: vec!["metrics".into(), "baseline".into()], + lifetime: Lifetime::Ping, + disabled: false, + dynamic_label: None, + }, }, None, ); @@ -279,3 +282,32 @@ fn test_scheduled_pings_are_sent() { assert!(trigger_ping.submit_sync(&glean, None)); assert_eq!(2, get_queued_pings(glean.get_data_path()).unwrap().len()); } + +#[test] +fn database_write_timings_get_recorded() { + let (mut glean, _t) = new_glean(None); + + let metrics_ping = PingType::new("metrics", true, false, true, true, true, vec![], vec![]); + glean.register_ping_type(&metrics_ping); + + // We need to store a metric to record something. + let counter = CounterMetric::new(CommonMetricData { + name: "counter".into(), + category: "local".into(), + send_in_pings: vec!["metrics".into()], + ..Default::default() + }); + counter.add_sync(&glean, 1); + + assert!(metrics_ping.submit_sync(&glean, None)); + + let mut queued_pings = get_queued_pings(glean.get_data_path()).unwrap(); + assert_eq!(1, queued_pings.len(), "missing metrics ping"); + + let json = queued_pings.pop().unwrap().1; + let write_time = &json["metrics"]["timing_distribution"]["glean.database.write_time"]; + assert!( + 0 < write_time["sum"].as_i64().unwrap(), + "writing should take some time" + ); +} diff --git a/glean-core/tests/string.rs b/glean-core/tests/string.rs index 3ccfa8f494..0daa6e6e8c 100644 --- a/glean-core/tests/string.rs +++ b/glean-core/tests/string.rs @@ -104,12 +104,12 @@ fn long_string_values_are_truncated() { ..Default::default() }); - let test_sting = "01234567890".repeat(20); - metric.set_sync(&glean, test_sting.clone()); + let test_string = "01234567890".repeat(26); + metric.set_sync(&glean, test_string.clone()); // Check that data was truncated assert_eq!( - test_sting[..100], + test_string[..255], metric.get_value(&glean, "store1").unwrap() ); diff --git a/gradle-plugin/README.md b/gradle-plugin/README.md index 9ce43e9708..9d1c4b79a9 100644 --- a/gradle-plugin/README.md +++ b/gradle-plugin/README.md @@ -1,5 +1,5 @@ This directory contains a Gradle plugin for build-time functionality, such as generating specific metrics and documentation. -See `docs/user/adding-glean-to-your-project/index.md` for information about using this +See `docs/user/user/adding-glean-to-your-project/index.md` for information about using this plugin. diff --git a/gradle-plugin/src/main/groovy/mozilla/telemetry/glean-gradle-plugin/GleanGradlePlugin.groovy b/gradle-plugin/src/main/groovy/mozilla/telemetry/glean-gradle-plugin/GleanGradlePlugin.groovy index 6b19a2e680..d43f7c6a69 100644 --- a/gradle-plugin/src/main/groovy/mozilla/telemetry/glean-gradle-plugin/GleanGradlePlugin.groovy +++ b/gradle-plugin/src/main/groovy/mozilla/telemetry/glean-gradle-plugin/GleanGradlePlugin.groovy @@ -50,7 +50,7 @@ abstract class GleanMetricsYamlTransform implements TransformAction { // The version of glean_parser to install from PyPI. - private String GLEAN_PARSER_VERSION = "14.0" + private String GLEAN_PARSER_VERSION = "14.3" // The version of Miniconda is explicitly specified. // Miniconda3-4.5.12 is known to not work on Windows. private String MINICONDA_VERSION = "24.3.0-0" @@ -552,7 +552,7 @@ except: void apply(Project project) { isOffline = project.gradle.startParameter.offline - project.ext.glean_version = "60.3.0" + project.ext.glean_version = "60.4.0" def parserVersion = gleanParserVersion(project) // Print the required glean_parser version to the console. This is @@ -560,11 +560,16 @@ except: // builds. println("Requires glean_parser ${parserVersion}") - File envDir = setupPythonEnvironmentTasks(project, parserVersion) - // Store in both gleanCondaDir (for backward compatibility reasons) and - // the more accurate gleanPythonEnvDir variables. - project.ext.set("gleanCondaDir", envDir) - project.ext.set("gleanPythonEnvDir", envDir) + File envDir + if (project.ext.has("gleanPythonEnvDir")) { + envDir = new File(project.ext.gleanPythonEnvDir) + isOffline = true + } else { + envDir = setupPythonEnvironmentTasks(project, parserVersion) + project.ext.set("gleanPythonEnvDir", envDir) + } + // Also store in gleanCondaDir for backward compatibility reasons + project.ext.set("gleanCondaDir", project.ext.gleanPythonEnvDir) setupExtractMetricsFromAARTasks(project) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4c903f8d9f..21db8eaa07 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ android-plugin = "8.0.2" # Kotlin -kotlin-compiler = "1.9.23" +kotlin-compiler = "1.9.24" kotlinx-coroutines = "1.8.0" kotlinx-serialization = "1.6.3" @@ -16,9 +16,9 @@ kotlinx-serialization = "1.6.3" rust-android-gradle = "0.9.4" # AndroidX -androidx-annotation = "1.7.1" +androidx-annotation = "1.8.0" androidx-appcompat = "1.6.1" -androidx-browser = "1.7.0" +androidx-browser = "1.8.0" androidx-lifecycle = "2.7.0" androidx-work = "2.9.0" @@ -26,19 +26,19 @@ androidx-work = "2.9.0" jna = "5.14.0" # Linting and Static Analysis -detekt = "1.23.5" +detekt = "1.23.6" ktlint = "0.50.0" # AndroidX Testing -androidx-test-espresso = "3.5.1" -androidx-test-core = "1.5.0" -androidx-test-junit = "1.1.5" -androidx-test-runner = "1.5.2" +androidx-test-core = "1.6.1" +androidx-test-espresso = "3.6.1" +androidx-test-junit = "1.2.1" +androidx-test-runner = "1.6.1" androidx-test-uiautomator = "2.3.0" # Third Party Testing junit = "4.13.2" -mockito = "5.11.0" +mockito = "5.12.0" mockwebserver = "4.12.0" robolectric = "4.12.1" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49177..e6441136f3 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5c..a4413138c9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..b740cf1339 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/pyproject.toml b/pyproject.toml index 0edcf4df7c..48c0414f29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "glean-sdk" -version = "60.3.0" +version = "60.4.0" requires-python = ">=3.8" classifiers = [ "Intended Audience :: Developers", @@ -22,7 +22,7 @@ maintainers = [ dependencies = [ "semver>=2.13.0", - "glean_parser~=14.0", + "glean_parser~=14.3", ] [project.urls] diff --git a/samples/android/app/metrics.yaml b/samples/android/app/metrics.yaml index accd16f9c7..6597c8ef96 100644 --- a/samples/android/app/metrics.yaml +++ b/samples/android/app/metrics.yaml @@ -198,3 +198,20 @@ party: type: string diameter: type: number + animals: + type: object + description: > + Contains a list of party animals + notification_emails: + - CHANGE-ME@example.com + bugs: + - https://bugzilla.mozilla.org/TODO + data_reviews: + - http://example.com/reviews + data_sensitivity: + - technical + expires: never + structure: + type: array + items: + type: string diff --git a/samples/android/app/src/main/java/org/mozilla/samples/gleancore/MainActivity.kt b/samples/android/app/src/main/java/org/mozilla/samples/gleancore/MainActivity.kt index ebf66e8e34..33530d617b 100644 --- a/samples/android/app/src/main/java/org/mozilla/samples/gleancore/MainActivity.kt +++ b/samples/android/app/src/main/java/org/mozilla/samples/gleancore/MainActivity.kt @@ -52,6 +52,11 @@ open class MainActivity : AppCompatActivity() { balloons.add(Party.BalloonsObjectItem(colour = "green")) Party.balloons.set(balloons) + val animals = Party.AnimalsObject() + animals.add("Dog") + animals.add("Cat") + Party.animals.set(animals) + // This is referencing the event ping named 'click' from the metrics.yaml file. In // order to illustrate adding extra information to the event, it is also adding to the // 'extras' field a dictionary of values. Note that the dictionary keys must be diff --git a/samples/ios/app/glean-sample-app/ViewController.swift b/samples/ios/app/glean-sample-app/ViewController.swift index f4a02e48d2..82a6ed4d6c 100644 --- a/samples/ios/app/glean-sample-app/ViewController.swift +++ b/samples/ios/app/glean-sample-app/ViewController.swift @@ -63,6 +63,11 @@ class ViewController: UIViewController { balloons.append(Party.BalloonsObjectItem(colour: "green")) Party.balloons.set(balloons) + var animals: Party.AnimalsObject = [] + animals.append("Dog") + animals.append("Cat") + Party.animals.set(animals) + // This is referencing the event ping named 'click' from the metrics.yaml file. In // order to illustrate adding extra information to the event, it is also adding to the // 'extras' field a dictionary of values. Note that the dictionary keys must be diff --git a/samples/ios/app/metrics.yaml b/samples/ios/app/metrics.yaml index c2ac64a910..6302504feb 100644 --- a/samples/ios/app/metrics.yaml +++ b/samples/ios/app/metrics.yaml @@ -187,3 +187,21 @@ party: type: string diameter: type: number + + animals: + type: object + description: > + Contains a list of party animals + notification_emails: + - CHANGE-ME@example.com + bugs: + - https://bugzilla.mozilla.org/TODO + data_reviews: + - http://example.com/reviews + data_sensitivity: + - technical + expires: never + structure: + type: array + items: + type: string diff --git a/samples/rust/metrics.yaml b/samples/rust/metrics.yaml index f1cbf928ea..367a437c84 100644 --- a/samples/rust/metrics.yaml +++ b/samples/rust/metrics.yaml @@ -25,6 +25,49 @@ test.metrics: send_in_pings: - prototype + sample_labeled_counter: &defaults + type: labeled_counter + description: | + Just testing labeled_counter. + bugs: + - https://bugzilla.mozilla.org/1907991 + data_reviews: + - N/A + notification_emails: + - nobody@example.com + expires: never + send_in_pings: + - prototype + no_lint: + - COMMON_PREFIX + + sample_labeled_custom_distribution: + <<: *defaults + type: labeled_custom_distribution + range_min: 0 + range_max: 100 + bucket_count: 10 + histogram_type: linear + labels: + - aLabel + - 2label + + sample_labeled_memory_distribution: + <<: *defaults + type: labeled_memory_distribution + memory_unit: kilobyte + labels: + - aLabel + - 2label + + sample_labeled_timing_distribution: + <<: *defaults + type: labeled_timing_distribution + time_unit: millisecond + labels: + - aLabel + - 2label + party: balloons: type: object diff --git a/taskcluster/kinds/android-build/kind.yml b/taskcluster/kinds/android-build/kind.yml index c030e40b39..667bdc90b2 100644 --- a/taskcluster/kinds/android-build/kind.yml +++ b/taskcluster/kinds/android-build/kind.yml @@ -33,7 +33,7 @@ tasks: # and the gradle command. Another options could be to set those env vars # here like: [export, 'PATH=$HOME/.cargo/bin:$PATH' - [source, taskcluster/scripts/rustup-setup.sh] - - [bash, '-c', 'echo "rust.targets=linux-x86-64\n" > local.properties'] + - [bash, '-c', 'echo "rust.targets=linux-x86-64,arm64\n" > local.properties'] gradlew: - 'clean' - 'assembleDebugUnitTest'