diff --git a/api/src/traced_router.rs b/api/src/traced_router.rs index 26f5f9f9..37b15625 100644 --- a/api/src/traced_router.rs +++ b/api/src/traced_router.rs @@ -125,7 +125,8 @@ impl< "http.url" = ?uri, "http.user_agent" = user_agent, "http.status_code" = field::Empty, - "otel.status_code" = "ok" + "otel.status_code" = "ok", + "otel.kind" = "server" ); if self.is_internal { diff --git a/frontend/routes/_middleware.ts b/frontend/routes/_middleware.ts index 5d030210..e6218680 100644 --- a/frontend/routes/_middleware.ts +++ b/frontend/routes/_middleware.ts @@ -26,7 +26,7 @@ const tracing = define.middleware(async (ctx) => { return resp; } finally { const end = new Date(); - ctx.state.span.record(ctx.url.pathname, start, end, attributes); + ctx.state.span.record(ctx.url.pathname, start, end, attributes, "SERVER"); } }); diff --git a/frontend/routes/index.tsx b/frontend/routes/index.tsx index d0e16f1b..c35f511c 100644 --- a/frontend/routes/index.tsx +++ b/frontend/routes/index.tsx @@ -257,16 +257,32 @@ export const handler = define.handlers({ anonymous: true, }), (async () => { + const span = ctx.state.span.child(); + const start = new Date(); + const attributes: Record = { + "http.url": "https://deno.com/blog/json?tag=JSR", + "http.method": "GET", + }; let posts: Post[] = []; try { const jsrPosts = await fetch("https://deno.com/blog/json?tag=JSR", { signal: AbortSignal.timeout(1000), }); + attributes["http.status_code"] = BigInt(jsrPosts.status); if (jsrPosts.ok) { posts = await jsrPosts.json() as Post[]; } - } catch (_e) { - // ignore + } catch (e) { + attributes["error"] = "true"; + attributes["error.message"] = String((e as Error).message); + } finally { + span.record( + "fetch_dotcom_posts", + start, + new Date(), + attributes, + "CLIENT", + ); } return posts; })(), diff --git a/frontend/utils/api.ts b/frontend/utils/api.ts index d1ac7f10..498d3aac 100644 --- a/frontend/utils/api.ts +++ b/frontend/utils/api.ts @@ -259,6 +259,7 @@ export class API { "error.message": result.message, }), }, + "CLIENT", ); } return result; diff --git a/frontend/utils/tracing.ts b/frontend/utils/tracing.ts index a5defac9..88d14fef 100644 --- a/frontend/utils/tracing.ts +++ b/frontend/utils/tracing.ts @@ -52,6 +52,8 @@ const OTLP_ENDPOINT = Deno.env.get("OTLP_ENDPOINT"); const FLUSH_INTERVAL = 1000; // 5s +export type SpanKind = "SERVER" | "CLIENT" | "INTERNAL"; + interface RecordedSpan { traceId: string; parentSpanId: string | null; @@ -60,8 +62,15 @@ interface RecordedSpan { endTime: Date; displayName: string; attributes: Record; + spanKind: SpanKind; } +const OTLP_SPAN_KIND: Record = { + INTERNAL: 1, + SERVER: 2, + CLIENT: 3, +}; + const BATCH_SPAN_IMMEDIATE_FLUSH_LEN = 100; const BATCH_SPAN_OVERFLOW_LEN = 1000; @@ -164,6 +173,7 @@ export class Tracer { startTime: span.startTime, endTime: span.endTime, attributes: { attributeMap }, + spanKind: span.spanKind, } satisfies Span; }); await this.#cloudTrace!.projectsTracesBatchWrite( @@ -198,6 +208,7 @@ export class Tracer { parentSpanId: span.parentSpanId, spanId: span.spanId, name: span.displayName, + kind: OTLP_SPAN_KIND[span.spanKind], startTimeUnixNano: span.startTime.getTime() * 1e6, endTimeUnixNano: span.endTime.getTime() * 1e6, attributes, @@ -325,6 +336,7 @@ export class TraceSpan { startTime: Date, endTime: Date, attributes: Record, + spanKind: SpanKind, ) { if (!this.isSampled) return; this.#tracer.recordSpan({ @@ -335,6 +347,7 @@ export class TraceSpan { endTime, displayName, attributes, + spanKind, }); }