From a041c2e2be54cdb92ecd68f14a25989bc23d559c Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Mon, 3 Aug 2026 15:49:53 +0200 Subject: [PATCH 1/8] Implement labeled {custom|memory|timing} distribution for Android --- .../telemetry/glean/private/Aliases.kt | 15 ++ .../private/CustomDistributionMetricType.kt | 26 +- .../glean/private/LabeledMetricType.kt | 68 ++++-- .../private/MemoryDistributionMetricType.kt | 17 +- .../private/TimingDistributionMetricType.kt | 17 +- .../AccumulationsBeforeGleanInitTest.kt | 14 +- .../glean/private/LabeledMetricTypeTest.kt | 231 ++++++++++++------ glean-core/src/glean.udl | 10 + 8 files changed, 284 insertions(+), 114 deletions(-) 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 e9314f8675..ad31eb3d41 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 @@ -63,3 +63,18 @@ 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 + +/** + * The set of data specifically needed to construct labeled memory distribution metric types. + */ +typealias MemoryDistributionLabeledMetricData = mozilla.telemetry.glean.internal.LabeledMetricData.MemoryDistribution + +/** + * The set of data specifically needed to construct labeled timing distribution metric types. + */ +typealias TimingDistributionLabeledMetricData = mozilla.telemetry.glean.internal.LabeledMetricData.TimingDistribution + +/** + * The set of data specifically needed to construct labeled custom distribution metric types. + */ +typealias CustomDistributionLabeledMetricData = mozilla.telemetry.glean.internal.LabeledMetricData.CustomDistribution diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/CustomDistributionMetricType.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/CustomDistributionMetricType.kt index d5fa91648f..ae950181fc 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/CustomDistributionMetricType.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/CustomDistributionMetricType.kt @@ -26,15 +26,23 @@ import mozilla.telemetry.glean.testing.ErrorType * Instances of this class type are automatically generated by the parsers at build time, * allowing developers to record values that were previously registered in the metrics.yaml file. */ -class CustomDistributionMetricType( - meta: CommonMetricData, - rangeMin: Long, - rangeMax: Long, - bucketCount: Long, - histogramType: HistogramType, -) : HistogramBase { - val inner: CustomDistributionMetric by lazy { - CustomDistributionMetric(meta, rangeMin, rangeMax, bucketCount, histogramType) +class CustomDistributionMetricType : HistogramBase { + lateinit var inner: CustomDistributionMetric + + constructor( + meta: CommonMetricData, + rangeMin: Long, + rangeMax: Long, + bucketCount: Long, + histogramType: HistogramType, + ) { + Dispatchers.Delayed.launch { + inner = CustomDistributionMetric(meta, rangeMin, rangeMax, bucketCount, histogramType) + } + } + + constructor(metric: CustomDistributionMetric) { + inner = metric } /** 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 6538ff74aa..7714f5347a 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 @@ -8,11 +8,14 @@ import androidx.annotation.VisibleForTesting import mozilla.telemetry.glean.testing.ErrorType import mozilla.telemetry.glean.internal.LabeledBoolean as InternalLabeledBoolean import mozilla.telemetry.glean.internal.LabeledCounter as InternalLabeledCounter +import mozilla.telemetry.glean.internal.LabeledCustomDistribution as InternalLabeledCustomDistribution +import mozilla.telemetry.glean.internal.LabeledMemoryDistribution as InternalLabeledMemoryDistribution import mozilla.telemetry.glean.internal.LabeledQuantity as InternalLabeledQuantity import mozilla.telemetry.glean.internal.LabeledString as InternalLabeledString +import mozilla.telemetry.glean.internal.LabeledTimingDistribution as InternalLabeledTimingDistribution class LabeledBoolean constructor( - meta: CommonLabeledMetricData, + meta: LabeledMetricData, labels: List?, ) { val metric = InternalLabeledBoolean(meta, labels) @@ -21,7 +24,7 @@ class LabeledBoolean constructor( } class LabeledCounter constructor( - meta: CommonLabeledMetricData, + meta: LabeledMetricData, labels: List?, ) { val metric = InternalLabeledCounter(meta, labels) @@ -30,7 +33,7 @@ class LabeledCounter constructor( } class LabeledQuantity constructor( - meta: CommonLabeledMetricData, + meta: LabeledMetricData, labels: List?, ) { val metric = InternalLabeledQuantity(meta, labels) @@ -39,7 +42,7 @@ class LabeledQuantity constructor( } class LabeledString constructor( - meta: CommonLabeledMetricData, + meta: LabeledMetricData, labels: List?, ) { val metric = InternalLabeledString(meta, labels) @@ -47,6 +50,33 @@ class LabeledString constructor( fun get(label: String): StringMetricType = StringMetricType(metric.get(label)) } +class LabeledMemoryDistribution constructor( + meta: LabeledMetricData, + labels: List?, +) { + val metric = InternalLabeledMemoryDistribution(meta, labels) + + fun get(label: String): MemoryDistributionMetricType = MemoryDistributionMetricType(metric.get(label)) +} + +class LabeledTimingDistribution constructor( + meta: LabeledMetricData, + labels: List?, +) { + val metric = InternalLabeledTimingDistribution(meta, labels) + + fun get(label: String): TimingDistributionMetricType = TimingDistributionMetricType(metric.get(label)) +} + +class LabeledCustomDistribution constructor( + meta: LabeledMetricData, + labels: List?, +) { + val metric = InternalLabeledCustomDistribution(meta, labels) + + fun get(label: String): CustomDistributionMetricType = CustomDistributionMetricType(metric.get(label)) +} + /** * This implements the developer facing API for labeled metrics. * @@ -61,33 +91,22 @@ class LabeledString constructor( */ @Suppress("LongParameterList") class LabeledMetricType( - private val disabled: Boolean, - category: String, - lifetime: Lifetime, - name: String, - private val labels: Set? = null, - private val sendInPings: List, + private val meta: LabeledMetricData, private val subMetric: T, + private val labels: Set? = null, ) { // The inner labeled metric, from which actual metrics are constructed. private val inner: Any init { - val meta = CommonLabeledMetricData( - cmd = CommonMetricData( - category = category, - name = name, - sendInPings = sendInPings, - disabled = disabled, - lifetime = lifetime, - ), - ) - this.inner = when (subMetric) { is CounterMetricType -> LabeledCounter(meta, labels?.toList()) is BooleanMetricType -> LabeledBoolean(meta, labels?.toList()) is StringMetricType -> LabeledString(meta, labels?.toList()) is QuantityMetricType -> LabeledQuantity(meta, labels?.toList()) + is CustomDistributionMetricType -> LabeledCustomDistribution(meta, labels?.toList()) + is MemoryDistributionMetricType -> LabeledMemoryDistribution(meta, labels?.toList()) + is TimingDistributionMetricType -> LabeledTimingDistribution(meta, labels?.toList()) else -> error("Can not create a labeled version of this metric type") } } @@ -116,6 +135,9 @@ class LabeledMetricType( is LabeledBoolean -> this.inner.get(label) as T is LabeledString -> this.inner.get(label) as T is LabeledQuantity -> this.inner.get(label) as T + is LabeledCustomDistribution -> this.inner.get(label) as T + is LabeledMemoryDistribution -> this.inner.get(label) as T + is LabeledTimingDistribution -> this.inner.get(label) as T else -> error("Can not create a labeled version of this metric type") } @@ -153,6 +175,9 @@ class LabeledMetricType( is LabeledBoolean -> this.inner.metric.testGetNumRecordedErrors(errorType) is LabeledString -> this.inner.metric.testGetNumRecordedErrors(errorType) is LabeledQuantity -> this.inner.metric.testGetNumRecordedErrors(errorType) + is LabeledCustomDistribution -> this.inner.metric.testGetNumRecordedErrors(errorType) + is LabeledMemoryDistribution -> this.inner.metric.testGetNumRecordedErrors(errorType) + is LabeledTimingDistribution -> this.inner.metric.testGetNumRecordedErrors(errorType) else -> error("Can not create a labeled version of this metric type") } @@ -173,6 +198,9 @@ class LabeledMetricType( is LabeledCounter -> this.inner.metric.testGetValue(pingName) is LabeledString -> this.inner.metric.testGetValue(pingName) is LabeledQuantity -> this.inner.metric.testGetValue(pingName) + is LabeledCustomDistribution -> this.inner.metric.testGetValue(pingName) + is LabeledMemoryDistribution -> this.inner.metric.testGetValue(pingName) + is LabeledTimingDistribution -> this.inner.metric.testGetValue(pingName) else -> error("Can not create a labeled version of this metric type") }!! } diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/MemoryDistributionMetricType.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/MemoryDistributionMetricType.kt index f4ce088087..5894a33fb7 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/MemoryDistributionMetricType.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/MemoryDistributionMetricType.kt @@ -15,11 +15,18 @@ import mozilla.telemetry.glean.testing.ErrorType * Instances of this class type are automatically generated by the parsers at build time, * allowing developers to record values that were previously registered in the metrics.yaml file. */ -class MemoryDistributionMetricType( - meta: CommonMetricData, - memoryUnit: MemoryUnit, -) : HistogramBase { - val inner: MemoryDistributionMetric by lazy { MemoryDistributionMetric(meta, memoryUnit) } +class MemoryDistributionMetricType : HistogramBase { + lateinit var inner: MemoryDistributionMetric + + constructor(meta: CommonMetricData, memoryUnit: MemoryUnit) { + Dispatchers.Delayed.launch { + inner = MemoryDistributionMetric(meta, memoryUnit) + } + } + + constructor(metric: MemoryDistributionMetric) { + inner = metric + } /** * Accumulates the provided sample in the metric. diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/TimingDistributionMetricType.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/TimingDistributionMetricType.kt index 29cb09eca4..559bae952a 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/private/TimingDistributionMetricType.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/private/TimingDistributionMetricType.kt @@ -16,11 +16,18 @@ import mozilla.telemetry.glean.testing.ErrorType * Instances of this class type are automatically generated by the parsers at build time, * allowing developers to record values that were previously registered in the metrics.yaml file. */ -class TimingDistributionMetricType( - meta: CommonMetricData, - timeUnit: TimeUnit, -) : HistogramBase { - val inner: TimingDistributionMetric by lazy { TimingDistributionMetric(meta, timeUnit) } +class TimingDistributionMetricType : HistogramBase { + lateinit var inner: TimingDistributionMetric + + constructor(meta: CommonMetricData, timeUnit: TimeUnit) { + Dispatchers.Delayed.launch { + inner = TimingDistributionMetric(meta, timeUnit) + } + } + + constructor(metric: TimingDistributionMetric) { + inner = metric + } /** * Starts tracking time for the provided metric. diff --git a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/AccumulationsBeforeGleanInitTest.kt b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/AccumulationsBeforeGleanInitTest.kt index 8d838c4bbe..0918f06e23 100644 --- a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/AccumulationsBeforeGleanInitTest.kt +++ b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/AccumulationsBeforeGleanInitTest.kt @@ -60,11 +60,15 @@ class AccumulationsBeforeGleanInitTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "test.telemetry", - lifetime = Lifetime.APPLICATION, - name = "pre_init_counter", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "test.telemetry", + lifetime = Lifetime.APPLICATION, + name = "pre_init_counter", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) diff --git a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/LabeledMetricTypeTest.kt b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/LabeledMetricTypeTest.kt index d94e1cedd0..f7e4840df2 100644 --- a/glean-core/android/src/test/java/mozilla/telemetry/glean/private/LabeledMetricTypeTest.kt +++ b/glean-core/android/src/test/java/mozilla/telemetry/glean/private/LabeledMetricTypeTest.kt @@ -36,11 +36,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) @@ -70,11 +74,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, labels = setOf("foo", "bar", "baz"), ) @@ -106,11 +114,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) @@ -140,11 +152,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) @@ -180,11 +196,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) @@ -233,11 +253,15 @@ class LabeledMetricTypeTest { ) val labeledBooleanMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_boolean_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_boolean_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = booleanMetric, ) @@ -286,11 +310,15 @@ class LabeledMetricTypeTest { ) val labeledStringMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_string_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_string_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = stringMetric, ) @@ -339,11 +367,15 @@ class LabeledMetricTypeTest { ) val labeledStringMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_string_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_string_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = stringMetric, ) @@ -367,11 +399,15 @@ class LabeledMetricTypeTest { ) val labeledBooleanMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_boolean_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_boolean_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = booleanMetric, ) @@ -395,11 +431,15 @@ class LabeledMetricTypeTest { ) val labeledQuantityMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_quantity_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_quantity_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = quantityMetric, ) @@ -424,11 +464,15 @@ class LabeledMetricTypeTest { ) val labeledEventMetric = LabeledMetricType>( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_event_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_event_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = eventMetric, ) @@ -451,11 +495,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, labels = setOf("foo", "bar", "baz"), ) @@ -493,11 +541,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_nocrash", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_nocrash", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, labels = setOf("foo"), ) @@ -525,11 +577,15 @@ class LabeledMetricTypeTest { ) val labeledCounterMetric = LabeledMetricType( - disabled = false, - category = "telemetry", - lifetime = Lifetime.APPLICATION, - name = "labeled_counter_metric", - sendInPings = listOf("metrics"), + CommonLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_counter_metric", + sendInPings = listOf("metrics"), + ), + ), subMetric = counterMetric, ) @@ -541,4 +597,39 @@ class LabeledMetricTypeTest { assertEquals(1, values["label1"]) assertEquals(2, values["label2"]) } + + @Test + fun `test labeled memory distribution`() { + val metric = MemoryDistributionMetricType( + CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.PING, + name = "memory_distribution", + sendInPings = listOf("store1"), + ), + memoryUnit = MemoryUnit.KILOBYTE, + ) + + val labeledMetric = LabeledMetricType( + MemoryDistributionLabeledMetricData( + cmd = CommonMetricData( + disabled = false, + category = "telemetry", + lifetime = Lifetime.APPLICATION, + name = "labeled_memory_distribution", + sendInPings = listOf("store1"), + ), + unit = MemoryUnit.KILOBYTE, + ), + subMetric = metric, + ) + + labeledMetric["label1"].accumulate(1) + labeledMetric["label2"].accumulate(2) + + val kb = 1024 + assertEquals(1L * kb, labeledMetric["label1"].testGetValue()!!.sum) + assertEquals(2L * kb, labeledMetric["label2"].testGetValue()!!.sum) + } } diff --git a/glean-core/src/glean.udl b/glean-core/src/glean.udl index 128393d09e..62a1b4da8a 100644 --- a/glean-core/src/glean.udl +++ b/glean-core/src/glean.udl @@ -528,6 +528,16 @@ interface LabeledQuantity { record? test_get_value(optional string? ping_name = null); }; +interface LabeledMemoryDistribution { + constructor(LabeledMetricData meta, sequence? labels); + + MemoryDistributionMetric get(string label); + + i32 test_get_num_recorded_errors(ErrorType error); + + record? test_get_value(optional string? ping_name = null); +}; + interface StringListMetric { constructor(CommonMetricData meta); From 535e81247c91a548af29c241a2d8ccd8736b4c60 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Mon, 3 Aug 2026 16:03:42 +0200 Subject: [PATCH 2/8] Implement labeled {custom|memory|timing} distribution for iOS --- .../ios/Glean/Metrics/LabeledMetric.swift | 61 +++-- .../Metrics/LabeledMetricTests.swift | 242 ++++++++++++++---- glean-core/src/glean.udl | 20 ++ samples/ios/app/metrics.yaml | 18 ++ 4 files changed, 269 insertions(+), 72 deletions(-) diff --git a/glean-core/ios/Glean/Metrics/LabeledMetric.swift b/glean-core/ios/Glean/Metrics/LabeledMetric.swift index cfc9984b77..2a6f6dbca7 100644 --- a/glean-core/ios/Glean/Metrics/LabeledMetric.swift +++ b/glean-core/ios/Glean/Metrics/LabeledMetric.swift @@ -13,8 +13,6 @@ /// but records metrics for the underlying metric type `T` in the storage engine for that type. /// The only difference is that labeled metrics are stored with the special key `$category.$name/$label`. public final class LabeledMetricType: @unchecked Sendable { - let disabled: Bool - let sendInPings: [String] let subMetric: T let inner: AnyObject @@ -24,38 +22,30 @@ public final class LabeledMetricType: @unchecked Sendable { /// * `BooleanMetricType` /// * `CounterMetricType` /// * `StringMetricType` - /// * `QuantityMetric` + /// * `QuantityMetricType` + /// * `LabeledCustomDistribution` + /// * `LabeledMemoryDistribution` + /// * `LabeledTimingDistribution` /// /// Throws an exception when used with unsupported sub-metrics. - public init( - category: String, - name: String, - sendInPings: [String], - lifetime: Lifetime, - disabled: Bool, - subMetric: T, - labels: [String]? = nil - ) throws { - let meta = CommonMetricData( - category: category, - name: name, - sendInPings: sendInPings, - lifetime: lifetime, - disabled: disabled - ) - self.disabled = disabled - self.sendInPings = sendInPings + public init(_ meta: LabeledMetricData, subMetric: T, labels: [String]? = nil) throws { self.subMetric = subMetric switch subMetric { case is CounterMetricType: - self.inner = LabeledCounter(.common(cmd: meta), labels) + self.inner = LabeledCounter(meta, labels) case is BooleanMetricType: - self.inner = LabeledBoolean(.common(cmd: meta), labels) + self.inner = LabeledBoolean(meta, labels) case is StringMetricType: - self.inner = LabeledString(.common(cmd: meta), labels) - case is QuantityMetric: - self.inner = LabeledQuantity(.common(cmd: meta), labels) + self.inner = LabeledString(meta, labels) + case is QuantityMetricType: + self.inner = LabeledQuantity(meta, labels) + case is MemoryDistributionMetricType: + self.inner = LabeledMemoryDistribution(meta, labels) + case is TimingDistributionMetricType: + self.inner = LabeledTimingDistribution(meta, labels) + case is CustomDistributionMetricType: + self.inner = LabeledCustomDistribution(meta, labels) default: throw "Can not create a labeled version of this metric type" } @@ -78,7 +68,6 @@ public final class LabeledMetricType: @unchecked Sendable { /// * label: The label /// - returns: The specific metric for that label public subscript(label: String) -> T { - switch self.inner { case is LabeledCounter: return (self.inner as! LabeledCounter).get(label) as! T @@ -88,6 +77,12 @@ public final class LabeledMetricType: @unchecked Sendable { return (self.inner as! LabeledString).get(label) as! T case is LabeledQuantity: return (self.inner as! LabeledQuantity).get(label) as! T + case is LabeledMemoryDistribution: + return (self.inner as! LabeledMemoryDistribution).get(label) as! T + case is LabeledTimingDistribution: + return (self.inner as! LabeledTimingDistribution).get(label) as! T + case is LabeledCustomDistribution: + return (self.inner as! LabeledCustomDistribution).get(label) as! T default: // The constructor will already throw an exception on an unhandled sub-metric type assertUnreachable() @@ -109,6 +104,12 @@ public final class LabeledMetricType: @unchecked Sendable { return (self.inner as! LabeledString).testGetNumRecordedErrors(errorType) case is LabeledQuantity: return (self.inner as! LabeledQuantity).testGetNumRecordedErrors(errorType) + case is LabeledMemoryDistribution: + return (self.inner as! LabeledMemoryDistribution).testGetNumRecordedErrors(errorType) + case is LabeledTimingDistribution: + return (self.inner as! LabeledTimingDistribution).testGetNumRecordedErrors(errorType) + case is LabeledCustomDistribution: + return (self.inner as! LabeledCustomDistribution).testGetNumRecordedErrors(errorType) default: // The constructor will already throw an exception on an unhandled sub-metric type assertUnreachable() @@ -133,6 +134,12 @@ public final class LabeledMetricType: @unchecked Sendable { return labeled.testGetValue(pingName)! case let labeled as LabeledQuantity: return labeled.testGetValue(pingName)! + case let labeled as LabeledMemoryDistribution: + return labeled.testGetValue(pingName)! + case let labeled as LabeledTimingDistribution: + return labeled.testGetValue(pingName)! + case let labeled as LabeledCustomDistribution: + return labeled.testGetValue(pingName)! default: // The constructor will already throw an exception on an unhandled sub-metric type assertUnreachable() diff --git a/glean-core/ios/GleanTests/Metrics/LabeledMetricTests.swift b/glean-core/ios/GleanTests/Metrics/LabeledMetricTests.swift index cbab212efe..2060672287 100644 --- a/glean-core/ios/GleanTests/Metrics/LabeledMetricTests.swift +++ b/glean-core/ios/GleanTests/Metrics/LabeledMetricTests.swift @@ -5,6 +5,7 @@ @testable import Glean import XCTest +// swiftlint:disable type_body_length class LabeledMetricTypeTests: XCTestCase { override func setUp() { resetGleanDiscardingInitialPings(testCase: self, tag: "LabeledMetricTypeTests") @@ -24,11 +25,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledCounterMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric ) @@ -55,11 +60,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledCounterMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric, labels: ["foo", "bar", "baz"] ) @@ -88,11 +97,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledCounterMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric ) @@ -120,11 +133,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledCounterMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric ) @@ -162,11 +179,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledStringMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric ) @@ -187,11 +208,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledBooleanMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_boolean_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_boolean_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: booleanMetric ) @@ -212,11 +237,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledQuantityMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_quantity_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_quantity_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: quantityMetric ) @@ -237,11 +266,15 @@ class LabeledMetricTypeTests: XCTestCase { ), nil) XCTAssertThrowsError(try LabeledMetricType>( - category: "telemetry", - name: "labeled_event_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_event_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: eventMetric )) { error in XCTAssertEqual(error as! String, "Can not create a labeled version of this metric type") @@ -258,11 +291,15 @@ class LabeledMetricTypeTests: XCTestCase { )) let labeledCounterMetric = try! LabeledMetricType( - category: "telemetry", - name: "labeled_counter_metric", - sendInPings: ["metrics"], - lifetime: .application, - disabled: false, + .common( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_counter_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ) + ), subMetric: counterMetric ) @@ -274,4 +311,119 @@ class LabeledMetricTypeTests: XCTestCase { XCTAssertEqual(1, labeledValues["label1"] as! Int32) XCTAssertEqual(2, labeledValues["label2"] as! Int32) } + + func testLabeledMemoryDistribution() { + let metric = MemoryDistributionMetricType( + CommonMetricData( + category: "telemetry", + name: "labeled_memory_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ), + .kilobyte + ) + + let labeledMetric = try! LabeledMetricType( + .memoryDistribution( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_memory_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ), + unit: .kilobyte + ), + subMetric: metric + ) + + labeledMetric["label1"].accumulate(1) + labeledMetric["label2"].accumulate(2) + + let kb = Int64(1024) + + XCTAssertEqual(1 * kb, labeledMetric["label1"].testGetValue()!.sum) + XCTAssertEqual(2 * kb, labeledMetric["label2"].testGetValue()!.sum) + } + + func testLabeledTimingDistribution() { + let metric = TimingDistributionMetricType( + CommonMetricData( + category: "telemetry", + name: "labeled_timing_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ), + .nanosecond + ) + + let labeledMetric = try! LabeledMetricType( + .timingDistribution( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_timing_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ), + unit: .nanosecond + ), + subMetric: metric + ) + + var id = labeledMetric["label1"].start() + labeledMetric["label1"].stopAndAccumulate(id) + + id = labeledMetric["label2"].start() + labeledMetric["label2"].stopAndAccumulate(id) + + id = labeledMetric["label2"].start() + labeledMetric["label2"].stopAndAccumulate(id) + + XCTAssertEqual(1, labeledMetric["label1"].testGetValue()!.count) + XCTAssertEqual(2, labeledMetric["label2"].testGetValue()!.count) + } + + func testLabeledCustomDistribution() { + let metric = CustomDistributionMetricType( + CommonMetricData( + category: "telemetry", + name: "labeled_custom_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ), + 0, + 60000, + 100, + .exponential, + ) + + let labeledMetric = try! LabeledMetricType( + .customDistribution( + cmd: CommonMetricData( + category: "telemetry", + name: "labeled_custom_distribution_metric", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false, + ), + rangeMin: 0, + rangeMax: 60000, + bucketCount: 100, + histogramType: .exponential, + ), + subMetric: metric + ) + + labeledMetric["label1"].accumulateSamples([1]) + labeledMetric["label2"].accumulateSamples([2]) + labeledMetric["label2"].accumulateSamples([3]) + + XCTAssertEqual(1, labeledMetric["label1"].testGetValue()!.count) + XCTAssertEqual(2, labeledMetric["label2"].testGetValue()!.count) + } } +// swiftlint:enable type_body_length diff --git a/glean-core/src/glean.udl b/glean-core/src/glean.udl index 62a1b4da8a..afb6159cf8 100644 --- a/glean-core/src/glean.udl +++ b/glean-core/src/glean.udl @@ -538,6 +538,26 @@ interface LabeledMemoryDistribution { record? test_get_value(optional string? ping_name = null); }; +interface LabeledTimingDistribution { + constructor(LabeledMetricData meta, sequence? labels); + + TimingDistributionMetric get(string label); + + i32 test_get_num_recorded_errors(ErrorType error); + + record? test_get_value(optional string? ping_name = null); +}; + +interface LabeledCustomDistribution { + constructor(LabeledMetricData meta, sequence? labels); + + CustomDistributionMetric get(string label); + + i32 test_get_num_recorded_errors(ErrorType error); + + record? test_get_value(optional string? ping_name = null); +}; + interface StringListMetric { constructor(CommonMetricData meta); diff --git a/samples/ios/app/metrics.yaml b/samples/ios/app/metrics.yaml index fea4eea065..bb30ad290c 100644 --- a/samples/ios/app/metrics.yaml +++ b/samples/ios/app/metrics.yaml @@ -301,3 +301,21 @@ codegen_test: range_max: 100 bucket_count: 100 histogram_type: linear + + labeled_timing_distribution: + <<: *defaults + type: labeled_timing_distribution + time_unit: millisecond + + labeled_memory_distribution: + <<: *defaults + type: labeled_memory_distribution + memory_unit: byte + + labeled_custom_distribution: + <<: *defaults + type: labeled_custom_distribution + range_min: 0 + range_max: 100 + bucket_count: 100 + histogram_type: exponential From 67bc15d30d31f5e20f592ddb15551822fe0f9a01 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 7 Aug 2026 11:19:16 +0200 Subject: [PATCH 3/8] Implement labeled {custom|memory|timing} distribution (and labeled quantity) for Python --- glean-core/python/glean/_loader.py | 37 ++++++++++- glean-core/python/glean/metrics/__init__.py | 6 ++ glean-core/python/glean/metrics/labeled.py | 20 +++++- .../python/tests/metrics/test_labeled.py | 25 ++++++++ samples/python/glean-sample/__main__.py | 11 ++++ samples/python/metrics.yaml | 61 +++++++++++++++++++ 6 files changed, 156 insertions(+), 4 deletions(-) diff --git a/glean-core/python/glean/_loader.py b/glean-core/python/glean/_loader.py index 146c80d8f9..ed3058261e 100644 --- a/glean-core/python/glean/_loader.py +++ b/glean-core/python/glean/_loader.py @@ -32,7 +32,11 @@ "event": metrics.EventMetricType, "labeled_boolean": metrics.LabeledBooleanMetricType, "labeled_counter": metrics.LabeledCounterMetricType, + "labeled_custom_distribution": metrics.LabeledCustomDistributionMetricType, + "labeled_memory_distribution": metrics.LabeledMemoryDistributionMetricType, + "labeled_quantity": metrics.LabeledQuantityMetricType, "labeled_string": metrics.LabeledStringMetricType, + "labeled_timing_distribution": metrics.LabeledTimingDistributionMetricType, "memory_distribution": metrics.MemoryDistributionMetricType, "object": metrics.ObjectMetricType, "ping": metrics.PingType, @@ -92,6 +96,10 @@ gp_metrics.MemoryUnit.megabyte: metrics.MemoryUnit.MEGABYTE, gp_metrics.MemoryUnit.gigabyte: metrics.MemoryUnit.GIGABYTE, }, + "histogram_type": { + gp_metrics.HistogramType.linear: metrics.HistogramType.LINEAR, + gp_metrics.HistogramType.exponential: metrics.HistogramType.EXPONENTIAL, + }, } @@ -289,9 +297,32 @@ def _get_metric_objects( args["label"] = None meta_args, rest = _split_ctor_args(args) if getattr(metric, "labeled", False): - glean_metric = metric_type( - metrics.LabeledMetricData.COMMON(metrics.CommonMetricData(**meta_args)), **rest - ) + if metric.type == "labeled_custom_distribution": + glean_metric = metric_type( + metrics.LabeledMetricData.CUSTOM_DISTRIBUTION( + metrics.CommonMetricData(**meta_args), **rest + ) + ) + elif metric.type == "labeled_timing_distribution": + rest["unit"] = rest["time_unit"] + del rest["time_unit"] + glean_metric = metric_type( + metrics.LabeledMetricData.TIMING_DISTRIBUTION( + metrics.CommonMetricData(**meta_args), **rest + ) + ) + elif metric.type == "labeled_memory_distribution": + rest["unit"] = rest["memory_unit"] + del rest["memory_unit"] + glean_metric = metric_type( + metrics.LabeledMetricData.MEMORY_DISTRIBUTION( + metrics.CommonMetricData(**meta_args), **rest + ) + ) + else: + glean_metric = metric_type( + metrics.LabeledMetricData.COMMON(metrics.CommonMetricData(**meta_args)), **rest + ) else: glean_metric = metric_type(metrics.CommonMetricData(**meta_args), **rest) diff --git a/glean-core/python/glean/metrics/__init__.py b/glean-core/python/glean/metrics/__init__.py index e3fab36301..0b7b00a788 100644 --- a/glean-core/python/glean/metrics/__init__.py +++ b/glean-core/python/glean/metrics/__init__.py @@ -38,8 +38,11 @@ from .labeled import ( LabeledBooleanMetricType, LabeledCounterMetricType, + LabeledCustomDistributionMetricType, + LabeledMemoryDistributionMetricType, LabeledQuantityMetricType, LabeledStringMetricType, + LabeledTimingDistributionMetricType, ) from .ping import PingType from .string import StringMetricType @@ -62,9 +65,12 @@ "EventMetricType", "LabeledBooleanMetricType", "LabeledCounterMetricType", + "LabeledCustomDistributionMetricType", + "LabeledMemoryDistributionMetricType", "LabeledMetricData", "LabeledQuantityMetricType", "LabeledStringMetricType", + "LabeledTimingDistributionMetricType", "Lifetime", "MemoryDistributionMetricType", "MemoryUnit", diff --git a/glean-core/python/glean/metrics/labeled.py b/glean-core/python/glean/metrics/labeled.py index a27073aec7..cb1dddecef 100644 --- a/glean-core/python/glean/metrics/labeled.py +++ b/glean-core/python/glean/metrics/labeled.py @@ -8,9 +8,12 @@ from .._uniffi import LabeledBoolean from .._uniffi import LabeledCounter +from .._uniffi import LabeledCustomDistribution +from .._uniffi import LabeledMemoryDistribution from .._uniffi import LabeledMetricData -from .._uniffi import LabeledString from .._uniffi import LabeledQuantity +from .._uniffi import LabeledString +from .._uniffi import LabeledTimingDistribution from ..testing import ErrorType @@ -107,9 +110,24 @@ class LabeledQuantityMetricType(LabeledMetricBase): _ctor = LabeledQuantity +class LabeledCustomDistributionMetricType(LabeledMetricBase): + _ctor = LabeledCustomDistribution + + +class LabeledMemoryDistributionMetricType(LabeledMetricBase): + _ctor = LabeledMemoryDistribution + + +class LabeledTimingDistributionMetricType(LabeledMetricBase): + _ctor = LabeledTimingDistribution + + __all__ = [ "LabeledBooleanMetricType", "LabeledCounterMetricType", + "LabeledCustomDistributionMetricType", + "LabeledMemoryDistributionMetricType", "LabeledQuantityMetricType", "LabeledStringMetricType", + "LabeledTimingDistributionMetricType", ] diff --git a/glean-core/python/tests/metrics/test_labeled.py b/glean-core/python/tests/metrics/test_labeled.py index 3ba30baec3..bf4c76ee42 100644 --- a/glean-core/python/tests/metrics/test_labeled.py +++ b/glean-core/python/tests/metrics/test_labeled.py @@ -264,3 +264,28 @@ def test_rapidly_recreating_labeled_metrics_does_not_crash(): labeled_counter_metric["foo"].add(1) assert max_attempts == labeled_counter_metric["foo"].test_get_value() + + +def test_labeled_custom_distribution(): + labeled_counter_metric = metrics.LabeledCustomDistributionMetricType( + LabeledMetricData.CUSTOM_DISTRIBUTION( + CommonMetricData( + disabled=False, + category="telemetry", + lifetime=Lifetime.APPLICATION, + name="labeled_counter_metric", + send_in_pings=["metrics"], + label=None, + ), + range_min=0, + range_max=100, + bucket_count=100, + histogram_type=metrics.HistogramType.LINEAR, + ) + ) + + labeled_counter_metric["label1"].accumulate_samples([1, 2]) + labeled_counter_metric["label2"].accumulate_single_sample(3) + + assert 2 == labeled_counter_metric["label1"].test_get_value().count + assert 1 == labeled_counter_metric["label2"].test_get_value().count diff --git a/samples/python/glean-sample/__main__.py b/samples/python/glean-sample/__main__.py index 4880c704d1..f13fd3cecb 100644 --- a/samples/python/glean-sample/__main__.py +++ b/samples/python/glean-sample/__main__.py @@ -44,6 +44,17 @@ ch.append(f) metrics.party.chooser.set(ch) +metrics.codegen_test.quantity.set(1) +metrics.codegen_test.custom_distribution.accumulate_samples([1, 2]) +metrics.codegen_test.memory_distribution.accumulate_samples([1, 2]) +metrics.codegen_test.timing_distribution.accumulate_samples([1, 2]) + + +metrics.codegen_test.labeled_quantity["label"].set(1) +metrics.codegen_test.labeled_custom_distribution["label"].accumulate_samples([1, 2]) +metrics.codegen_test.labeled_memory_distribution["label"].accumulate_samples([1, 2]) +metrics.codegen_test.labeled_timing_distribution["label"].accumulate_samples([1, 2]) + pings.prototype.submit() Glean.shutdown() diff --git a/samples/python/metrics.yaml b/samples/python/metrics.yaml index 8f5c476d04..6ce4be7204 100644 --- a/samples/python/metrics.yaml +++ b/samples/python/metrics.yaml @@ -74,3 +74,64 @@ party: - type: string - type: number - type: boolean + +codegen_test: + counter: &defaults + type: counter + description: | + A metric for codegen testing -- + no extra data, just setting the defaults + bugs: + - https://bugzilla.mozilla.org/TODO + data_reviews: + - http://example.com/reviews + notification_emails: + - CHANGE-ME@example.com + expires: never + send_in_pings: + - sample + + quantity: + <<: *defaults + type: quantity + unit: apples + + timing_distribution: + <<: *defaults + type: timing_distribution + time_unit: millisecond + + memory_distribution: + <<: *defaults + type: memory_distribution + memory_unit: byte + + custom_distribution: + <<: *defaults + type: custom_distribution + range_min: 0 + range_max: 100 + bucket_count: 100 + histogram_type: linear + + labeled_quantity: + <<: *defaults + type: labeled_quantity + + labeled_timing_distribution: + <<: *defaults + type: labeled_timing_distribution + time_unit: millisecond + + labeled_memory_distribution: + <<: *defaults + type: labeled_memory_distribution + memory_unit: byte + + labeled_custom_distribution: + <<: *defaults + type: labeled_custom_distribution + range_min: 0 + range_max: 100 + bucket_count: 100 + histogram_type: exponential From 8b1347badd678aff55d8cf47eb632e91208a5ce0 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Thu, 6 Aug 2026 14:53:27 +0200 Subject: [PATCH 4/8] Documentation for labeled memory distribution --- .../metrics/labeled_memory_distributions.md | 155 ++++++++++++++++-- 1 file changed, 143 insertions(+), 12 deletions(-) diff --git a/docs/user/reference/metrics/labeled_memory_distributions.md b/docs/user/reference/metrics/labeled_memory_distributions.md index e20491ff59..ed0be3d540 100644 --- a/docs/user/reference/metrics/labeled_memory_distributions.md +++ b/docs/user/reference/metrics/labeled_memory_distributions.md @@ -13,10 +13,43 @@ Accumulate the provided sample in the metric. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Memory + +Network.httpUploadBandwidth[http_version].accumulate(requestSize * 8.0 / 1048576.0 / sendTime.seconds) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Memory; + +Network.INSTANCE.httpUploadBandwidth()[http_version].accumulate(requestSize * 8.0 / 1048576.0 / sendTime.seconds); +``` + +
+
+ +```Swift +import Glean + +Network.httpUploadBandwidth[http_version].accumulate(request_size * 8.0 / 1048576.0 / send_time) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.network.http_upload_badwidth[http_version].accumulate(request_size * 8.0 / 1048576.0 / send_time) +``` + +
```Rust @@ -79,10 +112,71 @@ in Rust where it's required. `None` or no argument will default to the first val {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +// Assert the sum of all HTTP2 samples is 42MBps. +assertEquals(42, Network.httpUploadBandwidth[http_version].testGetValue().sum) + +// Assert there's only the one sample +assertEquals(1, Network.httpUploadBandwidth[http_version].testGetValue().count) + +// Buckets are indexed by their lower bound. +assertEquals(1, Network.httpUploadBandwidth[http_version].testGetValue().values[41]) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network; + +// Assert the sum of all HTTP2 samples is 42MBps. +assertEquals(42, Network.INSTANCE.httpUploadBandwidth()[http_version].testGetValue().sum); + +// Assert there's only the one sample +assertEquals(1, Network.INSTANCE.httpUploadBandwidth()[http_version].testGetValue().count); + +// Buckets are indexed by their lower bound. +assertEquals(1, Network.INSTANCE.httpUploadBandwidth()[http_version].testGetValue().values[41]); +``` + +
+
+ +```Swift +import Glean + +// Assert the sum of all HTTP2 samples is 42MBps. +XCTAssertEqual(42, Network.httpUploadBandwidth[http_version].testGetValue().sum) + +// Assert there's only the one sample +XCTAssertEqual(1, Network.httpUploadBandwidth[http_version].testGetValue().count); + +// Buckets are indexed by their lower bound. +XCTAssertEqual(1, Network.httpUploadBandwidth[http_version].testGetValue().values[41]); +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Assert the sum of all HTTP2 samples is 42MBps. +assert 42 == metrics.network.http_upload_bandwidth.get("h2").test_get_value().sum + +# Assert there's only the one sample +assert 1 == metrics.network.http_upload_badwidth.get("h2").test_get_value().count + +# Buckets are indexed by their lower bound. +assert 1 == metrics.network.http_upload_bandwidth.get("h2").test_get_value().values[41] +``` + +
```Rust @@ -130,10 +224,47 @@ Gets the number of errors recorded for a given labeled custom distribution metri {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +// Assert there were no negative or overlarge values instrumented. +assertEquals(0, Network.httpUploadBandwidth.testGetNumRecordedErrors(ErrorType.INVALID_VALUE)) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network; + +// Assert there were no negative or overlarge values instrumented. +assertEquals(0, Network.INSTANCE.httpUploadBandwidth().testGetNumRecordedErrors(ErrorType.INVALID_VALUE)); +``` + +
+
+ +```Swift +import Glean + +// Assert there were no negative or overlarge values instrumented. +XCTAssertEqual(0, Network.httpUploadBandwidth.testGetNumRecordedErrors(.invalidValue) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Assert there were no negative or overlarge values instrumented. +assert 0 == metrics.network.http_upload_bandwidth.get.test_get_num_recorded_errors(ErrorType.INVALID_VALUE) +``` + +
```Rust From 3156b837d44cd44ba64eef03e6f8e9fe549691d1 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Thu, 6 Aug 2026 14:28:02 +0200 Subject: [PATCH 5/8] Documentation for labeled custom distributions --- .../metrics/labeled_custom_distributions.md | 201 ++++++++++++++++-- 1 file changed, 185 insertions(+), 16 deletions(-) diff --git a/docs/user/reference/metrics/labeled_custom_distributions.md b/docs/user/reference/metrics/labeled_custom_distributions.md index 77ae289f0e..3d0c2b9832 100644 --- a/docs/user/reference/metrics/labeled_custom_distributions.md +++ b/docs/user/reference/metrics/labeled_custom_distributions.md @@ -16,10 +16,47 @@ Accumulate the provided samples in the metric. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +Network.http3LateAckRatio["ack"].accumulateSamples(listOf((late_ack * 10000) / packets_tx)) +Network.http3LateAckRatio["pto"].accumulateSamples(listOf((pto_ack * 10000) / packets_tx)) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network; + +Network.INSTANCE.http3LateAckRatio()["ack"].accumulateSamples(listOf((late_ack * 10000) / packets_tx)); +Network.INSTANCE.http3LateAckRatio()["pto"].accumulateSamples(listOf((pto_ack * 10000) / packets_tx)); +``` + +
+
+ +```Swift +import Glean + +Network.http3LateAckRatio["ack"].accumulateSamples([(late_ack * 10000) / packets_tx]); +Network.http3LateAckRatio["pto"].accumulateSamples([(pto_ack * 10000) / packets_tx]); +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.network.http3_late_ack_ratio["ack"].accumulate_samples([(late_ack * 10000) // packets_tx]) +metrics.network.http3_late_ack_ratio["pto"].accumulate_samples([(late_pto * 10000) // packets_tx]) +``` + +
```Rust @@ -85,10 +122,47 @@ Accumulates one sample and appends it to the metric. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +Network.http3LateAckRatio["ack"].accumulateSingleSample((late_ack * 10000) / packets_tx) +Network.http3LateAckRatio["pto"].accumulateSingleSample((pto_ack * 10000) / packets_tx) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network; + +Network.INSTANCE.http3LateAckRatio()["ack"].accumulateSingleSample((late_ack * 10000) / packets_tx); +Network.INSTANCE.http3LateAckRatio()["pto"].accumulateSingleSample((pto_ack * 10000) / packets_tx); +``` + +
+
+ +```Swift +import Glean + +Network.http3LateAckRatio["ack"].accumulateSingleSample((late_ack * 10000) / packets_tx); +Network.http3LateAckRatio["pto"].accumulateSingleSample((pto_ack * 10000) / packets_tx); +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.network.http3_late_ack_ratio["ack"].accumulate_single_sample([(late_ack * 10000) // packets_tx]) +metrics.network.http3_late_ack_ratio["pto"].accumulate_single_sample([(late_pto * 10000) // packets_tx]) +``` + +
```Rust @@ -159,10 +233,71 @@ in Rust where it's required. `None` or no argument will default to the first val {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +// Assert the sum of all `ack` samples is 42. +assertEquals(42, Network.http3LateAckRatio["ack"].testGetValue().sum) + +// Assert there's only the one sample +assertEquals(1, Network.http3LateAckRatio["ack"].testGetValue().count) + +// Buckets are indexed by their lower bound. +assertEquals(1, Network.http3LateAckRatio["ack"].testGetValue().values[41]) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network + +// Assert the sum of all `ack` samples is 42. +assertEquals(42, Network.INSTANCE.http3LateAckRatio()["ack"].testGetValue().sum); + +// Assert there's only the one sample +assertEquals(1, Network.INSTANCE.http3LateAckRatio()["ack"].testGetValue().count); + +// Buckets are indexed by their lower bound. +assertEquals(1, Network.INSTANCE.http3LateAckRatio()["ack"].testGetValue().values[41]); +``` + +
+
+ +```Swift +import Glean + +// Assert the sum of all `ack` samples is 42. +XCTAssertEqual(42, Network.http3LateAckRatio["ack"].testGetValue().sum) + +// Assert there's only the one sample +XCTAssertEqual(1, Network.http3LateAckRatio["ack"].testGetValue().count) + +// Buckets are indexed by their lower bound. +XCTAssertEqual(1, Network.http3LateAckRatio["ack"].testGetValue().values[41]) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Assert the sum of all samples is 42. +assert 42 == metrics.network.http3_late_ack_ratio.get("ack").test_get_value().sum + +# Assert there's only the one sample +assert 1 == metrics.network.http3_late_ack_ratio.get("ack").test_get_value().count + +# Buckets are indexed by their lower bound. +assert 1 == metrics.network.http3_late_ack_ratio.get("ack").test_get_value().values[41] +``` + +
```Rust @@ -209,10 +344,44 @@ Gets the number of errors recorded for a given labeled custom distribution metri {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Network + +assertEquals(0, Network.http3LateAckRatio.testGetNumRecordedErrors(ErrorType.INVALID_VALUE) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Network; + +assertEquals(0, Network.http3LateAckRatio.testGetNumRecordedErrors(ErrorType.INVALID_VALUE); +``` + +
+
+ +```Swift +import Glean + +XCTAssertEqual(0, Network.http3LateAckRatio.testGetNumRecordedErrors(.invalidValue) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Assert there were no negative values instrumented. +assert 0 == metrics.network.http3_late_ack_ratio.get("ack").test_get_num_recorded_errors(ErrorType.INVALID_VALUE) +``` + +
```Rust From 4b356402315788c9561c17cae136d953669d3925 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Thu, 6 Aug 2026 15:13:31 +0200 Subject: [PATCH 6/8] Documentation for labeled timing distributions --- .../metrics/labeled_timing_distributions.md | 349 ++++++++++++++++-- 1 file changed, 318 insertions(+), 31 deletions(-) diff --git a/docs/user/reference/metrics/labeled_timing_distributions.md b/docs/user/reference/metrics/labeled_timing_distributions.md index 2707751e7d..2c2350e2d5 100644 --- a/docs/user/reference/metrics/labeled_timing_distributions.md +++ b/docs/user/reference/metrics/labeled_timing_distributions.md @@ -16,10 +16,45 @@ Returns a unique `TimerId` for the new timer. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import mozilla.components.service.glean.GleanTimerId +import org.mozilla.yourApplication.GleanMetrics.Devtools + +val start: GleanTimerId = Devtools.ColdToolboxOpenDelay[toolboxId].start() +``` + +
+
+ +```Java +import mozilla.components.service.glean.GleanTimerId; +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +val start: GleanTimerId = Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].start(); +``` + +
+
+ +```Swift +import Glean + +let start = Devtools.ColdToolboxOpenDelay[toolboxId].start() +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +start = metrics.devtools.cold_toolbox_open_delay[toolbox_id].start() +``` + +
```Rust @@ -78,10 +113,43 @@ Do not use the provided `TimerId` after passing it to this method. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +Devtools.ColdToolboxOpenDelay[toolboxId].stopAndAccumulate(start) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].stopAndAccumulate(start); +``` + +
+
+ +```Swift +import Glean + +Devtools.ColdToolboxOpenDelay[toolboxId].stopAndAccumulate(start) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.devtools.cold_toolbox_open_delay[toolbox_id].stop_and_accumulate(start) +``` + +
```Rust @@ -137,10 +205,43 @@ Aborts a previous `start` call, consuming the supplied timer id. {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +Devtools.ColdToolboxOpenDelay[toolboxId].cancel(start) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].cancel(start); +``` + +
+
+ +```Swift +import Glean + +Devtools.ColdToolboxOpenDelay[toolboxId].cancel(start) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.devtools.cold_toolbox_open_delay[toolbox_id].cancel(start) +``` + +
```Rust @@ -208,10 +309,43 @@ instance this method was called on is using `TimeUnit::Second`, then {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +Devtools.ColdToolboxOpenDelay[toolboxId].accumulateSamples(samples) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].accumulateSamples(samples); +``` + +
+
+ +```Swift +import Glean + +Devtools.ColdToolboxOpenDelay[toolboxId].accumulateSamples(samples) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.devtools.cold_toolbox_open_delay[toolbox_id].accumulate_samples(samples) +``` + +
```Rust @@ -281,10 +415,43 @@ instance this method was called on is using `TimeUnit::Second`, then {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +Devtools.ColdToolboxOpenDelay[toolboxId].accumulateSingleSample(sample) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].accumulateSingleSample(sample); +``` + +
+
+ +```Swift +import Glean + +Devtools.ColdToolboxOpenDelay[toolboxId].accumulateSingleSample(sample) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +metrics.devtools.cold_toolbox_open_delay[toolbox_id].accumulate_single_sample(sample) +``` + +
```Rust @@ -342,10 +509,40 @@ For convenience one can measure the time of a function or block of code. {{#include ../../../shared/tab_header.md}} -
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +Devtools.ColdToolboxOpenDelay[toolboxId].measure { + // measure the delay +} +``` + +
-
-
+
+ +```Swift +import Glean + +Devtool.ColdToolboxOpenDelay[toolboxId].measure { + // measure the delay +} +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +with metrics.devtools.cold_toolbox_open_delay[toolbox_id].measure(): + # measue the delay +``` + +
@@ -399,10 +596,63 @@ in Rust where it's required. `None` or no argument will default to the first val {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +// Usually you don't know the exact timing values, +// but you do know how many samples there are: +assertEquals(2, Devtools.ColdToolboxOpenDelay[toolboxId].testGetValue().count) + +// ...and the lower bound of how long they all took: +assertTrue(400 <= Devtools.ColdToolboxOpenDelay[toolboxId].testGetValue().sum) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +// Usually you don't know the exact timing values, +// but you do know how many samples there are: +assertEquals(2, Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].testGetValue().count); + +// ...and the lower bound of how long they all took: +assertTrue(400 <= Devtools.INSTANCE.ColdToolboxOpenDelay()[toolboxId].testGetValue().sum); +``` + +
+
+ +```Swift +import Glean + +// Usually you don't know the exact timing values, +// but you do know how many samples there are: +XCTAssertEqual(2, Devtools.ColdToolboxOpenDelay[toolboxId]).testGetValue().count) + +// ...and the lower bound of how long they all took: +XCTAssertLessThanOrEqual(400, Devtools.ColdToolboxOpenDelay[toolboxId]).testGetValue().sum) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Usually you don't know the exact timing values, +# but you do know how many samples there are: +assert 2 == metrics.devtools.cold_toolbox_open_delay[toolbox_id].test_get_value().count + +# ...and the lower bound of how long they all took: +assert 400 <= metrics.devtools.cold_toolbox_open_delay[toolbox_id].test_get_value().sum +``` + +
```Rust @@ -449,10 +699,47 @@ Gets the number of errors recorded for a given labeled timing distribution metri {{#include ../../../shared/tab_header.md}} -
-
-
-
+
+ +```Kotlin +import org.mozilla.yourApplication.GleanMetrics.Devtools + +// Assert there were no negative values instrumented. +assertEquals(0, Devtools.ColdToolboxOpenDelay.testGetNumRecordedErrors(ErrorType.INVALID_VALUE)) +``` + +
+
+ +```Java +import org.mozilla.yourApplication.GleanMetrics.Devtools; + +// Assert there were no negative values instrumented. +assertEquals(0, Devtools.INSTANCE.ColdToolboxOpenDelay().testGetNumRecordedErrors(ErrorType.INVALID_VALUE)); +``` + +
+
+ +```Swift +import Glean + +// Assert there were no negative values instrumented. +XCTAssertEqual(0, Devtools.ColdToolboxOpenDelay.testGetNumRecordedErrors(.invalidValue)) +``` + +
+
+ +```Python +from glean import load_metrics +metrics = load_metrics("metrics.yaml") + +# Assert there were no negative values instrumented. +assert 0 == metrics.devtools.cold_toolbox_open_delay.test_get_num_recorded_errors(ErrorType.INVALID_VALUE) +``` + +
```Rust From b90281b3b9f8952debe480ca0260eeafc3a83306 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 21 Aug 2026 10:59:15 +0200 Subject: [PATCH 7/8] Add changelog entry for new labeled timing distributions --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e287c44668..32a65e9962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ [Full changelog](https://github.com/mozilla/glean/compare/v70.0.0...main) +* Android + * Implement labeled {custom|memory|timing} distribution ([#3573](https://github.com/mozilla/glean/pull/3573)) +* iOS + * Implement labeled {custom|memory|timing} distribution ([#3573](https://github.com/mozilla/glean/pull/3573)) +* Python + * Implement labeled {custom|memory|timing} distribution and labeled quantity ([#3573](https://github.com/mozilla/glean/pull/3573)) + # v70.0.0 (2026-08-20) [Full changelog](https://github.com/mozilla/glean/compare/v69.0.0...v70.0.0) From f1fecbd593f1219be944c39fd6e0da326839d61e Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 21 Aug 2026 10:58:31 +0200 Subject: [PATCH 8/8] Update to glean_parser v21.0.1 --- CHANGELOG.md | 2 ++ Cargo.lock | 2 +- Makefile | 2 +- glean-core/Cargo.toml | 2 +- glean-core/build/Cargo.toml | 2 +- glean-core/build/src/lib.rs | 2 +- glean-core/ios/sdk_generator.sh | 2 +- glean-core/python/glean/__init__.py | 2 +- .../telemetry/glean-gradle-plugin/GleanGradlePlugin.groovy | 2 +- pyproject.toml | 2 +- samples/glean-sym-test/Cargo.lock | 2 +- 11 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a65e9962..19bf8638ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ [Full changelog](https://github.com/mozilla/glean/compare/v70.0.0...main) +* General + * Updated to `glean_parser` v21.0.0 ([#3573](https://github.com/mozilla/glean/issues/3573)) * Android * Implement labeled {custom|memory|timing} distribution ([#3573](https://github.com/mozilla/glean/pull/3573)) * iOS diff --git a/Cargo.lock b/Cargo.lock index c4d08b51f3..300182bdfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,7 +572,7 @@ dependencies = [ [[package]] name = "glean-build" -version = "20.1.0" +version = "21.0.1" dependencies = [ "tempfile", "xshell-venv", diff --git a/Makefile b/Makefile index bc39582265..f0f3493a9c 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,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~=20.1 + $(GLEAN_PYENV)/bin/pip install glean_parser~=21.0 $(GLEAN_PYENV)/bin/glean_parser translate --allow-reserved \ -f markdown \ -o ./docs/user/user/collected-metrics \ diff --git a/glean-core/Cargo.toml b/glean-core/Cargo.toml index e973da9ecd..cccb711861 100644 --- a/glean-core/Cargo.toml +++ b/glean-core/Cargo.toml @@ -21,7 +21,7 @@ include = [ rust-version = "1.90" [package.metadata.glean] -glean-parser = "20.1.0" +glean-parser = "21.0.1" [badges] circle-ci = { repository = "mozilla/glean", branch = "main" } diff --git a/glean-core/build/Cargo.toml b/glean-core/build/Cargo.toml index 23cb9e6018..dc913de982 100644 --- a/glean-core/build/Cargo.toml +++ b/glean-core/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glean-build" -version = "20.1.0" +version = "21.0.1" 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 1be411d8c0..59bd8b7d22 100644 --- a/glean-core/build/src/lib.rs +++ b/glean-core/build/src/lib.rs @@ -39,7 +39,7 @@ use std::{env, path::PathBuf}; use xshell_venv::{Result, Shell, VirtualEnv}; -const GLEAN_PARSER_VERSION: &str = "20.1.0"; +const GLEAN_PARSER_VERSION: &str = "21.0.1"; /// A Glean Rust bindings generator. pub struct Builder { diff --git a/glean-core/ios/sdk_generator.sh b/glean-core/ios/sdk_generator.sh index bedfd79a2c..bc204a25f9 100755 --- a/glean-core/ios/sdk_generator.sh +++ b/glean-core/ios/sdk_generator.sh @@ -25,7 +25,7 @@ set -e -GLEAN_PARSER_VERSION=20.1 +GLEAN_PARSER_VERSION=21.0 # CMDNAME is used in the usage text below. # shellcheck disable=SC2034 diff --git a/glean-core/python/glean/__init__.py b/glean-core/python/glean/__init__.py index bb95ab3ca8..61ce393c2a 100644 --- a/glean-core/python/glean/__init__.py +++ b/glean-core/python/glean/__init__.py @@ -31,7 +31,7 @@ __email__ = "glean-team@mozilla.com" -GLEAN_PARSER_VERSION = "20.1.0" +GLEAN_PARSER_VERSION = "21.0.1" parser_version = VersionInfo.parse(GLEAN_PARSER_VERSION) parser_version_next_major = parser_version.bump_major() 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 7d46b7e6f7..f8eb9a4d94 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 @@ -59,7 +59,7 @@ abstract class GleanMetricsYamlTransform implements TransformAction { // The version of glean_parser to install from PyPI. - private String GLEAN_PARSER_VERSION = "20.1" + private String GLEAN_PARSER_VERSION = "21.0" // 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" diff --git a/pyproject.toml b/pyproject.toml index 845ac3bb27..1fd3f46e7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ maintainers = [ dependencies = [ "semver>=2.13.0", - "glean_parser~=20.1", + "glean_parser~=21.0", ] [project.urls] diff --git a/samples/glean-sym-test/Cargo.lock b/samples/glean-sym-test/Cargo.lock index 7a2c532ed0..596f193451 100644 --- a/samples/glean-sym-test/Cargo.lock +++ b/samples/glean-sym-test/Cargo.lock @@ -458,7 +458,7 @@ dependencies = [ [[package]] name = "glean-build" -version = "20.1.0" +version = "21.0.1" dependencies = [ "xshell-venv", ]