Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

[Full Changelog](In progress)

### Ads Client
- Add agnostic telemetry support (compatible with Glean)

# v147.0 (_2025-12-07_)

### Relay
Expand Down
1 change: 1 addition & 0 deletions automation/build_ios_artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export PROJECT=MozillaRustComponentsWrapper
./tools/sdk_generator.sh \
-g Glean \
-o ./megazords/ios-rust/Sources/MozillaRustComponentsWrapper/Generated/Glean \
"${SOURCE_ROOT}"/components/ads-client/metrics.yaml \
"${SOURCE_ROOT}"/components/nimbus/metrics.yaml \
"${SOURCE_ROOT}"/components/logins/metrics.yaml \
"${SOURCE_ROOT}"/components/sync_manager/metrics.yaml \
Expand Down
36 changes: 36 additions & 0 deletions components/ads-client/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
buildscript {
if (gradle.hasProperty("mozconfig")) {
repositories {
gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
maven {
url = repository
if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
allowInsecureProtocol = true
}
}
}
}

dependencies {
classpath libs.mozilla.glean.gradle.plugin
}
}
}

plugins {
alias libs.plugins.python.envs.plugin
}

apply from: "$appServicesRootDir/build-scripts/component-common.gradle"
apply from: "$appServicesRootDir/publish.gradle"

ext {
gleanNamespace = "mozilla.telemetry.glean"
gleanYamlFiles = ["${project.projectDir}/../metrics.yaml"]
if (gradle.hasProperty("mozconfig")) {
gleanPythonEnvDir = gradle.mozconfig.substs.GRADLE_GLEAN_PARSER_VENV
}
}
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"

android {
namespace 'org.mozilla.appservices.ads_client'
}

dependencies {
api project(":httpconfig")

implementation libs.mozilla.glean

testImplementation libs.mozilla.glean.forUnitTests
}

ext.configureUniFFIBindgen("ads_client")
Expand Down
115 changes: 107 additions & 8 deletions components/ads-client/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,93 @@ Configuration for initializing the ads client.
pub struct MozAdsClientConfig {
pub environment: Environment,
pub cache_config: Option<MozAdsCacheConfig>,
pub telemetry: Option<Arc<dyn MozAdsTelemetry>>,
}
```

| Field | Type | Description |
| -------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
| `environment` | `Environment` | Selects which MARS environment to connect to. Unless in a dev build, this value can only ever be Prod. |
| `cache_config` | `Option<MozAdsCacheConfig>` | Optional configuration for the internal cache. |
| Field | Type | Description |
| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `environment` | `Environment` | Selects which MARS environment to connect to. Unless in a dev build, this value can only ever be Prod. |
| `cache_config` | `Option<MozAdsCacheConfig>` | Optional configuration for the internal cache. |
| `telemetry` | `Option<Arc<dyn MozAdsTelemetry>>` | Optional telemetry instance for recording metrics. If not provided, a no-op implementation is used. |

---

## `MozAdsTelemetry`

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.

```rust
pub trait MozAdsTelemetry: Send + Sync {
fn record_build_cache_error(&self, label: String, value: String);
fn record_client_error(&self, label: String, value: String);
fn record_client_operation_total(&self, label: String);
fn record_deserialization_error(&self, label: String, value: String);
fn record_http_cache_outcome(&self, label: String, value: String);
}
```

### Implementing Telemetry

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.

#### Swift Example

```swift
import MozillaRustComponents
import Glean

public final class AdsClientTelemetry: MozAdsTelemetry {
public func recordBuildCacheError(label: String, value: String) {
AdsClientMetrics.buildCacheError[label].set(value)
}

public func recordClientError(label: String, value: String) {
AdsClientMetrics.clientError[label].set(value)
}

public func recordClientOperationTotal(label: String) {
AdsClientMetrics.clientOperationTotal[label].add()
}

public func recordDeserializationError(label: String, value: String) {
AdsClientMetrics.deserializationError[label].set(value)
}

public func recordHttpCacheOutcome(label: String, value: String) {
AdsClientMetrics.httpCacheOutcome[label].set(value)
}
}
```

#### Kotlin Example

```kotlin
import mozilla.appservices.adsclient.MozAdsTelemetry
import org.mozilla.appservices.ads_client.GleanMetrics.AdsClient

