Skip to content

Commit 2801ef5

Browse files
authored
feat(ads-client): add glean metrics (#7111)
1 parent 4e6614a commit 2801ef5

File tree

18 files changed

+702
-257
lines changed

18 files changed

+702
-257
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
[Full Changelog](In progress)
44

5+
### Ads Client
6+
- Add agnostic telemetry support (compatible with Glean)
7+
58
# v147.0 (_2025-12-07_)
69

710
### Relay

automation/build_ios_artifacts.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export PROJECT=MozillaRustComponentsWrapper
1111
./tools/sdk_generator.sh \
1212
-g Glean \
1313
-o ./megazords/ios-rust/Sources/MozillaRustComponentsWrapper/Generated/Glean \
14+
"${SOURCE_ROOT}"/components/ads-client/metrics.yaml \
1415
"${SOURCE_ROOT}"/components/nimbus/metrics.yaml \
1516
"${SOURCE_ROOT}"/components/logins/metrics.yaml \
1617
"${SOURCE_ROOT}"/components/sync_manager/metrics.yaml \

components/ads-client/android/build.gradle

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,48 @@
1+
buildscript {
2+
if (gradle.hasProperty("mozconfig")) {
3+
repositories {
4+
gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
5+
maven {
6+
url = repository
7+
if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
8+
allowInsecureProtocol = true
9+
}
10+
}
11+
}
12+
}
13+
14+
dependencies {
15+
classpath libs.mozilla.glean.gradle.plugin
16+
}
17+
}
18+
}
19+
20+
plugins {
21+
alias libs.plugins.python.envs.plugin
22+
}
23+
124
apply from: "$appServicesRootDir/build-scripts/component-common.gradle"
225
apply from: "$appServicesRootDir/publish.gradle"
326

27+
ext {
28+
gleanNamespace = "mozilla.telemetry.glean"
29+
gleanYamlFiles = ["${project.projectDir}/../metrics.yaml"]
30+
if (gradle.hasProperty("mozconfig")) {
31+
gleanPythonEnvDir = gradle.mozconfig.substs.GRADLE_GLEAN_PARSER_VENV
32+
}
33+
}
34+
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
35+
436
android {
537
namespace 'org.mozilla.appservices.ads_client'
638
}
739

840
dependencies {
941
api project(":httpconfig")
42+
43+
implementation libs.mozilla.glean
44+
45+
testImplementation libs.mozilla.glean.forUnitTests
1046
}
1147

1248
ext.configureUniFFIBindgen("ads_client")

