feat!: API options object support to control sentry handling; removal of logger.sentry.
This commit is contained in:
+236
-24
@@ -1,77 +1,289 @@
|
||||
/** A severity threshold, ordered from most to least verbose. */
|
||||
/**
|
||||
* Severity threshold for log filtering, ordered from most to least verbose.
|
||||
*
|
||||
* | Level | Meaning |
|
||||
* |-----------|----------------------------------------------|
|
||||
* | `trace` | Extremely detailed diagnostics |
|
||||
* | `debug` | Development diagnostics |
|
||||
* | `info` | Routine operational messages |
|
||||
* | `warn` | Unexpected but recoverable conditions |
|
||||
* | `error` | Failures that need attention |
|
||||
* | `silent` | Suppresses all console-bound output |
|
||||
*
|
||||
* A logger emits events at or above its configured threshold (e.g. `warn`
|
||||
* allows `warn` and `error`).
|
||||
*/
|
||||
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "silent";
|
||||
|
||||
/**
|
||||
* Deployment stage used to pick a default log level when none is configured
|
||||
* explicitly or via runtime overrides.
|
||||
*
|
||||
* Defaults:
|
||||
* - `development` → `trace`
|
||||
* - `staging` → `warn`
|
||||
* - `production` → `error`
|
||||
*/
|
||||
export type LoggerEnvironment = "development" | "staging" | "production";
|
||||
|
||||
/**
|
||||
* A value passed as a log argument after the message.
|
||||
*
|
||||
* Prefer a zero-argument function for expensive payloads: it is only invoked
|
||||
* when the event is actually emitted, so suppressed logs avoid the work.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* logger.debug("state", () => expensiveSnapshot());
|
||||
* ```
|
||||
*/
|
||||
export type LogData = unknown | (() => unknown);
|
||||
|
||||
/** A structured event after its level policy has allowed it to be emitted. */
|
||||
/**
|
||||
* Per-call options for `trace` / `debug` / `log` / `info` / `warn` / `error`
|
||||
* (and `assert`). Not supported on console utilities such as `group`.
|
||||
*
|
||||
* When the last argument is a plain object whose keys are only these option
|
||||
* names, it is treated as options rather than log data.
|
||||
*/
|
||||
export interface LogCallOptions {
|
||||
/**
|
||||
* Force this event to Sentry Logs, ignoring the Sentry Logs threshold.
|
||||
* No-op for `error` (errors become Issues, not Logs) and in development.
|
||||
*/
|
||||
sentry?: boolean;
|
||||
/**
|
||||
* Skip creating a Sentry Issue for an `error`-level event.
|
||||
* No-op at lower levels.
|
||||
*/
|
||||
suppressSentry?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A structured log event after level and namespace policy have allowed it
|
||||
* (or after it was selected for Sentry delivery).
|
||||
*/
|
||||
export interface LogEvent {
|
||||
/** Severity of this event. Never `silent`. */
|
||||
level: Exclude<LogLevel, "silent">;
|
||||
/**
|
||||
* Colon-joined namespace for this logger (e.g. `"WIDGET:Button"`).
|
||||
* Omitted when the logger was created without a namespace.
|
||||
*/
|
||||
namespace?: string;
|
||||
/**
|
||||
* Evaluated arguments for the event (call options already stripped).
|
||||
* Lazy `() => unknown` functions have already been invoked.
|
||||
*/
|
||||
arguments: readonly unknown[];
|
||||
/** Wall-clock time when the event was created. */
|
||||
timestamp: Date;
|
||||
/** Whether this event was explicitly selected for Sentry capture. */
|
||||
sendToSentry: boolean;
|
||||
/** Whether the default console sink should render this event. */
|
||||
/** Deployment stage from {@link LoggerOptions.environment}. */
|
||||
environment: LoggerEnvironment;
|
||||
/**
|
||||
* `true` when the event should be written to Sentry Logs
|
||||
* (`sentry.logger.*`), not as an Issue.
|
||||
*/
|
||||
sendToSentryLogs: boolean;
|
||||
/**
|
||||
* `true` when the event should be captured as a Sentry Issue
|
||||
* (`captureException` / `captureMessage`).
|
||||
*/
|
||||
sendToSentryIssue: boolean;
|
||||
/**
|
||||
* `true` when the event passed the logger's level/namespace policy and
|
||||
* should be rendered by console-oriented sinks.
|
||||
*/
|
||||
sendToConsole: boolean;
|
||||
}
|
||||
|
||||
/** A destination for enabled log events. */
|
||||
/**
|
||||
* Destination that receives enabled {@link LogEvent}s.
|
||||
*
|
||||
* Provide custom sinks via {@link LoggerOptions.sinks}, or use
|
||||
* {@link createConsoleSink} / `createSentrySink` from `@mifi/logger/sentry`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const sink: LogSink = {
|
||||
* emit(event) {
|
||||
* if (event.sendToConsole) analytics.track("log", event);
|
||||
* },
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export interface LogSink {
|
||||
/**
|
||||
* Handle a single log event.
|
||||
* Implementations should respect `sendToConsole` / `sendToSentryLogs` /
|
||||
* `sendToSentryIssue` as needed.
|
||||
*/
|
||||
emit(event: LogEvent): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link createLogger}.
|
||||
*
|
||||
* Precedence for the effective console level (highest wins):
|
||||
* 1. Explicit {@link LoggerOptions.level}
|
||||
* 2. Browser `sessionStorage.showLoggingFor` (or {@link LoggerOptions.sessionStorage})
|
||||
* 3. `MIFI_LOG_LEVEL` / `MIFI_LOG_NAMESPACES` from {@link LoggerOptions.env} or `process.env`
|
||||
* 4. Default for {@link LoggerOptions.environment}
|
||||
*/
|
||||
export interface LoggerOptions {
|
||||
/**
|
||||
* Deployment stage. Selects the default log level when no override applies.
|
||||
* @see LoggerEnvironment
|
||||
*/
|
||||
environment: LoggerEnvironment;
|
||||
/**
|
||||
* Optional namespace label(s). A string is used as-is; an array is joined
|
||||
* with `:` (empty segments dropped). Shown in console output as
|
||||
* `[A][B]` for `"A:B"`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* createLogger({ environment: "development", namespace: "API" });
|
||||
* createLogger({ environment: "development", namespace: ["WIDGET", "Button"] });
|
||||
* ```
|
||||
*/
|
||||
namespace?: string | readonly string[];
|
||||
/** Runtime used for the default destination policy. It is inferred when omitted. */
|
||||
/**
|
||||
* Runtime used for the default destination policy (console vs Sentry-only
|
||||
* in production browsers). Inferred from `globalThis.window` when omitted.
|
||||
*/
|
||||
runtime?: "browser" | "node";
|
||||
/** Hard override. It takes precedence over runtime environment defaults. */
|
||||
/**
|
||||
* Hard level override. Takes precedence over session storage, env vars,
|
||||
* and environment defaults.
|
||||
*/
|
||||
level?: LogLevel;
|
||||
/** Browser-only session override source; defaults to global sessionStorage when available. */
|
||||
/**
|
||||
* Browser-only source for the `showLoggingFor` override key.
|
||||
* Defaults to `globalThis.sessionStorage` when available.
|
||||
*
|
||||
* Expected value format matches {@link parseLoggingOverride}:
|
||||
* `debug` or `debug:WIDGET,API`.
|
||||
*/
|
||||
sessionStorage?: Pick<Storage, "getItem">;
|
||||
/** Node/container environment source; defaults to process.env when available. */
|
||||
/**
|
||||
* Node/container environment variable map. Defaults to `process.env`
|
||||
* when available.
|
||||
*
|
||||
* Recognized keys:
|
||||
* - `MIFI_LOG_LEVEL` — one of the {@link LogLevel} values
|
||||
* - `MIFI_LOG_NAMESPACES` — optional comma-separated namespace filters
|
||||
*/
|
||||
env?: Record<string, string | undefined>;
|
||||
/** Production error destination. Browser errors go only here; Node errors also go to stderr. */
|
||||
/**
|
||||
* Staging/production Sentry destination (typically from `createSentrySink`).
|
||||
* Ignored in development (nothing is sent to Sentry).
|
||||
*
|
||||
* - Production **browser**: default sinks are Sentry only (no console).
|
||||
* - Production **Node**: console (stderr for errors) and Sentry.
|
||||
* - Staging: console and Sentry.
|
||||
*
|
||||
* Ignored when {@link LoggerOptions.sinks} is provided.
|
||||
*/
|
||||
sentry?: LogSink | false;
|
||||
/** Complete destination override. When provided, `sentry` and default console behavior are not used. */
|
||||
/**
|
||||
* Complete destination list. When set, replaces the default console/Sentry
|
||||
* policy entirely — {@link LoggerOptions.sentry} is not used.
|
||||
*/
|
||||
sinks?: readonly LogSink[];
|
||||
}
|
||||
|
||||
/** Explicit Sentry reporting methods. These bypass the logger's normal level threshold. */
|
||||
export interface SentryLogger {
|
||||
trace(...arguments_: readonly unknown[]): void;
|
||||
debug(...arguments_: readonly unknown[]): void;
|
||||
log(...arguments_: readonly unknown[]): void;
|
||||
info(...arguments_: readonly unknown[]): void;
|
||||
warn(...arguments_: readonly unknown[]): void;
|
||||
error(...arguments_: readonly unknown[]): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Console-shaped logger with namespaces, lazy arguments, child loggers,
|
||||
* and optional Sentry Issues / Logs delivery.
|
||||
*
|
||||
* Standard methods (`trace` … `error`) accept an optional trailing
|
||||
* {@link LogCallOptions} object. Console utility methods (`group`, `time`,
|
||||
* `table`, …) are gated by the same console policy and call through to
|
||||
* `globalThis.console` when enabled — they do not accept {@link LogCallOptions}.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const logger = createLogger({ environment: "development", namespace: "WIDGET" });
|
||||
* logger.info("ready");
|
||||
* logger.child("Button").debug("clicked", { id: 1 });
|
||||
* logger.warn("investigate", { orderId }, { sentry: true });
|
||||
* logger.error("expected", err, { suppressSentry: true });
|
||||
* ```
|
||||
*/
|
||||
export interface Logger {
|
||||
/**
|
||||
* Colon-joined namespace for this instance, if any.
|
||||
* Immutable; use {@link Logger.child} to nest further segments.
|
||||
*/
|
||||
readonly namespace?: string;
|
||||
/** Sends selected events to a configured Sentry sink without changing the default policy for other logs. */
|
||||
readonly sentry: SentryLogger;
|
||||
/**
|
||||
* Returns a new logger that appends `namespace` to this logger's namespace.
|
||||
*
|
||||
* @param namespace - Segment to append (e.g. `"Button"` → `"WIDGET:Button"`).
|
||||
* @returns A new {@link Logger} sharing the same options and sinks.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const widget = createLogger({ environment: "development", namespace: "WIDGET" });
|
||||
* const button = widget.child("Button"); // namespace === "WIDGET:Button"
|
||||
* ```
|
||||
*/
|
||||
child(namespace: string): Logger;
|
||||
/** Emit a `trace` event when the level policy allows it. */
|
||||
trace(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
trace(...arguments_: readonly unknown[]): void;
|
||||
/** Emit a `debug` event when the level policy allows it. */
|
||||
debug(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
debug(...arguments_: readonly unknown[]): void;
|
||||
/** Alias of {@link Logger.info}. */
|
||||
log(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
log(...arguments_: readonly unknown[]): void;
|
||||
/** Emit an `info` event when the level policy allows it. */
|
||||
info(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
info(...arguments_: readonly unknown[]): void;
|
||||
/** Emit a `warn` event when the level policy allows it. */
|
||||
warn(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
warn(...arguments_: readonly unknown[]): void;
|
||||
/** Emit an `error` event when the level policy allows it. */
|
||||
error(message?: unknown, data?: LogData, options?: LogCallOptions): void;
|
||||
error(...arguments_: readonly unknown[]): void;
|
||||
/**
|
||||
* When `condition` is falsy, emits an `error` with `"Assertion failed"`
|
||||
* followed by any extra arguments (optional trailing {@link LogCallOptions}).
|
||||
*
|
||||
* @param condition - Truthy values pass silently.
|
||||
* @param arguments_ - Extra context included with the failure event.
|
||||
*/
|
||||
assert(condition: unknown, ...arguments_: readonly unknown[]): void;
|
||||
/** Starts a console group when the level policy allows `info`. */
|
||||
group(...arguments_: readonly unknown[]): void;
|
||||
/** Starts a collapsed console group when the level policy allows `info`. */
|
||||
groupCollapsed(...arguments_: readonly unknown[]): void;
|
||||
/** Ends the current console group when the level policy allows `info`. */
|
||||
groupEnd(): void;
|
||||
/** Logs an object with interactive inspection when the level policy allows `info`. */
|
||||
dir(item?: unknown, options?: unknown): void;
|
||||
/** Logs XML/HTML as an interactive tree when the level policy allows `info`. */
|
||||
dirxml(...arguments_: readonly unknown[]): void;
|
||||
/** Renders tabular data when the level policy allows `info`. */
|
||||
table(tabularData?: unknown, properties?: readonly string[]): void;
|
||||
/** Clears the console when the level policy allows `info`. */
|
||||
clear(): void;
|
||||
/** Increments a named counter when the level policy allows `info`. */
|
||||
count(label?: string): void;
|
||||
/** Resets a named counter when the level policy allows `info`. */
|
||||
countReset(label?: string): void;
|
||||
/** Starts a named timer when the level policy allows `info`. */
|
||||
time(label?: string): void;
|
||||
/** Logs elapsed time for a named timer when the level policy allows `info`. */
|
||||
timeLog(label?: string, ...arguments_: readonly unknown[]): void;
|
||||
/** Stops a named timer and logs elapsed time when the level policy allows `info`. */
|
||||
timeEnd(label?: string): void;
|
||||
/** Adds a timestamp marker to the performance timeline when allowed. */
|
||||
timeStamp(label?: string): void;
|
||||
/** Starts a CPU profile (where supported) when the level policy allows `info`. */
|
||||
profile(label?: string): void;
|
||||
/** Ends a CPU profile (where supported) when the level policy allows `info`. */
|
||||
profileEnd(label?: string): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user