logixlysia
Version:
🦊 Logixlysia is a logger for Elysia
169 lines (168 loc) • 6.42 kB
TypeScript
import { Elysia } from "elysia";
import { Logger as PinoLogger, LoggerOptions as PinoLoggerOptions } from "pino";
type Pino = PinoLogger<never, boolean>;
type RequestInfo = Request;
type LogLevel = "DEBUG" | "INFO" | "WARNING" | "ERROR";
interface StoreData {
beforeTime: bigint;
}
interface LogixlysiaStore {
beforeTime?: bigint;
logger: Logger;
pino: Pino;
}
interface Transport {
log: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void | Promise<void>;
}
interface LogRotationConfig {
compress?: boolean;
compression?: "gzip";
/**
* Rotate at a fixed interval, e.g. '1d', '12h'.
*/
interval?: string;
/**
* Keep at most N files or keep files for a duration like '7d'.
*/
maxFiles?: number | string;
/**
* Max log file size before rotation, e.g. '10m', '5k', or a byte count.
*/
maxSize?: string | number;
}
interface LogFilter {
/**
* Array of log levels to allow. If specified, only logs with these levels will be processed.
* If not specified, all log levels will be allowed.
*/
level?: LogLevel[];
}
/**
* Configuration for pino-pretty transport output.
*
* - `true`: Enable pretty printing with default options
* - `false` or `undefined`: Disable pretty printing
* - Object: Enable with custom pino-pretty options (colorize, translateTime, messageKey, errorKey, etc.)
*
* @see https://github.com/pinojs/pino-pretty#options
*/
type PrettyPrintConfig = boolean | Record<string, unknown>;
type LogPreset = "dev" | "prod" | "json";
interface Options {
config?: {
showStartupMessage?: boolean;
startupMessageFormat?: "simple" | "banner";
useColors?: boolean;
ip?: boolean;
timestamp?: {
translateTime?: string;
};
customLogFormat?: string;
/** Service name shown in `{service}` token (e.g. evlog-style `[my-app]`). */
service?: string;
/** Duration (ms) below this uses green; default 500. */
slowThreshold?: number;
/** Duration (ms) at or above this uses red + `{speed}` badge; default 1000. */
verySlowThreshold?: number;
/** Render `data.context` as tree lines under the main log line; default true. */
showContextTree?: boolean;
/** How many object nesting levels to expand in the context tree; default 1. */
contextDepth?: number;
/** Include query parameters in the logged URL path; default false. */
logQueryParams?: boolean;
/** Skip automatic WebSocket lifecycle logs from `wrapWs`; default false. */
disableWebSocketLogging?: boolean;
logFilter?: LogFilter;
transports?: Transport[];
useTransportsOnly?: boolean;
disableInternalLogger?: boolean;
disableFileLogging?: boolean;
logFilePath?: string;
logRotation?: LogRotationConfig;
/**
* Automatically redact sensitive information (PII) from logs.
* Masks emails, IP addresses, Luhn-valid payment card numbers, and JWTs in strings and deeply nested objects.
*/
autoRedact?: boolean;
pino?: (PinoLoggerOptions & {
prettyPrint?: PrettyPrintConfig;
}) | undefined;
};
/**
* Opinionated defaults for common environments.
* Explicit `config` fields override preset values.
*/
preset?: LogPreset;
}
interface Logger {
debug: (request: RequestInfo, message: string, context?: Record<string, unknown>) => void;
error: (request: RequestInfo, message: string, context?: Record<string, unknown>) => void;
getContext: (key: RequestInfo | object) => Readonly<Record<string, unknown>>;
handleHttpError: (request: RequestInfo, error: unknown, store: StoreData) => void;
info: (request: RequestInfo, message: string, context?: Record<string, unknown>) => void;
log: (level: LogLevel, request: RequestInfo, data: Record<string, unknown>, store: StoreData) => void;
mergeContext: (key: RequestInfo | object, partial: Record<string, unknown>) => void;
pino: Pino;
warn: (request: RequestInfo, message: string, context?: Record<string, unknown>) => void;
}
interface LogixlysiaContext {
request: Request;
store: LogixlysiaStore;
}
/** HTTP `Request` or WebSocket instance for accumulated context. */
type ContextKey = Request | object;
interface RequestContextStore {
clearContext: (key: ContextKey) => void;
getContext: (key: ContextKey) => Readonly<Record<string, unknown>>;
mergeContext: (key: ContextKey, partial: Record<string, unknown>) => void;
}
interface WebSocketLike {
readonly data?: {
store?: {
logger?: Logger;
};
};
readonly id?: string;
}
interface WsHandlerHooks<
TMessage = unknown,
TWs extends WebSocketLike = WebSocketLike
> {
close?: (ws: TWs) => void;
message?: (ws: TWs, message: TMessage) => void;
open?: (ws: TWs) => void;
}
declare const createWsHandlerWrapper: (options: Options, logger: Logger, contextStore: RequestContextStore) => <
TMessage,
TWs extends WebSocketLike,
const THooks extends WsHandlerHooks<TMessage, TWs>
>(path: string, hooks: THooks) => THooks;
/** Applies preset defaults; explicit `config` keys override preset values. */
declare const resolveOptions: (options?: Options) => Options;
import pino from "pino";
declare const createLogger: (options?: Options, pinoFactory?: typeof pino, externalContextStore?: RequestContextStore) => Logger;
/** Plugin entry: shares one request-context store across the Elysia lifecycle. */
declare const createPluginLogger: (options: Options, contextStore: RequestContextStore) => Logger;
/**
* Empty singleton slots must not use `Record<string, never>`: intersecting that with Elysia's `Context`
* makes every key (including `store`) become `never` because each key is merged with `never`.
*/
interface EmptyElysiaSlot {
readonly __logixlysiaEmpty?: never;
}
/**
* Explicit singleton without Elysia's `SingletonBase` `Record<string, unknown>` on decorator/derive/resolve so
* merged `Context` and WebSocket `ws.data` keep precise keys after `.use(logixlysia())`.
*/
interface LogixlysiaSingleton {
decorator: EmptyElysiaSlot;
derive: EmptyElysiaSlot;
resolve: EmptyElysiaSlot;
store: LogixlysiaStore;
}
type Logixlysia = Elysia<"", LogixlysiaSingleton>;
type LogixlysiaPlugin = Logixlysia & {
wrapWs: ReturnType<typeof createWsHandlerWrapper>;
};
declare const logixlysia: (rawOptions?: Options) => LogixlysiaPlugin;
export { resolveOptions, logixlysia, logixlysia as default, createWsHandlerWrapper, createPluginLogger, createLogger, WsHandlerHooks, Transport, StoreData, Pino, Options, LogixlysiaStore, LogixlysiaSingleton, LogixlysiaPlugin, LogixlysiaContext, Logixlysia, Logger, LogPreset, LogLevel, EmptyElysiaSlot };