Skip to content

Commit

Permalink
Add jaeger bindings to asset-worker (#7761)
Browse files Browse the repository at this point in the history
  • Loading branch information
WillTaylorDev authored Jan 14, 2025
1 parent dc2ab35 commit bb85c9a
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-walls-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/workers-shared": minor
---

Adds jaeger tracing for asset-worker.
13 changes: 12 additions & 1 deletion packages/workers-shared/asset-worker/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {
import { getHeaders } from "./utils/headers";
import type { AssetConfig } from "../../utils/types";
import type EntrypointType from "./index";
import type { Env } from "./index";

export const handleRequest = async (
request: Request,
env: Env,
configuration: Required<AssetConfig>,
exists: typeof EntrypointType.prototype.unstable_exists,
getByETag: typeof EntrypointType.prototype.unstable_getByETag
Expand Down Expand Up @@ -52,7 +54,16 @@ export const handleRequest = async (
return new InternalServerErrorResponse(new Error("Unknown action"));
}

const asset = await getByETag(intent.asset.eTag);
const asset = await env.JAEGER.enterSpan("getByETag", async (span) => {
span.setTags({
pathname,
eTag: intent.asset.eTag,
status: intent.asset.status,
});

return await getByETag(intent.asset.eTag);
});

const headers = getHeaders(intent.asset.eTag, asset.contentType, request);

const strongETag = `"${intent.asset.eTag}"`;
Expand Down
40 changes: 30 additions & 10 deletions packages/workers-shared/asset-worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import { applyConfigurationDefaults } from "./configuration";
import { decodePath, getIntent, handleRequest } from "./handler";
import { InternalServerErrorResponse } from "./responses";
import { getAssetWithMetadataFromKV } from "./utils/kv";
import type { AssetConfig, UnsafePerformanceTimer } from "../../utils/types";
import { mockJaegerBinding } from "./utils/mocks";
import type {
AssetConfig,
JaegerTracing,
UnsafePerformanceTimer,
} from "../../utils/types";
import type { ColoMetadata, Environment, ReadyAnalytics } from "./types";

type Env = {
export type Env = {
/*
* ASSETS_MANIFEST is a pipeline binding to an ArrayBuffer containing the
* binary-encoded site manifest
Expand All @@ -26,10 +31,11 @@ type Env = {
CONFIG: AssetConfig;

SENTRY_DSN: string;

SENTRY_ACCESS_CLIENT_ID: string;
SENTRY_ACCESS_CLIENT_SECRET: string;

JAEGER: JaegerTracing;

ENVIRONMENT: Environment;
ANALYTICS: ReadyAnalytics;
COLO_METADATA: ColoMetadata;
Expand All @@ -56,6 +62,11 @@ export default class extends WorkerEntrypoint<Env> {
const startTimeMs = performance.now();

try {
if (!this.env.JAEGER) {
// For wrangler tests, if we don't have a jaeger binding, default to a mocked binding
this.env.JAEGER = mockJaegerBinding();
}

sentry = setupSentry(
request,
this.ctx,
Expand All @@ -74,8 +85,8 @@ export default class extends WorkerEntrypoint<Env> {
sentry.setUser({ userAgent: userAgent, colo: colo });
}

const url = new URL(request.url);
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,
Expand All @@ -89,12 +100,21 @@ export default class extends WorkerEntrypoint<Env> {
});
}

return handleRequest(
request,
config,
this.unstable_exists.bind(this),
this.unstable_getByETag.bind(this)
);
return await this.env.JAEGER.enterSpan("handleRequest", async (span) => {
span.setTags({
hostname: url.hostname,
eyeballPath: url.pathname,
env: this.env.ENVIRONMENT,
});

return handleRequest(
request,
this.env,
config,
this.unstable_exists.bind(this),
this.unstable_getByETag.bind(this)
);
});
} catch (err) {
const response = new InternalServerErrorResponse(err as Error);

Expand Down
32 changes: 32 additions & 0 deletions packages/workers-shared/asset-worker/src/utils/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { JaegerTracing, Span } from "../../../utils/types";

export function mockJaegerBindingSpan(): Span {
return {
addLogs: () => {},
setTags: () => {},
end: () => {},
isRecording: true,
};
}

export function mockJaegerBinding(): JaegerTracing {
return {
enterSpan: (_, span, ...args) => {
return span(mockJaegerBindingSpan(), ...args);
},
getSpanContext: () => ({
traceId: "test-trace",
spanId: "test-span",
parentSpanId: "test-parent-span",
traceFlags: 0,
}),
runWithSpanContext: (_, callback, ...args) => {
return callback(...args);
},

traceId: "test-trace",
spanId: "test-span",
parentSpanId: "test-parent-span",
cfTraceIdHeader: "test-trace:test-span:0",
};
}
41 changes: 41 additions & 0 deletions packages/workers-shared/asset-worker/tests/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { vi } from "vitest";
import { applyConfigurationDefaults } from "../src/configuration";
import { handleRequest } from "../src/handler";
import { mockJaegerBinding } from "../src/utils/mocks";
import type { AssetConfig } from "../../utils/types";

describe("[Asset Worker] `handleRequest`", () => {
Expand All @@ -17,8 +18,14 @@ describe("[Asset Worker] `handleRequest`", () => {
contentType: "text/html",
});

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

const response = await handleRequest(
new Request("https://example.com/"),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
configuration,
exists,
getByETag
Expand All @@ -40,10 +47,16 @@ describe("[Asset Worker] `handleRequest`", () => {
contentType: "text/html",
});

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

const response = await handleRequest(
new Request("https://example.com/", {
headers: { "If-None-Match": `"${eTag}"` },
}),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
configuration,
exists,
getByETag
Expand All @@ -65,10 +78,16 @@ describe("[Asset Worker] `handleRequest`", () => {
contentType: "text/html",
});

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

const response = await handleRequest(
new Request("https://example.com/", {
headers: { "If-None-Match": `W/"${eTag}"` },
}),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
configuration,
exists,
getByETag
Expand All @@ -90,10 +109,16 @@ describe("[Asset Worker] `handleRequest`", () => {
contentType: "text/html",
});

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

const response = await handleRequest(
new Request("https://example.com/", {
headers: { "If-None-Match": "a fake etag!" },
}),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
configuration,
exists,
getByETag
Expand All @@ -110,9 +135,15 @@ describe("[Asset Worker] `handleRequest`", () => {
"/test.html": "dddddddddd",
};

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

// Attempt to path traverse down to the root /test within asset-server
let response = await handleRequest(
new Request("https://example.com/blog/../test"),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
applyConfigurationDefaults({}),
async (pathname: string) => {
if (pathname.startsWith("/blog/")) {
Expand All @@ -133,6 +164,8 @@ describe("[Asset Worker] `handleRequest`", () => {
// Attempt to path traverse down to the root /test within asset-server
response = await handleRequest(
new Request("https://example.com/blog/%2E%2E/test"),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
applyConfigurationDefaults({}),
async (pathname: string) => {
if (pathname.startsWith("/blog/")) {
Expand Down Expand Up @@ -170,9 +203,15 @@ describe("[Asset Worker] `handleRequest`", () => {
contentType: "text/html",
});

const mockEnv = {
JAEGER: mockJaegerBinding(),
};

// first malformed URL should return 404 as no match above
const response = await handleRequest(
new Request("https://example.com/%A0"),
// @ts-expect-error Empty config default to using mocked jaeger
mockEnv,
configuration,
exists,
getByEtag
Expand All @@ -182,6 +221,8 @@ describe("[Asset Worker] `handleRequest`", () => {
// but second malformed URL should return 307 as it matches and then redirects
const response2 = await handleRequest(
new Request("https://example.com/%A0%A0"),
// @ts-expect-error Empty config default to using mocked jaeger
{},
configuration,
exists,
getByEtag
Expand Down
37 changes: 37 additions & 0 deletions packages/workers-shared/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,40 @@ export interface UnsafePerformanceTimer {
readonly timeOrigin: number;
now: () => number;
}

export interface JaegerTracing {
enterSpan<T extends unknown[], R = void>(
name: string,
span: (s: Span, ...args: T) => R,
...args: T
): R;
getSpanContext(): SpanContext | null;
runWithSpanContext<T extends unknown[]>(
spanContext: SpanContext | null,
callback: (...args: T) => unknown,
...args: T
): unknown;

readonly traceId: string | null;
readonly spanId: string | null;
readonly parentSpanId: string | null;
readonly cfTraceIdHeader: string | null;
}

export interface Span {
addLogs(logs: JaegerRecord): void;
setTags(tags: JaegerRecord): void;
end(): void;

isRecording: boolean;
}

export interface SpanContext {
traceId: string;
spanId: string;
parentSpanId: string;
traceFlags: number;
}

export type JaegerValue = string | number | boolean;
export type JaegerRecord = Record<string, JaegerValue>;

0 comments on commit bb85c9a

Please sign in to comment.