Skip to content

Commit a6b95f1

Browse files
committed
Analysis file/package names in analytics blobs
Expose `file_name` and `package_name` as Analytics Engine blob dimensions instead of treating them as private/redacted fields. Web UI analytics field builders now populate these values for file and report events, and the check script now enforces that both keys stay in `ANALYTICS_BLOB_KEYS` to prevent regressions.
1 parent 915fa0b commit a6b95f1

7 files changed

Lines changed: 58 additions & 8 deletions

File tree

packages/apk-webui/functions/analytics.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ const ANALYTICS_BLOB_KEYS = [
2121
"source_label",
2222
"error_name",
2323
"viewport_bucket",
24-
"route",
25-
"method",
24+
"file_name",
25+
"package_name",
2626
];
2727

2828
const ANALYTICS_DOUBLE_KEYS = [
@@ -54,8 +54,6 @@ const PRIVATE_EVENT_KEYS = new Set([
5454
"error_message",
5555
"error_stack",
5656
"file_size_bytes",
57-
"file_name",
58-
"package_name",
5957
"report_path",
6058
"url_host",
6159
"url_path",

packages/apk-webui/scripts/check.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ async function validateAnalyticsEngineKeyBudgets() {
8888
throw new Error(`${name} has ${values.length} entries; Analytics Engine supports at most 20`);
8989
}
9090
}
91+
92+
const blobKeys = readStringArray(source, "ANALYTICS_BLOB_KEYS");
93+
for (const key of ["file_name", "package_name"]) {
94+
if (!blobKeys.includes(key)) {
95+
throw new Error(`ANALYTICS_BLOB_KEYS must include ${key}`);
96+
}
97+
}
9198
}
9299

93100
function readStringArray(source, name) {

packages/apk-webui/src/app/analytics-fields.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export function getFileAnalyticsFields(file) {
1111
}
1212

1313
return compactAnalyticsObject({
14+
file_name: file.name || "",
1415
file_extension: getFileExtension(file.name),
1516
size_bucket: formatSizeBucket(file.size),
1617
});
@@ -25,6 +26,8 @@ export function getReportAnalyticsFields(report) {
2526
const archive = info.archive || null;
2627

2728
return compactAnalyticsObject({
29+
file_name: report?.fileName || "",
30+
package_name: info.packageName || "",
2831
file_extension: getFileExtension(report?.fileName),
2932
size_bucket: formatSizeBucket(report?.fileSizeBytes),
3033
duration_ms: Number(report?.durationMs) || 0,

packages/apk-webui/src/app/analytics.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ const PRIVATE_EVENT_KEYS = new Set([
1515
"chat_id",
1616
"error_message",
1717
"error_stack",
18-
"file_name",
19-
"package_name",
2018
"report_path",
2119
"url_host",
2220
"url_path",

packages/bot-worker/src/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,8 @@ function buildMessageTelemetryFields(update, message, command, botMentioned, loc
22142214
update_type: getUpdateType(update),
22152215
locale,
22162216
chat_type: message.chat?.type || null,
2217+
chat_title: isTelegramGroupChat(message.chat) ? message.chat?.title || null : null,
2218+
chat_username: isTelegramGroupChat(message.chat) ? formatTelegramUsername(message.chat?.username) : null,
22172219
chat_id: message.chat?.id != null ? String(message.chat.id) : null,
22182220
message_id: message.message_id || null,
22192221
message_thread_id: message.message_thread_id || null,
@@ -2230,6 +2232,15 @@ function buildMessageTelemetryFields(update, message, command, botMentioned, loc
22302232
};
22312233
}
22322234

2235+
function isTelegramGroupChat(chat) {
2236+
return chat?.type === "group" || chat?.type === "supergroup" || chat?.type === "channel";
2237+
}
2238+
2239+
function formatTelegramUsername(username) {
2240+
const value = String(username || "").trim().replace(/^@/u, "");
2241+
return value ? `@${value}` : null;
2242+
}
2243+
22332244
function getUpdateType(update) {
22342245
if (update?.message) {
22352246
return "message";

packages/bot-worker/src/observability.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ const ANALYTICS_BLOB_KEYS = [
66
"event",
77
"surface",
88
"route",
9-
"method",
10-
"path",
9+
"chat_title",
10+
"chat_username",
1111
"chat_type",
1212
"update_type",
1313
"command",

packages/bot-worker/test/observability.test.mjs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,36 @@ test("analytics datapoints include APK file and package identifiers", () => {
3535
assert.ok(datapoints[0].blobs.includes("sample.apk"));
3636
assert.ok(datapoints[0].blobs.includes("com.example.app"));
3737
});
38+
39+
test("analytics datapoints include Telegram group title and public username", () => {
40+
const datapoints = [];
41+
const env = {
42+
USAGE_ANALYTICS: {
43+
writeDataPoint(point) {
44+
datapoints.push(point);
45+
},
46+
},
47+
};
48+
const originalLog = console.log;
49+
console.log = () => {};
50+
51+
try {
52+
logInfoEvent(
53+
env,
54+
{ request_id: "request-1", surface: "worker", route: "telegram_webhook" },
55+
"telegram.update.received",
56+
{
57+
result: "received",
58+
chat_title: "LibChecker Group",
59+
chat_username: "@libchecker_group",
60+
},
61+
);
62+
} finally {
63+
console.log = originalLog;
64+
}
65+
66+
assert.equal(datapoints.length, 1);
67+
assert.equal(datapoints[0].blobs.length, 20);
68+
assert.ok(datapoints[0].blobs.includes("LibChecker Group"));
69+
assert.ok(datapoints[0].blobs.includes("@libchecker_group"));
70+
});

0 commit comments

Comments
 (0)