import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Tracer from "effect/Tracer"; import type { HttpClient } from "effect/unstable/http"; import { OtlpSerialization, OtlpTracer } from "effect/unstable/observability"; export interface RelayClientTracingConfig { readonly tracesUrl: string; readonly tracesDataset: string; readonly tracesToken: string; } export interface RelayClientTracingResource { readonly serviceName: string; readonly serviceVersion?: string; readonly runtime: string; readonly client: string; readonly component?: string; } export class RelayClientTracer extends Context.Reference( "@t3tools/shared/relayTracing/RelayClientTracer", { defaultValue: () => Option.none(), }, ) {} export const withRelayClientTracing = ( effect: Effect.Effect, ): Effect.Effect => RelayClientTracer.pipe( Effect.flatMap( Option.match({ onNone: () => effect, onSome: (tracer) => effect.pipe(Effect.provideService(Tracer.Tracer, tracer)), }), ), ); export function makeRelayClientTracingLayer( config: RelayClientTracingConfig | null, resource: RelayClientTracingResource, ): Layer.Layer { if (config === null) { return Layer.succeed(RelayClientTracer, Option.none()); } const tracerLayer = OtlpTracer.layer({ url: config.tracesUrl, headers: { Authorization: `Bearer ${config.tracesToken}`, "X-Axiom-Dataset": config.tracesDataset, }, resource: { serviceName: resource.serviceName, serviceVersion: resource.serviceVersion, attributes: { "service.runtime": resource.runtime, "service.component": resource.component ?? "relay-client", "t3.client.surface": resource.client, }, }, }).pipe(Layer.provide(OtlpSerialization.layerJson)); return Layer.effect(RelayClientTracer, Tracer.Tracer.pipe(Effect.map(Option.some))).pipe( Layer.provide(tracerLayer), ); }