components/ads-client/docs/usage.md

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,93 @@ Configuration for initializing the ads client.
5858
pub struct MozAdsClientConfig {
5959
pub environment: Environment,
6060
pub cache_config: Option<MozAdsCacheConfig>,
61+
pub telemetry: Option<Arc<dyn MozAdsTelemetry>>,
6162
}
6263
```
6364

64-
| Field | Type | Description |
65-
| -------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
66-
| `environment` | `Environment` | Selects which MARS environment to connect to. Unless in a dev build, this value can only ever be Prod. |
67-
| `cache_config` | `Option<MozAdsCacheConfig>` | Optional configuration for the internal cache. |
65+
| Field | Type | Description |
66+
| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------ |
67+
| `environment` | `Environment` | Selects which MARS environment to connect to. Unless in a dev build, this value can only ever be Prod. |
68+
| `cache_config` | `Option<MozAdsCacheConfig>` | Optional configuration for the internal cache. |
69+
| `telemetry` | `Option<Arc<dyn MozAdsTelemetry>>` | Optional telemetry instance for recording metrics. If not provided, a no-op implementation is used. |
70+
71+
---
72+
73+
## `MozAdsTelemetry`
74+
75+
Telemetry interface for recording ads client metrics. You must provide an implementation of this interface to the `MozAdsClientConfig` constructor to enable telemetry collection. If no telemetry instance is provided, a no-op implementation is used and no metrics will be recorded.
76+
77+
```rust
78+
pub trait MozAdsTelemetry: Send + Sync {
79+
fn record_build_cache_error(&self, label: String, value: String);
80+
fn record_client_error(&self, label: String, value: String);
81+
fn record_client_operation_total(&self, label: String);
82+
fn record_deserialization_error(&self, label: String, value: String);
83+
fn record_http_cache_outcome(&self, label: String, value: String);
84+
}
85+
```
86+
87+
### Implementing Telemetry
88+
89+
To enable telemetry collection, you need to implement the `MozAdsTelemetry` interface and provide an instance to the `MozAdsClientConfig` constructor. The following examples show how to bind Glean metrics to the telemetry interface.
90+
91+
#### Swift Example
92+
93+
```swift
94+
import MozillaRustComponents
95+
import Glean
96+
97+
public final class AdsClientTelemetry: MozAdsTelemetry {
98+
public func recordBuildCacheError(label: String, value: String) {
99+
AdsClientMetrics.buildCacheError[label].set(value)
100+
}
101+
102+
public func recordClientError(label: String, value: String) {
103+
AdsClientMetrics.clientError[label].set(value)
104+
}
105+
106+
public func recordClientOperationTotal(label: String) {
107+
AdsClientMetrics.clientOperationTotal[label].add()
108+
}
109+
110+
public func recordDeserializationError(label: String, value: String) {
111+
AdsClientMetrics.deserializationError[label].set(value)
112+
}
113+
114+
public func recordHttpCacheOutcome(label: String, value: String) {
115+
AdsClientMetrics.httpCacheOutcome[label].set(value)
116+
}
117+
}
118+
```
119+
120+
#### Kotlin Example
121+
122+
```kotlin
123+
import mozilla.appservices.adsclient.MozAdsTelemetry
124+
import org.mozilla.appservices.ads_client.GleanMetrics.AdsClient
125+
126+
class AdsClientTelemetry : MozAdsTelemetry {
127+
override fun recordBuildCacheError(label: String, value: String) {
128+
AdsClient.buildCacheError[label].set(value)
129+
}
130+
131+
override fun recordClientError(label: String, value: String) {
132+
AdsClient.clientError[label].set(value)
133+
}
134+
135+
override fun recordClientOperationTotal(label: String) {
136+
AdsClient.clientOperationTotal[label].add()
137+
}
138+
139+
override fun recordDeserializationError(label: String, value: String) {
140+
AdsClient.deserializationError[label].set(value)
141+
}
142+
143+
override fun recordHttpCacheOutcome(label: String, value: String) {
144+
AdsClient.httpCacheOutcome[label].set(value)
145+
}
146+
}
147+
```
68148

69149
---
70150

@@ -400,21 +480,40 @@ If the effective TTL resolves to 0 seconds, the response is not cached.
400480

401481
#### Example Client Configuration
402482

403-
```rust
404-
// Swift / Kotlin pseudocode
483+
```swift
484+
// Swift example
405485
let cache = MozAdsCacheConfig(
406486
dbPath: "/tmp/ads_cache.sqlite",
407487
defaultCacheTtlSeconds: 600, // 10 min
408488
maxSizeMib: 20 // 20 MiB
409489
)
410490

491+
let telemetry = AdsClientTelemetry()
411492
let clientCfg = MozAdsClientConfig(
412493
environment: .prod,
413-
cacheConfig: cache
494+
cacheConfig: cache,
495+
telemetry: telemetry
496+
)
497+
498+
let client = MozAdsClient(clientConfig: clientCfg)
499+
```
500+
501+
```kotlin
502+
// Kotlin example
503+
val cache = MozAdsCacheConfig(
504+
dbPath = "/tmp/ads_cache.sqlite",
505+
defaultCacheTtlSeconds = 600L, // 10 min
506+
maxSizeMib = 20L // 20 MiB
414507
)
415508

416-
let client = MozAdsClient.new(clientConfig: clientCfg)
509+
val telemetry = AdsClientTelemetry()
510+
val clientCfg = MozAdsClientConfig(
511+
environment = MozAdsEnvironment.PROD,
512+
cacheConfig = cache,
513+
telemetry = telemetry
514+
)
417515

516+
val client = MozAdsClient(clientCfg)
418517
```
419518

