/** * OpenTelemetry SDK bootstrap for a DKG node (boot side, daemon-only consumer). * * Registers the global Tracer + Meter providers at daemon startup so that the * call-site facade in `@origintrail-official/dkg-core` (getTracer/withSpan/ * getMetrics) — used across agent/publisher/chain/sync — produces real spans and * metrics. When telemetry is disabled, or a signal has no endpoint, this * registers NOTHING: the core facade then talks to the API's built-in no-op * providers (zero cost, no outbound calls). * * Logs are NOT handled here — they stay on the hand-rolled `OtlpLogWorker` * (bounded buffer + retry + at-source redaction); the OTel Logs SDK is still * "Development". This module only wires traces + metrics, and shares ONE * Resource with the log worker so all three signals describe the same node. * * BROWSER SAFETY: the heavy Node-only OTel SDK packages (sdk-trace-node, * sdk-metrics, exporter-*-otlp-proto, resources) and `@origintrail-official/dkg-core` * are loaded via DYNAMIC import inside `initTelemetry`/`shutdownTelemetry`, never * statically. Only `@opentelemetry/api` (browser-safe, no Node built-ins) is a * static import. That keeps this module — and the package root that re-exports * it — free of server-only code in any static bundle graph: a browser bundler * never pulls the Node SDK because it sits behind a runtime `import()` that the * UI never triggers. */ import { type TelemetryResourceInput } from './telemetry-resource.js'; /** * Stable resource identity, shared by logs + traces + metrics. Single canonical * type — re-exported alias of `TelemetryResourceInput` (the shared resource * model in ./telemetry-resource) so logs and traces/metrics can't drift. */ export type TelemetryResource = TelemetryResourceInput; export interface OtlpSignalConfig { endpoint?: string; /** Bearer token → Authorization header. */ token?: string; headers?: Record; /** Per-signal opt-out. A signal is on only when it has an endpoint AND this is not false. */ enabled?: boolean; } export interface TelemetryInitConfig { /** Master gate. When false, nothing is registered. */ enabled?: boolean; resource?: TelemetryResource; traces?: OtlpSignalConfig & { sampleRatio?: number; }; metrics?: OtlpSignalConfig & { exportIntervalMs?: number; }; } /** * LEGACY config shape (pre-PR #1317 stub). Kept so old JS callers that pass * `{ enabled, metricsEndpoint, serviceName }` keep working — `initTelemetry` * normalizes it into `TelemetryInitConfig` (metricsEndpoint → metrics.endpoint). * @deprecated Use `TelemetryInitConfig` (traces/metrics signal blocks). */ export interface TelemetryConfig { enabled?: boolean; /** OTLP HTTP endpoint for metrics (e.g. http://localhost:4318/v1/metrics) */ metricsEndpoint?: string; /** Service name for resource attributes */ serviceName?: string; } /** * Initialize traces + metrics. No-op when disabled or when a signal has no * endpoint. Safe to call once at daemon boot. Idempotent (subsequent calls are * ignored once configured). Async because the Node OTel SDK is dynamically * imported (see the BROWSER SAFETY note above). */ export declare function initTelemetry(input: TelemetryInitConfig | TelemetryConfig): Promise; /** True once EITHER signal (traces or metrics) has registered a real provider. */ export declare function isTelemetryConfigured(): boolean; /** * Wall-clock reserve for the terminal flush in the daemon's graceful-shutdown * path. `SHUTDOWN_HARD_TIMEOUT_MS` is 15 s and the catch-up grace drain ahead * of this may already have consumed 5 s, so the flush gets a fixed 2 s — well * under the ~10 s remainder, leaving room for runner close, `agent.stop()`, * the final `shutdownTelemetry()` and the database close behind it. */ export declare const TERMINAL_FLUSH_BUDGET_MS = 2000; /** * Flush pending spans/metrics WITHOUT tearing anything down. * * Exists because `shutdownTelemetry()` is not a flush: it shuts the providers * down, clears the OTel API globals and calls `rebuildMetrics()`, so every * later `getMetrics()` binds to a no-op meter. The daemon needs a point in * shutdown where already-recorded terminal state is exported while the * provider stays LIVE, because work that runs afterwards (`agent.stop()` * quiescing parent-side sync) still emits the attempts, bytes and active time * that belong to those same terminal records. * * ## Why each side is bounded differently * * Neither provider is bounded by default, and they are not bounded the same * way (verified against the installed `@opentelemetry/sdk-*@2.8.0`): * * - `MetricReader.forceFlush(options)` applies **no timeout at all** when * `options.timeoutMillis` is absent — it just awaits `onForceFlush()`, whose * trailing `this._exporter.forceFlush()` is wrapped by nothing. Passing * `timeoutMillis` routes the whole thing through `callWithTimeout`, so the * argument — not this comment — is what bounds the meter. * - `BasicTracerProvider.forceFlush()` accepts **no arguments**; it reads the * constructor's `forceFlushTimeoutMillis` (default 30 000). The only way to * bound it per call is to race it. * * The two bounded legs run concurrently, so the aggregate wait is also the * budget. Deliberately there is NO extra outer race around both: an outer * bound would return inside the budget even if one leg's own bound were * removed, which would make the per-leg bounds untestable — and an untestable * bound is how the previous "bounded" flush turned out to have no bound. * * Never throws into the shutdown path: a timeout or rejection is logged and * teardown continues. Bounded effort, not a delivery guarantee — an exporter * that cannot flush inside the reserve still loses its last points. */ export declare function flushTelemetry(options?: { budgetMs?: number; log?: (message: string) => void; }): Promise; /** * Flush + shut down providers. Used both at daemon teardown AND when telemetry * is turned off via the runtime master gate, so it must FULLY reverse * `initTelemetry`: stop the exporters, then clear the OTel API globals so a * later `initTelemetry` (live re-enable) can register fresh providers — without * the `disable()` calls, the API keeps the first (now shut-down) provider and a * re-enable would silently no-op. Safe if never initialized; idempotent. * * Flush and shutdown run SEQUENTIALLY per provider, and that is not tidiness. * They used to be pushed into one array and awaited by a single `Promise.all`, * i.e. started concurrently against the same provider — and * `MeterProvider.forceFlush()` opens with `if (this._shutdown) { diag.warn(…); * return; }`. Whichever call won the race decided whether the flush happened at * all; it worked only because the synchronous `_shutdown` read happened to be * scheduled first. Awaiting the flush before starting the shutdown removes the * race instead of relying on scheduling order. */ export declare function shutdownTelemetry(): Promise; /** * @deprecated No-op compatibility shim for the pre-PR #1317 telemetry API. Node * metrics flow through the `@origintrail-official/dkg-core` facade * (`getMetrics()`), not this call. Kept so old callers don't break at import. */ export declare function recordGauge(_name: string, _value: number): void; /** * @deprecated No-op compatibility shim for the pre-PR #1317 telemetry API. Spans * are created via the core facade `withSpan()`/`getTracer()`. Kept so old * callers don't break at import. */ export declare function setOperationSpan(_operationId: string, _operationName: string): void; //# sourceMappingURL=telemetry.d.ts.map