Skip to content

Commit

Permalink
Add analytics to asset-worker
Browse files Browse the repository at this point in the history
  • Loading branch information
WillTaylorDev committed Oct 22, 2024
1 parent c794935 commit 2e91066
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-rats-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/workers-shared": minor
---

Adds analytics to asset-worker
77 changes: 77 additions & 0 deletions packages/workers-shared/asset-worker/src/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { ReadyAnalytics } from "./types";

// This will allow us to make breaking changes to the analytic schema
const VERSION = 1;

// When adding new columns please update the schema
type Data = {
// -- Doubles --
// double1 - The time it takes for the whole request to complete in milliseconds
requestTime?: number;
// double2 - Colo ID
coloId?: number;
// double3 - Metal ID
metalId?: number;
// double4 - Colo tier (e.g. tier 1, tier 2, tier 3)
coloTier?: number;

// -- Blobs --
// blob1 - Hostname of the request
hostname?: string;
// blob2 - User agent making the request
userAgent?: string;
// blob3 - Html handling option
htmlHandling?: string;
// blob4 - Not found handling option
notFoundHandling?: string;
// blob5 - Error message
error?: string;
// blob6 - The current version UUID of asset-worker
version?: string;
// blob7 - Region of the colo (e.g. WEUR)
coloRegion?: string;
};

export class Analytics {
private data: Data = {};
private readyAnalytics?: ReadyAnalytics;

constructor(readyAnalytics?: ReadyAnalytics) {
this.readyAnalytics = readyAnalytics;
}

setData(newData: Partial<Data>) {
this.data = { ...this.data, ...newData };
}

getData(key: keyof Data) {
return this.data[key];
}

write(hostname?: string) {
if (!this.readyAnalytics) {
return;
}

this.readyAnalytics.logEvent({
version: VERSION,
accountId: 0, // TODO: need to plumb through
indexId: hostname,
doubles: [
this.data.requestTime ?? -1, // double1
this.data.coloId ?? -1, // double2
this.data.metalId ?? -1, // double3
this.data.coloTier ?? -1, // double4
],
blobs: [
this.data.hostname?.substring(0, 256), // blob1 - trim to 256 bytes
this.data.userAgent?.substring(0, 256), // blob2 - trim to 256 bytes
this.data.htmlHandling, // blob3
this.data.notFoundHandling, // blob4
this.data.error?.substring(0, 256), // blob5 - trim to 256 bytes
this.data.version, // blob6
this.data.coloRegion, // blob7
],
});
}
}
49 changes: 47 additions & 2 deletions packages/workers-shared/asset-worker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { WorkerEntrypoint } from "cloudflare:workers";
import { PerformanceTimer } from "../../utils/performance";
import { setupSentry } from "../../utils/sentry";
import { Analytics } from "./analytics";
import { AssetsManifest } from "./assets-manifest";
import { applyConfigurationDefaults } from "./configuration";
import { decodePath, getIntent, handleRequest } from "./handler";
Expand All @@ -8,7 +10,8 @@ import {
MethodNotAllowedResponse,
} from "./responses";
import { getAssetWithMetadataFromKV } from "./utils/kv";
import type { AssetConfig } from "../../utils/types";
import type { AssetConfig, UnsafePerformanceTimer } from "../../utils/types";
import type { ColoMetadata, Environment, ReadyAnalytics } from "./types";

