Skip to content

Commit 8dc2b7d

Browse files
Add analytics to asset-worker (#7053)
1 parent cef32c8 commit 8dc2b7d

File tree

9 files changed

+165
-16
lines changed

9 files changed

+165
-16
lines changed

.changeset/eleven-rats-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/workers-shared": minor
3+
---
4+
5+
Adds analytics to asset-worker
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type { ReadyAnalytics } from "./types";
2+
3+
// This will allow us to make breaking changes to the analytic schema
4+
const VERSION = 1;
5+
6+
// When adding new columns please update the schema
7+
type Data = {
8+
// -- Doubles --
9+
// double1 - The time it takes for the whole request to complete in milliseconds
10+
requestTime?: number;
11+
// double2 - Colo ID
12+
coloId?: number;
13+
// double3 - Metal ID
14+
metalId?: number;
15+
// double4 - Colo tier (e.g. tier 1, tier 2, tier 3)
16+
coloTier?: number;
17+
18+
// -- Blobs --
19+
// blob1 - Hostname of the request
20+
hostname?: string;
21+
// blob2 - User agent making the request
22+
userAgent?: string;
23+
// blob3 - Html handling option
24+
htmlHandling?: string;
25+
// blob4 - Not found handling option
26+
notFoundHandling?: string;
27+
// blob5 - Error message
28+
error?: string;
29+
// blob6 - The current version UUID of asset-worker
30+
version?: string;
31+
// blob7 - Region of the colo (e.g. WEUR)
32+
coloRegion?: string;
33+
};
34+
35+
export class Analytics {
36+
private data: Data = {};
37+
private readyAnalytics?: ReadyAnalytics;
38+
39+
constructor(readyAnalytics?: ReadyAnalytics) {
40+
this.readyAnalytics = readyAnalytics;
41+
}
42+
43+
setData(newData: Partial<Data>) {
44+
this.data = { ...this.data, ...newData };
45+
}
46+
47+
getData(key: keyof Data) {
48+
return this.data[key];
49+
}
50+
51+
write(hostname?: string) {
52+
if (!this.readyAnalytics) {
53+
return;
54+
}
55+
56+
this.readyAnalytics.logEvent({
57+
version: VERSION,
58+
accountId: 0, // TODO: need to plumb through
59+
indexId: hostname,
60+
doubles: [
61+
this.data.requestTime ?? -1, // double1
62+
this.data.coloId ?? -1, // double2
63+
this.data.metalId ?? -1, // double3
64+
this.data.coloTier ?? -1, // double4
65+
],
66+
blobs: [
67+
this.data.hostname?.substring(0, 256), // blob1 - trim to 256 bytes
68+
this.data.userAgent?.substring(0, 256), // blob2 - trim to 256 bytes
69+
this.data.htmlHandling, // blob3
70+
this.data.notFoundHandling, // blob4
71+
this.data.error?.substring(0, 256), // blob5 - trim to 256 bytes
72+
this.data.version, // blob6
73+
this.data.coloRegion, // blob7
74+
],
75+
});
76+
}
77+
}

packages/workers-shared/asset-worker/src/index.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { WorkerEntrypoint } from "cloudflare:workers";
2+
import { PerformanceTimer } from "../../utils/performance";
23
import { setupSentry } from "../../utils/sentry";
4+
import { Analytics } from "./analytics";
35
import { AssetsManifest } from "./assets-manifest";
46
import { applyConfigurationDefaults } from "./configuration";
57
import { decodePath, getIntent, handleRequest } from "./handler";
@@ -8,7 +10,8 @@ import {
810
MethodNotAllowedResponse,
911
} from "./responses";
1012
import { getAssetWithMetadataFromKV } from "./utils/kv";
11-
import type { AssetConfig } from "../../utils/types";
13+
import type { AssetConfig, UnsafePerformanceTimer } from "../../utils/types";
14+
import type { ColoMetadata, Environment, ReadyAnalytics } from "./types";
1215

1316
type Env = {
1417
/*
@@ -29,6 +32,12 @@ type Env = {
2932

3033
SENTRY_ACCESS_CLIENT_ID: string;
3134
SENTRY_ACCESS_CLIENT_SECRET: string;
35+
36+
ENVIRONMENT: Environment;
37+
ANALYTICS: ReadyAnalytics;
38+
COLO_METADATA: ColoMetadata;
39+
UNSAFE_PERFORMANCE: UnsafePerformanceTimer;
40+
VERSION_METADATA: WorkerVersionMetadata;
3241
};
3342

3443
/*
@@ -45,6 +54,10 @@ type Env = {
4554
export default class extends WorkerEntrypoint<Env> {
4655
async fetch(request: Request): Promise<Response> {
4756
let sentry: ReturnType<typeof setupSentry> | undefined;
57+
const analytics = new Analytics(this.env.ANALYTICS);
58+
const performance = new PerformanceTimer(this.env.UNSAFE_PERFORMANCE);
59+
const startTimeMs = performance.now();
60+
4861
try {
4962
sentry = setupSentry(
5063
request,
@@ -54,9 +67,34 @@ export default class extends WorkerEntrypoint<Env> {
5467
this.env.SENTRY_ACCESS_CLIENT_SECRET
5568
);
5669

70+
const config = applyConfigurationDefaults(this.env.CONFIG);
71+
const userAgent = request.headers.get("user-agent") ?? "UA UNKNOWN";
72+
73+
if (sentry) {
74+
const colo = this.env.COLO_METADATA.coloId;
75+
sentry.setTag("colo", this.env.COLO_METADATA.coloId);
76+
sentry.setTag("metal", this.env.COLO_METADATA.metalId);
77+
sentry.setUser({ userAgent: userAgent, colo: colo });
78+
}
79+
80+
if (this.env.COLO_METADATA && this.env.VERSION_METADATA) {
81+
const url = new URL(request.url);
82+
analytics.setData({
83+
coloId: this.env.COLO_METADATA.coloId,
84+
metalId: this.env.COLO_METADATA.metalId,
85+
coloTier: this.env.COLO_METADATA.coloTier,
86+
coloRegion: this.env.COLO_METADATA.coloRegion,
87+
version: this.env.VERSION_METADATA.id,
88+
hostname: url.hostname,
89+
htmlHandling: config.html_handling,
90+
notFoundHandling: config.not_found_handling,
91+
userAgent: userAgent,
92+
});
93+
}
94+
5795
return handleRequest(
5896
request,
59-
applyConfigurationDefaults(this.env.CONFIG),
97+
config,
6098
this.unstable_exists.bind(this),
6199
this.unstable_getByETag.bind(this)
62100
);
@@ -68,7 +106,14 @@ export default class extends WorkerEntrypoint<Env> {
68106
sentry.captureException(err);
69107
}
70108

109+
if (err instanceof Error) {
110+
analytics.setData({ error: err.message });
111+
}
112+
71113
return response;
114+
} finally {
115+
analytics.setData({ requestTime: performance.now() - startTimeMs });
116+
analytics.write();
72117
}
73118
}
74119

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export type Environment = "production" | "staging";
2+
3+
export interface ReadyAnalytics {
4+
logEvent: (e: ReadyAnalyticsEvent) => void;
5+
}
6+
7+
export interface ColoMetadata {
8+
metalId: number;
9+
coloId: number;
10+
coloRegion: string;
11+
coloTier: number;
12+
}
13+
14+
export interface ReadyAnalyticsEvent {
15+
accountId?: number;
16+
indexId?: string;
17+
version?: number;
18+
doubles?: (number | undefined)[];
19+
blobs?: (string | undefined)[];
20+
}

packages/workers-shared/asset-worker/wrangler.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,11 @@ type = "internal_assets"
3131

3232
[unsafe.metadata.build_options]
3333
stable_id = "cloudflare/cf_asset_worker"
34-
networks = ["cf","jdc"]
34+
networks = ["cf","jdc"]
35+
36+
[version_metadata]
37+
binding = "VERSION_METADATA"
38+
39+
[[unsafe.bindings]]
40+
name = "workers-asset-worker"
41+
type = "internal_capability_grants"

packages/workers-shared/router-worker/src/index.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1+
import { PerformanceTimer } from "../../utils/performance";
12
import { setupSentry } from "../../utils/sentry";
23
import { Analytics, DISPATCH_TYPE } from "./analytics";
3-
import { PerformanceTimer } from "./performance";
44
import type AssetWorker from "../../asset-worker/src/index";
5-
import type { RoutingConfig } from "../../utils/types";
6-
import type {
7-
ColoMetadata,
8-
Environment,
9-
ReadyAnalytics,
10-
UnsafePerformanceTimer,
11-
} from "./types";
5+
import type { RoutingConfig, UnsafePerformanceTimer } from "../../utils/types";
6+
import type { ColoMetadata, Environment, ReadyAnalytics } from "./types";
127

138
interface Env {
149
ASSET_WORKER: Service<AssetWorker>;

packages/workers-shared/router-worker/src/types.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ export interface ColoMetadata {
1111
coloTier: number;
1212
}
1313

14-
export interface UnsafePerformanceTimer {
15-
readonly timeOrigin: number;
16-
now: () => number;
17-
}
18-
1914
export interface ReadyAnalyticsEvent {
2015
accountId?: number;
2116
indexId?: string;

packages/workers-shared/utils/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ export const AssetConfigSchema = z.object({
2020

2121
export type RoutingConfig = z.infer<typeof RoutingConfigSchema>;
2222
export type AssetConfig = z.infer<typeof AssetConfigSchema>;
23+
24+
export interface UnsafePerformanceTimer {
25+
readonly timeOrigin: number;
26+
now: () => number;
27+
}

0 commit comments

Comments
 (0)