class AdsClientTelemetry : MozAdsTelemetry {
override fun recordBuildCacheError(label: String, value: String) {
AdsClient.buildCacheError[label].set(value)
}

override fun recordClientError(label: String, value: String) {
AdsClient.clientError[label].set(value)
}

override fun recordClientOperationTotal(label: String) {
AdsClient.clientOperationTotal[label].add()
}

override fun recordDeserializationError(label: String, value: String) {
AdsClient.deserializationError[label].set(value)
}

override fun recordHttpCacheOutcome(label: String, value: String) {
AdsClient.httpCacheOutcome[label].set(value)
}
}
```

---

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

#### Example Client Configuration

```rust
// Swift / Kotlin pseudocode
```swift
// Swift example
let cache = MozAdsCacheConfig(
dbPath: "/tmp/ads_cache.sqlite",
defaultCacheTtlSeconds: 600, // 10 min
maxSizeMib: 20 // 20 MiB
)

let telemetry = AdsClientTelemetry()
let clientCfg = MozAdsClientConfig(
environment: .prod,
cacheConfig: cache
cacheConfig: cache,
telemetry: telemetry
)

let client = MozAdsClient(clientConfig: clientCfg)
```

```kotlin
// Kotlin example
val cache = MozAdsCacheConfig(
dbPath = "/tmp/ads_cache.sqlite",
defaultCacheTtlSeconds = 600L, // 10 min
maxSizeMib = 20L // 20 MiB
)

let client = MozAdsClient.new(clientConfig: clientCfg)
val telemetry = AdsClientTelemetry()
val clientCfg = MozAdsClientConfig(
environment = MozAdsEnvironment.PROD,
cacheConfig = cache,
telemetry = telemetry
)

val client = MozAdsClient(clientCfg)
```

Where `db_path` represents the location of the SQLite file. This must be a file that the client has permission to write to.
Expand Down
127 changes: 127 additions & 0 deletions components/ads-client/metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.


---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0


ads_client:
build_cache_error:
type: labeled_string
description: >
Errors encountered when building the HTTP cache, labeled by error type.
The string value contains the error message or error type.
labels:
- builder_error
- database_error
- empty_db_path
- invalid_max_size
- invalid_ttl
bugs:
- https://github.com/mozilla/application-services/pull/7111
data_reviews:
- https://github.com/mozilla/application-services/pull/7111
data_sensitivity:
- technical
notification_emails:
- ahanot@mozilla.com
- llisi@mozilla.com
- toast@mozilla.com
expires: never

client_error:
type: labeled_string
description: >
Errors encountered when using the ads client, labeled by operation type.
The string value contains the error message or error type. Errors are
recorded even if they are propagated to the consumer.
labels:
- record_click
- record_impression
- report_ad
- request_ads
bugs:
- https://github.com/mozilla/application-services/pull/7111
data_reviews:
- https://github.com/mozilla/application-services/pull/7111
data_sensitivity:
- interaction
notification_emails:
- ahanot@mozilla.com
- llisi@mozilla.com
- toast@mozilla.com
expires: never

client_operation_total:
type: labeled_counter
description: >
The total number of operations attempted by the ads client, labeled by
operation type. Used as the denominator for client_operation_success_rate.
labels:
- new
- record_click
- record_impression
- report_ad
- request_ads
bugs:
- https://github.com/mozilla/application-services/pull/7111
data_reviews:
- https://github.com/mozilla/application-services/pull/7111
data_sensitivity:
- interaction
notification_emails:
- ahanot@mozilla.com
- llisi@mozilla.com
- toast@mozilla.com
expires: never

deserialization_error:
type: labeled_string
description: >
Deserialization errors encountered when parsing AdResponse data,
labeled by error type. The string value contains the error message or
details. Invalid ad items are skipped but these errors are tracked for
monitoring data quality issues.
labels:
- invalid_ad_item
- invalid_array
- invalid_structure
bugs:
- https://github.com/mozilla/application-services/pull/7111
data_reviews:
- https://github.com/mozilla/application-services/pull/7111
data_sensitivity:
- technical
notification_emails:
- ahanot@mozilla.com
- llisi@mozilla.com
- toast@mozilla.com
expires: never

http_cache_outcome:
type: labeled_string
description: >
The total number of outcomes encountered during read operations on the
http cache, labeled by type. The string value contains the error message or
error type.
labels:
- cleanup_failed
- hit
- lookup_failed
- miss_not_cacheable
- miss_stored
- no_cache
- store_failed
bugs:
- https://github.com/mozilla/application-services/pull/7111
data_reviews:
- https://github.com/mozilla/application-services/pull/7111
data_sensitivity:
- technical
notification_emails:
- ahanot@mozilla.com
- llisi@mozilla.com
- toast@mozilla.com
expires: never
Loading