import { BasicLogConfigurations, LogLevel, LoggingData } from "../types.mjs"; import { LogChannel } from "../log-channel.mjs"; //#region ../@warlock.js/logger/src/channels/sentry-log.d.ts /** * Sentry severity levels — the `@sentry/node` `SeverityLevel` union. */ type SentrySeverityLevel = "fatal" | "error" | "warning" | "log" | "info" | "debug"; /** * The Sentry `Scope` surface used while building a single event. */ interface SentryScopeLike { setLevel(level: SentrySeverityLevel): void; setTags(tags: Record): void; setContext(name: string, context: Record | null): void; } /** * A Sentry breadcrumb, as passed to `addBreadcrumb`. */ type SentryBreadcrumb = { category?: string; message?: string; level?: SentrySeverityLevel; data?: Record; }; /** * The subset of the `@sentry/node` API `SentryLog` calls. The `@sentry/node` * namespace satisfies this shape, so an app can pass it straight through as * `client`; a test (or a custom forwarder) can supply a compatible stand-in. */ interface SentryForwarder { captureException(exception: unknown): string; captureMessage(message: string, level?: SentrySeverityLevel): string; addBreadcrumb(breadcrumb: SentryBreadcrumb): void; withScope(callback: (scope: SentryScopeLike) => void): void; flush(timeout?: number): Promise; } /** * Sentry initialization options. Mirrors the common `@sentry/node` `NodeOptions` * fields; the index signature keeps any other SDK option valid without coupling * to the SDK's types. */ type SentryInitOptions = { dsn?: string; environment?: string; release?: string; sampleRate?: number; [key: string]: unknown; }; type SentryLogConfig = BasicLogConfigurations & { /** * Reuse an already-initialized Sentry instance — typically the `@sentry/node` * namespace from an app that already calls `Sentry.init(...)`. When set, the * channel forwards through it and never imports or re-initializes the SDK. */ client?: SentryForwarder; /** * Initialize Sentry from these options instead of reusing a host client. The * channel lazily imports `@sentry/node` and calls `Sentry.init(options)` once, * guarded so it never clobbers an existing client. */ options?: SentryInitOptions; /** * Levels delivered as Sentry *events* (these consume the error quota). Every * other level is recorded as a breadcrumb that rides along with the next * event, costing no quota. * * @default ["fatal", "error", "warn"] */ eventLevels?: LogLevel[]; /** * Milliseconds `flush()` waits for the transport to drain on shutdown. * * @default 2000 */ flushTimeout?: number; }; /** * Forwards log entries to Sentry. * * Entries at an `eventLevels` level (`error` / `warn` by default) become Sentry * **events**: an `Error` message via `captureException` (preserving the real * stack), any other message via `captureMessage`. Every other level becomes a * **breadcrumb** — buffered and attached to the next event, consuming no error * quota. `module` / `action` are attached as searchable tags and the entry's * `context` as a structured Sentry context. * * The SDK is an optional peer: pass an existing `client` (reused as-is) or * `options` (the channel lazily imports `@sentry/node` and initializes it). On * graceful shutdown, `await log.flush()` drains pending events via * `Sentry.flush(timeout)`. * * @example * // Existing app — reuse the initialized Sentry client * import * as Sentry from "@sentry/node"; * log.addChannel(new SentryLog({ client: Sentry })); * * @example * // New app — let the channel initialize Sentry * log.addChannel(new SentryLog({ options: { dsn: process.env.SENTRY_DSN } })); */ declare class SentryLog extends LogChannel { /** * {@inheritdoc} */ name: string; /** * {@inheritdoc} */ description: string; /** * {@inheritdoc} */ protected defaultConfigurations: SentryLogConfig; /** * The resolved forwarder — the injected `client` or the lazily-imported * `@sentry/node` namespace. `undefined` until `init()` resolves, and when the * SDK is absent (then `log()` surfaces the install message once). */ private sentry?; /** * Guards the one-time "@sentry/node is not installed" notice so a missing SDK * doesn't spam stderr on every entry. */ private warnedMissing; /** * Resolve an injected `client` (the Sentry namespace) **synchronously**, so an * entry logged on the same tick as construction — e.g. at app boot, before * the base schedules `init()` on the next tick via `setTimeout(0)` — is not * silently dropped. The `options` (lazy-import) path is inherently async and * still resolves in `init()`. */ constructor(configurations?: SentryLogConfig); /** * Resolve the forwarder: reuse the injected client, otherwise lazily import * `@sentry/node` and (only when explicit `options` are supplied and no client * exists yet) initialize it. Never throws — the base runs `init()` inside an * un-awaited `setTimeout`, so a throw would become an unhandled rejection and * `isInitialized` would never flip; a missing SDK is reported from `log()`. */ protected init(): Promise; /** * {@inheritdoc} */ log(data: LoggingData): Promise; /** * Drain pending Sentry events. Bounded by `flushTimeout` so an unreachable * Sentry can never hang a graceful shutdown. No-op when the SDK is absent. */ flush(): Promise; /** * Whether the level should be sent as a Sentry event (vs a breadcrumb). */ private isEventLevel; /** * Send an entry as a Sentry event. An `Error` message goes through * `captureException` so Sentry parses the real stack and groups properly; * any other message goes through `captureMessage`. `module` / `action` are * attached as tags and `context` as a structured context, scoped to this * event only via `withScope`. */ private captureEvent; /** * Map a logger level to a Sentry severity. `success` has no Sentry * equivalent, so it is reported as informational. */ private toSentryLevel; /** * Coerce a message into the string Sentry's APIs expect — an `Error`'s * `.message`, a string as-is, anything else safely JSON-serialized. */ private toText; /** * Surface the install instructions exactly once when the SDK is absent. The * logger can't log through itself here, so this writes to stderr — matching * how the file channels report write failures. */ private reportMissingSdk; } //#endregion export { SentryBreadcrumb, SentryForwarder, SentryInitOptions, SentryLog, SentryLogConfig, SentryScopeLike }; //# sourceMappingURL=sentry-log.d.mts.map