import { makeLocalFileTracer, makeTraceSink } from "@t3tools/shared/observability"; import { parsePersistedServerObservabilitySettings } from "@t3tools/shared/serverSettings"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Logger from "effect/Logger"; import * as Option from "effect/Option"; import * as References from "effect/References"; import * as Tracer from "effect/Tracer"; import { OtlpSerialization, OtlpTracer } from "effect/unstable/observability"; import * as DesktopBackendOutputLogModule from "./DesktopBackendOutputLog.ts"; import * as DesktopEnvironment from "./DesktopEnvironment.ts"; const DESKTOP_TRACE_BATCH_WINDOW_MS = 200; export { DesktopBackendOutputLog } from "./DesktopBackendOutputLog.ts"; export type DesktopLogAnnotations = Record; export interface DesktopComponentLogger { readonly annotate: ( effect: Effect.Effect, annotations?: DesktopLogAnnotations, ) => Effect.Effect; readonly logDebug: (message: string, annotations?: DesktopLogAnnotations) => Effect.Effect; readonly logInfo: (message: string, annotations?: DesktopLogAnnotations) => Effect.Effect; readonly logWarning: ( message: string, annotations?: DesktopLogAnnotations, ) => Effect.Effect; readonly logError: (message: string, annotations?: DesktopLogAnnotations) => Effect.Effect; } export function makeComponentLogger(component: string): DesktopComponentLogger { const annotate: DesktopComponentLogger["annotate"] = (effect, annotations) => effect.pipe( Effect.annotateLogs({ component, ...annotations, }), ); return { annotate, logDebug: (message, annotations) => annotate(Effect.logDebug(message), annotations), logInfo: (message, annotations) => annotate(Effect.logInfo(message), annotations), logWarning: (message, annotations) => annotate(Effect.logWarning(message), annotations), logError: (message, annotations) => annotate(Effect.logError(message), annotations), }; } const readPersistedOtlpTracesUrl = Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const environment = yield* DesktopEnvironment.DesktopEnvironment; const raw = yield* fileSystem.readFileString(environment.serverSettingsPath).pipe(Effect.option); if (Option.isNone(raw)) { return Option.none(); } const parsed = parsePersistedServerObservabilitySettings(raw.value); return Option.fromNullishOr(parsed.otlpTracesUrl); }); const resolveOtlpTracesUrl = Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; if (Option.isSome(environment.otlpTracesUrl)) { return environment.otlpTracesUrl; } return yield* readPersistedOtlpTracesUrl; }); const desktopLoggerLayer = Layer.mergeAll( Logger.layer([Logger.consolePretty(), Logger.tracerLogger], { mergeWithExisting: false }), Layer.succeed(References.MinimumLogLevel, "Info"), ); const tracerLayer = Layer.unwrap( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const otlpTracesUrl = yield* resolveOtlpTracesUrl; const tracePath = environment.path.join(environment.logDir, "desktop.trace.ndjson"); const sink = yield* makeTraceSink({ filePath: tracePath, maxBytes: DesktopBackendOutputLogModule.DESKTOP_LOG_FILE_MAX_BYTES, maxFiles: DesktopBackendOutputLogModule.DESKTOP_LOG_FILE_MAX_FILES, batchWindowMs: DESKTOP_TRACE_BATCH_WINDOW_MS, }); const delegate = Option.isNone(otlpTracesUrl) ? undefined : yield* OtlpTracer.make({ url: otlpTracesUrl.value, exportInterval: `${environment.otlpExportIntervalMs} millis`, resource: { serviceName: "desktop", attributes: { "service.runtime": "desktop", "service.mode": environment.isDevelopment ? "development" : "packaged", }, }, }); const tracer = yield* makeLocalFileTracer({ filePath: tracePath, maxBytes: DesktopBackendOutputLogModule.DESKTOP_LOG_FILE_MAX_BYTES, maxFiles: DesktopBackendOutputLogModule.DESKTOP_LOG_FILE_MAX_FILES, batchWindowMs: DESKTOP_TRACE_BATCH_WINDOW_MS, sink, ...(delegate ? { delegate } : {}), }); return Layer.succeed(Tracer.Tracer, tracer); }), ).pipe(Layer.provideMerge(OtlpSerialization.layerJson)); export const layer = Layer.mergeAll( DesktopBackendOutputLogModule.layer, desktopLoggerLayer, tracerLayer, Layer.succeed(Tracer.MinimumTraceLevel, "Info"), Layer.succeed(References.TracerTimingEnabled, true), );