420519
Where `db_path` represents the location of the SQLite file. This must be a file that the client has permission to write to.

components/ads-client/metrics.yaml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
6+
---
7+
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
8+
9+
10+
ads_client:
11+
build_cache_error:
12+
type: labeled_string
13+
description: >
14+
Errors encountered when building the HTTP cache, labeled by error type.
15+
The string value contains the error message or error type.
16+
labels:
17+
- builder_error
18+
- database_error
19+
- empty_db_path
20+
- invalid_max_size
21+
- invalid_ttl
22+
bugs:
23+
- https://github.com/mozilla/application-services/pull/7111
24+
data_reviews:
25+
- https://github.com/mozilla/application-services/pull/7111
26+
data_sensitivity:
27+
- technical
28+
notification_emails:
29+
30+
31+
32+
expires: never
33+
34+
client_error:
35+
type: labeled_string
36+
description: >
37+
Errors encountered when using the ads client, labeled by operation type.
38+
The string value contains the error message or error type. Errors are
39+
recorded even if they are propagated to the consumer.
40+
labels:
41+
- record_click
42+
- record_impression
43+
- report_ad
44+
- request_ads
45+
bugs:
46+
- https://github.com/mozilla/application-services/pull/7111
47+
data_reviews:
48+
- https://github.com/mozilla/application-services/pull/7111
49+
data_sensitivity:
50+
- interaction
51+
notification_emails:
52+
53+
54+
55+
expires: never
56+
57+
client_operation_total:
58+
type: labeled_counter
59+
description: >
60+
The total number of operations attempted by the ads client, labeled by
61+
operation type. Used as the denominator for client_operation_success_rate.
62+
labels:
63+
- new
64+
- record_click
65+
- record_impression
66+
- report_ad
67+
- request_ads
68+
bugs:
69+
- https://github.com/mozilla/application-services/pull/7111
70+
data_reviews:
71+
- https://github.com/mozilla/application-services/pull/7111
72+
data_sensitivity:
73+
- interaction
74+
notification_emails:
75+
76+
77+
78+
expires: never
79+
80+
deserialization_error:
81+
type: labeled_string
82+
description: >
83+
Deserialization errors encountered when parsing AdResponse data,
84+
labeled by error type. The string value contains the error message or
85+
details. Invalid ad items are skipped but these errors are tracked for
86+
monitoring data quality issues.
87+
labels:
88+
- invalid_ad_item
89+
- invalid_array
90+
- invalid_structure
91+
bugs:
92+
- https://github.com/mozilla/application-services/pull/7111
93+
data_reviews:
94+
- https://github.com/mozilla/application-services/pull/7111
95+
data_sensitivity:
96+
- technical
97+
notification_emails:
98+
99+
100+
101+
expires: never
102+
103+
http_cache_outcome:
104+
type: labeled_string
105+
description: >
106+
The total number of outcomes encountered during read operations on the
107+
http cache, labeled by type. The string value contains the error message or
108+
error type.
109+
labels:
110+
- cleanup_failed
111+
- hit
112+
- lookup_failed
113+
- miss_not_cacheable
114+
- miss_stored
115+
- no_cache
116+
- store_failed
117+
bugs:
118+
- https://github.com/mozilla/application-services/pull/7111
119+
data_reviews:
120+
- https://github.com/mozilla/application-services/pull/7111
121+
data_sensitivity:
122+
- technical
123+
notification_emails:
124+
125+
126+
127+
expires: never

0 commit comments

Comments
 (0)