Skip to content
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
34 changes: 34 additions & 0 deletions infra/relay/src/http/Api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,40 @@ describe("relay request tracing", () => {
}),
);

it.effect("records the Cloudflare edge a request entered through on its server span", () =>
Effect.gen(function* () {
const spans: Array<Tracer.NativeSpan> = [];
const tracer = Tracer.make({
span: (options) => {
const span = new Tracer.NativeSpan(options);
spans.push(span);
return span;
},
});
const source = new Request("https://relay.test/v1/environments/env-1/connect", {
method: "POST",
});
Object.defineProperty(source, "cf", {
value: { colo: "JNB", country: "ZA", continent: "AF", asn: 37_100 },
});

yield* traceRelayHttpRequestWith(
Effect.succeed(HttpServerResponse.empty({ status: 204 })),
Layer.succeed(Tracer.Tracer, tracer),
).pipe(
Effect.provideService(
HttpServerRequest.HttpServerRequest,
HttpServerRequest.fromWeb(source),
),
);

const server = spans.find((span) => span.kind === "server");
expect(server?.attributes.get("relay.edge.colo")).toBe("JNB");
expect(server?.attributes.get("relay.edge.country")).toBe("ZA");
expect(server?.attributes.get("relay.edge.continent")).toBe("AF");
}),
);

it.effect("records one server span inside the worker's disabled HTTP tracer", () =>
Effect.gen(function* () {
const spans: Array<Tracer.NativeSpan> = [];
Expand Down
21 changes: 19 additions & 2 deletions infra/relay/src/http/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,21 @@ const relayRequestDeadline = <E, R>(

const SENDER_TRACE_HEADER = /^(traceparent|tracestate|b3|x-b3-.*)$/i;

// Where the request entered Cloudflare's network. The Worker runs near its
// database, not at this edge, so these are the only way to split relay
// latency by where users are.
const annotateRelayEdgeLocation = Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const cf: unknown = request.source instanceof Request ? Reflect.get(request.source, "cf") : null;
if (typeof cf !== "object" || cf === null) return;
const attributes: Record<string, string> = {};
for (const key of ["colo", "country", "continent"] as const) {
const value: unknown = Reflect.get(cf, key);
if (typeof value === "string" && value) attributes[`relay.edge.${key}`] = value;
}
yield* Effect.annotateCurrentSpan(attributes);
});

// The method-named server span is created below; this wrapper must not add another span.
export const traceRelayHttpRequest = Effect.fnUntraced(function* <E, R>(
httpEffect: Effect.Effect<
Expand All @@ -216,7 +231,8 @@ export const traceRelayHttpRequest = Effect.fnUntraced(function* <E, R>(
>,
) {
const request = yield* HttpServerRequest.HttpServerRequest;
const traced = appendRelayTraceContextResponseHeader.pipe(
const traced = annotateRelayEdgeLocation.pipe(
Effect.andThen(appendRelayTraceContextResponseHeader),
Effect.andThen(relayRequestDeadline(httpEffect)),
);
if (!isRelayHookPath(request.url)) {
Expand All @@ -238,7 +254,8 @@ export const traceRelayHttpRequest = Effect.fnUntraced(function* <E, R>(
),
});
return yield* HttpMiddleware.tracer(
appendRelayTraceContextResponseHeader.pipe(
annotateRelayEdgeLocation.pipe(
Effect.andThen(appendRelayTraceContextResponseHeader),
Effect.andThen(
relayRequestDeadline(
httpEffect.pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request)),
Expand Down
Loading