type Env = {
/*
Expand All @@ -29,6 +32,12 @@ type Env = {

SENTRY_ACCESS_CLIENT_ID: string;
SENTRY_ACCESS_CLIENT_SECRET: string;

ENVIRONMENT: Environment;
ANALYTICS: ReadyAnalytics;
COLO_METADATA: ColoMetadata;
UNSAFE_PERFORMANCE: UnsafePerformanceTimer;
VERSION_METADATA: WorkerVersionMetadata;
};

/*
Expand All @@ -45,6 +54,10 @@ type Env = {
export default class extends WorkerEntrypoint<Env> {
async fetch(request: Request): Promise<Response> {
let sentry: ReturnType<typeof setupSentry> | undefined;
const analytics = new Analytics(this.env.ANALYTICS);
const performance = new PerformanceTimer(this.env.UNSAFE_PERFORMANCE);
const startTimeMs = performance.now();

try {
sentry = setupSentry(
request,
Expand All @@ -54,9 +67,34 @@ export default class extends WorkerEntrypoint<Env> {
this.env.SENTRY_ACCESS_CLIENT_SECRET
);

const config = applyConfigurationDefaults(this.env.CONFIG);
const userAgent = request.headers.get("user-agent") ?? "UA UNKNOWN";

if (sentry) {
const colo = this.env.COLO_METADATA.coloId;
sentry.setTag("colo", this.env.COLO_METADATA.coloId);
sentry.setTag("metal", this.env.COLO_METADATA.metalId);
sentry.setUser({ userAgent: userAgent, colo: colo });
}

if (this.env.COLO_METADATA && this.env.VERSION_METADATA) {
const url = new URL(request.url);
analytics.setData({
coloId: this.env.COLO_METADATA.coloId,
metalId: this.env.COLO_METADATA.metalId,
coloTier: this.env.COLO_METADATA.coloTier,
coloRegion: this.env.COLO_METADATA.coloRegion,
version: this.env.VERSION_METADATA.id,
hostname: url.hostname,
htmlHandling: config.html_handling,
notFoundHandling: config.not_found_handling,
userAgent: userAgent,
});
}

return handleRequest(
request,
applyConfigurationDefaults(this.env.CONFIG),
config,
this.unstable_exists.bind(this),
this.unstable_getByETag.bind(this)
);
Expand All @@ -68,7 +106,14 @@ export default class extends WorkerEntrypoint<Env> {
sentry.captureException(err);
}

if (err instanceof Error) {
analytics.setData({ error: err.message });
}

return response;
} finally {
analytics.setData({ requestTime: performance.now() - startTimeMs });
analytics.write();
}
}

Expand Down
20 changes: 20 additions & 0 deletions packages/workers-shared/asset-worker/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type Environment = "production" | "staging";

export interface ReadyAnalytics {
logEvent: (e: ReadyAnalyticsEvent) => void;
}

export interface ColoMetadata {
metalId: number;
coloId: number;
coloRegion: string;
coloTier: number;
}

export interface ReadyAnalyticsEvent {
accountId?: number;
indexId?: string;
version?: number;
doubles?: (number | undefined)[];
blobs?: (string | undefined)[];
}
9 changes: 8 additions & 1 deletion packages/workers-shared/asset-worker/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,11 @@ type = "internal_assets"

[unsafe.metadata.build_options]
stable_id = "cloudflare/cf_asset_worker"
networks = ["cf","jdc"]
networks = ["cf","jdc"]

[version_metadata]
binding = "VERSION_METADATA"

[[unsafe.bindings]]
name = "workers-asset-worker"
type = "internal_capability_grants"
11 changes: 3 additions & 8 deletions packages/workers-shared/router-worker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { PerformanceTimer } from "../../utils/performance";
import { setupSentry } from "../../utils/sentry";
import { Analytics, DISPATCH_TYPE } from "./analytics";
import { PerformanceTimer } from "./performance";
import type AssetWorker from "../../asset-worker/src/index";
import type { RoutingConfig } from "../../utils/types";
import type {
ColoMetadata,
Environment,
ReadyAnalytics,
UnsafePerformanceTimer,
} from "./types";
import type { RoutingConfig, UnsafePerformanceTimer } from "../../utils/types";
import type { ColoMetadata, Environment, ReadyAnalytics } from "./types";

interface Env {
ASSET_WORKER: Service<AssetWorker>;
Expand Down
5 changes: 0 additions & 5 deletions packages/workers-shared/router-worker/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ export interface ColoMetadata {
coloTier: number;
}

export interface UnsafePerformanceTimer {
readonly timeOrigin: number;
now: () => number;
}

export interface ReadyAnalyticsEvent {
accountId?: number;
indexId?: string;
Expand Down
5 changes: 5 additions & 0 deletions packages/workers-shared/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ export const AssetConfigSchema = z.object({

export type RoutingConfig = z.infer<typeof RoutingConfigSchema>;
export type AssetConfig = z.infer<typeof AssetConfigSchema>;

export interface UnsafePerformanceTimer {
readonly timeOrigin: number;
now: () => number;
}

0 comments on commit 2e91066

Please sign in to comment.