76 lines · 3.0 KB · TypeScript
RawDownload
import * as Alchemy from "alchemy";import * as Axiom from "alchemy/Axiom";import * as Output from "alchemy/Output";import * as Layer from "effect/Layer";import * as Effect from "effect/Effect";import * as Redacted from "effect/Redacted";import { OtlpSerialization, OtlpTracer } from "effect/unstable/observability"; import { relayResourceNameForStage } from "./deploymentConfig.ts"; const relayRecentSpansQuery = (dataset: string) =>  [    `['${dataset}']`,    `| where isnotnull(span_id) or isnotnull(trace_id)`,    `| extend requestMethod = column_ifexists('attributes.http.request.method', ''), path = column_ifexists('attributes.url.path', ''), endpoint = column_ifexists('attributes.http.route', ''), statusCode = column_ifexists('attributes.http.response.status_code', 0), customAttributes = column_ifexists('attributes.custom', dynamic({}))`,    `| extend userId = customAttributes['user']['id']`,    `| project _time, name, trace_id, span_id, duration, requestMethod, path, statusCode, endpoint, userId`,    `| order by _time desc`,    `| limit 200`,  ].join("\n"); export const RelayObservability = Effect.gen(function* () {  const { stage } = yield* Alchemy.Stack;  const traces = yield* Axiom.Dataset("RelayTracesDataset", {    name: relayResourceNameForStage("t3-code-relay-traces", stage),    kind: "otel:traces:v1",    description: "T3 Code relay Worker HTTP request spans.",    retentionDays: 30,    useRetentionPeriod: true,  });   const ingestToken = yield* Axiom.ApiToken("RelayAxiomIngestToken", {    name: relayResourceNameForStage("t3-code-relay-otel-ingest", stage),    description: "Owned by Alchemy. Scoped OTLP ingest token for relay HTTP spans.",    datasetCapabilities: Output.map(traces.name, (dataset) => ({      [dataset]: { ingest: ["create" as const] },    })),  });   yield* Axiom.View("RelayRecentSpansView", {    name: relayResourceNameForStage("t3-code-relay-recent-spans", stage),    description: "Recent relay HTTP request spans.",    datasets: [traces.name],    aplQuery: Output.map(traces.name, relayRecentSpansQuery),  });   return { traces, ingestToken } as const;}); export const withSpanAttributes =  (attributes: Record<string, unknown>) =>  <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>    Effect.annotateCurrentSpan(attributes).pipe(      Effect.andThen(effect.pipe(Effect.annotateSpans(attributes))),    ); export const makeRelayTraceLayer = (input: {  readonly tracesEndpoint: string;  readonly tracesDatasetName: string;  readonly ingestToken: Redacted.Redacted<string>;}) =>  OtlpTracer.layer({    url: input.tracesEndpoint,    resource: {      serviceName: "t3-code-relay-worker",      attributes: {        "service.runtime": "cloudflare-worker",        "service.component": "relay",      },    },    headers: {      Authorization: `Bearer ${Redacted.value(input.ingestToken)}`,      "X-Axiom-Dataset": input.tracesDatasetName,    },    exportInterval: "1 second",  }).pipe(Layer.provide(OtlpSerialization.layerJson));