Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analytics to asset-worker #7053

Merged
merged 1 commit into from
Oct 23, 2024
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
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;
}
Loading