@loglayer/transport-log-file-rotation
Version:
Log file rotation transport for the LogLayer logging library.
1 lines • 31.3 kB
Source Map (JSON)
{"version":3,"sources":["/home/runner/work/loglayer/loglayer/packages/transports/log-file-rotation/dist/index.cjs","../src/LogFileRotationTransport.ts"],"names":[],"mappings":"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","file":"/home/runner/work/loglayer/loglayer/packages/transports/log-file-rotation/dist/index.cjs","sourcesContent":[null,"import type { WriteStream } from \"node:fs\";\nimport { createReadStream, createWriteStream, writeFileSync } from \"node:fs\";\nimport { access, unlink } from \"node:fs/promises\";\nimport { pipeline } from \"node:stream/promises\";\nimport { createGzip } from \"node:zlib\";\nimport type { LoggerlessTransportConfig, LogLayerTransportParams } from \"@loglayer/transport\";\nimport { LoggerlessTransport } from \"@loglayer/transport\";\nimport FileStreamRotator from \"file-stream-rotator\";\n\ninterface FileStreamRotatorOptions {\n filename: string;\n frequency?: string;\n verbose?: boolean;\n date_format?: string;\n size?: string;\n max_logs?: string;\n audit_file?: string;\n end_stream?: boolean;\n extension?: string;\n create_symlink?: boolean;\n symlink_name?: string;\n utc?: boolean;\n audit_hash_type?: \"md5\" | \"sha256\";\n file_options?: {\n flags?: string;\n encoding?: string;\n mode?: number;\n };\n}\n\nexport interface LogFileRotationCallbacks {\n /**\n * Called when a log file is rotated\n * @param oldFile - The path to the old log file\n * @param newFile - The path to the new log file\n */\n onRotate?: (oldFile: string, newFile: string) => void;\n /**\n * Called when a new log file is created\n * @param newFile - The path to the new log file\n */\n onNew?: (newFile: string) => void;\n /**\n * Called when a log file is opened\n */\n onOpen?: () => void;\n /**\n * Called when a log file is closed\n */\n onClose?: () => void;\n /**\n * Called when an error occurs\n * @param error - The error that occurred\n */\n onError?: (error: Error) => void;\n /**\n * Called when the stream is finished\n */\n onFinish?: () => void;\n /**\n * Called when a log file is removed due to retention policy\n * @param info - Information about the removed log file\n */\n onLogRemoved?: (info: { date: number; name: string; hash: string }) => void;\n}\n\nexport interface LogFileRotationFieldNames {\n /**\n * Field name for the log level\n * @default \"level\"\n */\n level?: string;\n /**\n * Field name for the log message\n * @default \"message\"\n */\n message?: string;\n /**\n * Field name for the timestamp\n * @default \"timestamp\"\n */\n timestamp?: string;\n}\n\nexport interface LogFileRotationLevelMap {\n /**\n * Mapping for the 'fatal' log level\n * @example 60 or \"FATAL\"\n */\n fatal?: string | number;\n /**\n * Mapping for the 'error' log level\n * @example 50 or \"ERROR\"\n */\n error?: string | number;\n /**\n * Mapping for the 'warn' log level\n * @example 40 or \"WARN\"\n */\n warn?: string | number;\n /**\n * Mapping for the 'info' log level\n * @example 30 or \"INFO\"\n */\n info?: string | number;\n /**\n * Mapping for the 'debug' log level\n * @example 20 or \"DEBUG\"\n */\n debug?: string | number;\n /**\n * Mapping for the 'trace' log level\n * @example 10 or \"TRACE\"\n */\n trace?: string | number;\n}\n\nexport interface LogFileRotationBatchConfig {\n /**\n * Maximum number of log entries to queue before writing.\n * Default: 1000\n */\n size?: number;\n /**\n * Maximum time in milliseconds to wait before writing queued logs.\n * Default: 5000 (5 seconds)\n */\n timeout?: number;\n}\n\nexport interface LogFileRotationTransportConfig extends LoggerlessTransportConfig {\n /**\n * The filename pattern to use for the log files.\n * Supports date format using numerical values.\n * Example: \"./logs/application-%DATE%.log\"\n */\n filename: string;\n /**\n * Static data to be included in every log entry.\n * Can be either:\n * - A function that returns an object containing static data\n * - A direct object containing static data\n *\n * The data will be merged with the log entry before any other data.\n * If using a function, it will be called for each log entry.\n * @example\n * ```typescript\n * // Using a function\n * staticData: () => ({\n * hostname: hostname(),\n * pid: process.pid\n * })\n *\n * // Using an object\n * staticData: {\n * hostname: hostname(),\n * pid: process.pid\n * }\n * ```\n */\n staticData?: (() => Record<string, any>) | Record<string, any>;\n /**\n * The frequency of rotation. Can be:\n * - 'daily' for daily rotation\n * - 'date' for rotation on date format change\n * - '[1-30]m' for rotation every X minutes\n * - '[1-12]h' for rotation every X hours\n */\n frequency?: string;\n /**\n * The date format to use in the filename.\n * Uses single characters for each date component:\n * - 'Y' for full year\n * - 'M' for month\n * - 'D' for day\n * - 'H' for hour\n * - 'm' for minutes\n * - 's' for seconds\n *\n * Common patterns:\n * - For daily rotation: use \"YMD\" (creates files like app-20240117.log)\n * - For hourly/minute rotation: use \"YMDHm\" (creates files like app-202401171430.log)\n *\n * @default \"YMD\"\n */\n dateFormat?: string;\n /**\n * The size at which to rotate.\n * Examples: \"10M\", \"100K\", \"100B\"\n * If frequency is specified, this will be ignored.\n */\n size?: string;\n /**\n * Maximum number of logs to keep.\n * Can be a number of files or days (e.g., \"10d\" for 10 days)\n */\n maxLogs?: string | number;\n /**\n * Location to store the log audit file.\n * If not set, it will be stored in the root of the application.\n */\n auditFile?: string;\n /**\n * File extension to be appended to the filename.\n * Useful when using size restrictions as the rotation adds a count at the end.\n */\n extension?: string;\n /**\n * Create a tailable symlink to the current active log file.\n * Default: false\n */\n createSymlink?: boolean;\n /**\n * Name to use when creating the symbolic link.\n * Default: 'current.log'\n */\n symlinkName?: string;\n /**\n * Use UTC time for date in filename.\n * Default: false\n */\n utc?: boolean;\n /**\n * Use specified hashing algorithm for audit.\n * Default: 'md5'\n * Use 'sha256' for FIPS compliance.\n */\n auditHashType?: \"md5\" | \"sha256\";\n /**\n * File mode to be used when creating log files.\n * Default: 0o640 (user read/write, group read, others none)\n */\n fileMode?: number;\n /**\n * Options passed to the file stream.\n * See: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options\n */\n fileOptions?: {\n flags?: string;\n encoding?: string;\n mode?: number;\n };\n /**\n * Event callbacks for various file stream events\n */\n callbacks?: LogFileRotationCallbacks;\n /**\n * Custom field names for the log entry JSON\n * Default: { level: \"level\", message: \"message\", data: \"data\", timestamp: \"timestamp\" }\n */\n fieldNames?: LogFileRotationFieldNames;\n /**\n * Delimiter between log entries.\n * Default: \"\\n\"\n */\n delimiter?: string;\n /**\n * Custom function to generate timestamps for log entries.\n * Can return either a string (e.g., ISO string) or a number (e.g., Unix timestamp)\n * If not provided, defaults to new Date().toISOString()\n */\n timestampFn?: () => string | number;\n /**\n * Custom mapping for log levels.\n * Each log level can be mapped to either a string or number.\n * Example: { error: 50, warn: 40, info: 30, debug: 20, trace: 10, fatal: 60 }\n * Example: { error: \"ERROR\", warn: \"WARN\", info: \"INFO\", debug: \"DEBUG\", trace: \"TRACE\", fatal: \"FATAL\" }\n */\n levelMap?: LogFileRotationLevelMap;\n /**\n * Whether to compress rotated log files using gzip.\n * When enabled, rotated files will be compressed with .gz extension.\n * Default: false\n */\n compressOnRotate?: boolean;\n /**\n * Whether to enable verbose mode in the underlying file-stream-rotator.\n * When enabled, the rotator will log detailed information about its operations.\n * Default: false\n */\n verbose?: boolean;\n /**\n * Batch processing configuration.\n * If defined, batch processing will be enabled.\n * When batching is enabled, logs are queued in memory and written to disk in batches.\n * Queued logs are automatically flushed in the following situations:\n * - When the batch size is reached\n * - When the batch timeout is reached\n * - When the transport is disposed\n * - When the process exits (including SIGINT and SIGTERM signals)\n */\n batch?: LogFileRotationBatchConfig;\n}\n\n/**\n * A transport that writes logs to rotating files with support for time-based and size-based rotation.\n * Features include:\n * - Automatic log file rotation based on time (hourly, daily) or size\n * - Support for date patterns in filenames using numerical values (YYYY, MM, DD, etc.)\n * - Size-based rotation with support for KB, MB, and GB units\n * - Compression of rotated log files using gzip\n * - Maximum file count or age-based retention\n * - Automatic cleanup of old log files\n * - Batch processing of logs for improved performance\n * - Safe handling of process termination signals\n *\n * Each instance must have a unique filename to prevent race conditions.\n * If you need multiple loggers to write to the same file, share the same transport instance between them.\n */\nexport class LogFileRotationTransport extends LoggerlessTransport implements Disposable {\n /** Registry of active filenames to prevent multiple transports writing to the same file */\n private static activeFilenames = new Set<string>();\n /** The current write stream for the log file */\n private stream: WriteStream;\n /** Custom field names for log entries */\n private fieldNames: Required<LogFileRotationFieldNames>;\n /** Delimiter between log entries */\n private delimiter: string;\n /** Function to generate timestamps for log entries */\n private timestampFn: () => string | number;\n /** Custom mapping for log levels */\n private levelMap: LogFileRotationLevelMap;\n /** Whether to compress rotated files */\n private compressOnRotate: boolean;\n /** Whether a file is currently being compressed */\n private isCompressing: boolean;\n /** The base filename pattern for log files */\n private filename: string;\n /** Static data to be included in every log entry */\n private staticData?: (() => Record<string, any>) | Record<string, any>;\n /** Whether batch processing is enabled */\n private batchEnabled: boolean;\n /** Maximum number of log entries to queue before writing */\n private batchSize: number;\n /** Maximum time in milliseconds to wait before writing queued logs */\n private batchTimeout: number;\n /** Queue of log entries waiting to be written */\n private batchQueue: string[];\n /** Timer for batch flush timeout */\n private batchTimer: NodeJS.Timeout | null;\n /** Whether the transport is being disposed */\n private isDisposing: boolean;\n /** Event callbacks for various file stream events */\n private callbacks?: LogFileRotationCallbacks;\n /** Frequency of rotation (daily, hourly, etc.) */\n private frequency?: string;\n /** Whether to enable verbose mode */\n private verbose?: boolean;\n /** Date format for filename patterns */\n private dateFormat?: string;\n /** Size threshold for rotation */\n private size?: string;\n /** Maximum number of log files to keep */\n private maxLogs?: string | number;\n /** Path to the audit file */\n private auditFile?: string;\n /** File extension for log files */\n private extension?: string;\n /** Whether to create a symlink to current log */\n private createSymlink?: boolean;\n /** Name of the symlink file */\n private symlinkName?: string;\n /** Whether to use UTC time in filenames */\n private utc?: boolean;\n /** Hash algorithm for audit file */\n private auditHashType?: \"md5\" | \"sha256\";\n /** Options for file streams */\n private fileOptions?: {\n flags?: string;\n encoding?: string;\n mode?: number;\n };\n /** File mode to be used when creating log files */\n private fileMode?: number;\n\n /**\n * Generates the options for FileStreamRotator consistently across the transport\n * @returns FileStreamRotatorOptions object\n * @private\n */\n private getRotatorOptions(): FileStreamRotatorOptions {\n return {\n filename: this.filename,\n frequency: this.frequency,\n verbose: this.verbose ?? false,\n date_format: this.dateFormat,\n size: this.size,\n max_logs: this.maxLogs?.toString(),\n audit_file: this.auditFile || undefined,\n end_stream: true,\n extension: this.extension,\n create_symlink: this.createSymlink,\n symlink_name: this.symlinkName,\n utc: this.utc,\n audit_hash_type: this.auditHashType,\n file_options: {\n flags: \"a\",\n encoding: \"utf8\",\n mode: this.fileMode ?? 0o640,\n ...this.fileOptions,\n },\n };\n }\n\n /**\n * Creates a new LogFileRotationTransport instance.\n * @param params - Configuration options for the transport\n * @throws {Error} If the filename is already in use by another transport instance\n */\n constructor(params: LogFileRotationTransportConfig) {\n super(params);\n\n // Check if filename is already in use\n if (LogFileRotationTransport.activeFilenames.has(params.filename)) {\n throw new Error(\n `LogFileRotationTransport: Filename \"${params.filename}\" is already in use by another instance. To use the same file for multiple loggers, share the same transport instance between them.`,\n );\n }\n\n // Register the filename\n this.filename = params.filename;\n LogFileRotationTransport.activeFilenames.add(this.filename);\n\n // Set up field names with defaults\n this.fieldNames = {\n level: params.fieldNames?.level ?? \"level\",\n message: params.fieldNames?.message ?? \"message\",\n timestamp: params.fieldNames?.timestamp ?? \"timestamp\",\n };\n\n // Set up delimiter\n this.delimiter = params.delimiter ?? \"\\n\";\n\n // Set up timestamp function\n this.timestampFn = params.timestampFn ?? (() => new Date().toISOString());\n\n // Set up level mapping\n this.levelMap = params.levelMap ?? {};\n\n // Set up compression\n this.compressOnRotate = params.compressOnRotate ?? false;\n this.isCompressing = false;\n\n // Set up batching\n this.batchEnabled = !!params.batch;\n this.batchSize = params.batch?.size ?? 1000;\n this.batchTimeout = params.batch?.timeout ?? 5000;\n this.batchQueue = [];\n this.batchTimer = null;\n this.isDisposing = false;\n\n // Store other options\n this.callbacks = params.callbacks;\n this.frequency = params.frequency;\n this.verbose = params.verbose;\n this.dateFormat = params.dateFormat;\n this.size = params.size;\n this.maxLogs = params.maxLogs;\n this.auditFile = params.auditFile;\n this.extension = params.extension;\n this.createSymlink = params.createSymlink;\n this.symlinkName = params.symlinkName;\n this.utc = params.utc;\n this.auditHashType = params.auditHashType;\n this.fileOptions = params.fileOptions;\n this.fileMode = params.fileMode;\n this.staticData = params.staticData;\n\n // Set up exit handler for flushing\n if (this.batchEnabled) {\n // Handle normal process exit\n process.on(\"beforeExit\", () => {\n if (!this.isDisposing) {\n this.flush();\n }\n });\n\n // Handle SIGINT (Ctrl+C) and SIGTERM\n const handleSignal = (signal: string) => {\n if (!this.isDisposing) {\n // Synchronously flush logs to ensure they're written before exit\n this.flushSync();\n // Remove the filename from registry\n LogFileRotationTransport.activeFilenames.delete(this.filename);\n // Exit with the original signal\n process.exit(signal === \"SIGINT\" ? 130 : 143);\n }\n };\n\n process.on(\"SIGINT\", () => handleSignal(\"SIGINT\"));\n process.on(\"SIGTERM\", () => handleSignal(\"SIGTERM\"));\n }\n\n // Only create the stream if not in batch mode or if we have logs to write\n if (!this.batchEnabled) {\n this.initStream(this.getRotatorOptions());\n }\n }\n\n /**\n * Initializes the write stream and sets up event listeners.\n * This is called either immediately if batching is disabled,\n * or lazily when the first batch needs to be written if batching is enabled.\n * @param options - Options for the file stream rotator\n * @private\n */\n private initStream(options: FileStreamRotatorOptions): void {\n // FileStreamRotator.getStream() returns a WriteStream-compatible object\n this.stream = FileStreamRotator.getStream(options) as unknown as WriteStream;\n\n // Set up event listeners if callbacks are provided\n if (this.callbacks) {\n const { onRotate, onNew, onOpen, onClose, onError, onFinish, onLogRemoved } = this.callbacks;\n\n // Wrap the onRotate callback to handle compression\n if (this.compressOnRotate) {\n this.stream.on(\"rotate\", async (oldFile: string, newFile: string) => {\n try {\n this.isCompressing = true;\n const compressedPath = await this.compressFile(oldFile);\n await unlink(oldFile);\n onRotate?.(compressedPath, newFile);\n } catch (error) {\n this.callbacks?.onError?.(error as Error);\n } finally {\n this.isCompressing = false;\n }\n });\n } else if (onRotate) {\n this.stream.on(\"rotate\", onRotate);\n }\n\n if (onNew) {\n this.stream.on(\"new\", onNew);\n }\n if (onOpen) {\n this.stream.on(\"open\", onOpen);\n }\n if (onClose) {\n this.stream.on(\"close\", onClose);\n }\n if (onError) {\n this.stream.on(\"error\", onError);\n }\n if (onFinish) {\n this.stream.on(\"finish\", onFinish);\n }\n if (onLogRemoved) {\n this.stream.on(\"logRemoved\", onLogRemoved);\n }\n }\n }\n\n /**\n * Generates a unique path for a compressed log file.\n * If a file with .gz extension already exists, appends timestamp and counter.\n * @param filePath - The original log file path\n * @returns The unique path for the compressed file\n * @private\n */\n private async getUniqueCompressedFilePath(filePath: string): Promise<string> {\n let finalPath = `${filePath}.gz`;\n let counter = 0;\n\n try {\n while (true) {\n try {\n await access(finalPath);\n counter++;\n finalPath = `${filePath}.${Date.now()}.${counter}.gz`;\n } catch {\n break;\n }\n }\n } catch (_error) {\n finalPath = `${filePath}.${Date.now()}.gz`;\n }\n\n return finalPath;\n }\n\n /**\n * Compresses a log file using gzip.\n * @param filePath - Path to the file to compress\n * @returns Path to the compressed file\n * @private\n */\n private async compressFile(filePath: string): Promise<string> {\n const gzPath = await this.getUniqueCompressedFilePath(filePath);\n const gzip = createGzip();\n const source = createReadStream(filePath);\n const destination = createWriteStream(gzPath);\n\n await pipeline(source, gzip, destination);\n return gzPath;\n }\n\n /**\n * Flushes queued log entries to disk asynchronously.\n * This is used for normal batch processing operations.\n * @private\n */\n private flush(): void {\n if (!this.batchEnabled || this.batchQueue.length === 0) {\n return;\n }\n\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n // Initialize stream if it hasn't been created yet\n if (!this.stream) {\n this.initStream(this.getRotatorOptions());\n }\n\n const batchContent = this.batchQueue.join(\"\");\n this.stream.write(batchContent);\n this.batchQueue = [];\n }\n\n /**\n * Synchronously flush logs to disk.\n * This is used during process termination (SIGINT/SIGTERM) to ensure logs are written\n * before the process exits. This method uses synchronous file I/O to guarantee that\n * logs are written even during abrupt process termination.\n * @private\n */\n private flushSync(): void {\n if (!this.batchEnabled || this.batchQueue.length === 0) {\n return;\n }\n\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n // Initialize stream if it hasn't been created yet\n if (!this.stream) {\n this.initStream(this.getRotatorOptions());\n }\n\n const batchContent = this.batchQueue.join(\"\");\n // Use writeFileSync to ensure logs are written before process exit\n const rotator = this.stream as unknown as { currentFile: string };\n if (rotator.currentFile) {\n writeFileSync(rotator.currentFile, batchContent, { flag: \"a\" });\n }\n this.batchQueue = [];\n }\n\n /**\n * Schedules a batch flush operation.\n * This creates a timer that will flush the batch after the configured timeout.\n * The timer is unref'd to prevent keeping the process alive.\n * @private\n */\n private scheduleBatchFlush(): void {\n if (!this.batchTimer && !this.isDisposing) {\n this.batchTimer = setTimeout(() => {\n this.flush();\n }, this.batchTimeout);\n\n // Prevent timer from keeping the process alive\n if (this.batchTimer.unref) {\n this.batchTimer.unref();\n }\n }\n }\n\n /**\n * Processes and writes a log entry.\n * If batching is enabled, the entry is queued and written based on batch settings.\n * If batching is disabled, the entry is written immediately.\n * @param params - The log entry parameters\n * @returns The original messages array\n */\n shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams) {\n const logEntry = {\n [this.fieldNames.level]: this.levelMap[logLevel as keyof LogFileRotationLevelMap] ?? logLevel,\n [this.fieldNames.message]: messages.join(\" \") || \"\",\n [this.fieldNames.timestamp]: this.timestampFn(),\n ...(this.staticData ? (typeof this.staticData === \"function\" ? this.staticData() : this.staticData) : {}),\n ...(hasData ? data : {}),\n };\n\n const logString = `${JSON.stringify(logEntry)}${this.delimiter}`;\n\n if (this.batchEnabled) {\n this.batchQueue.push(logString);\n\n if (this.batchQueue.length >= this.batchSize) {\n this.flush();\n } else {\n this.scheduleBatchFlush();\n }\n } else {\n this.stream.write(logString);\n }\n\n return messages;\n }\n\n /**\n * Disposes of the transport, cleaning up resources and flushing any remaining logs.\n * This method:\n * 1. Prevents new batch flushes from being scheduled\n * 2. Cancels any pending batch flush\n * 3. Flushes any remaining logs\n * 4. Waits for any in-progress compression to complete\n * 5. Closes the write stream\n * 6. Removes the filename from the registry\n */\n [Symbol.dispose](): void {\n if (this.stream || this.batchEnabled) {\n this.isDisposing = true;\n\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n // Flush any remaining logs\n if (this.batchEnabled) {\n this.flush();\n }\n\n const checkAndEnd = () => {\n if (!this.isCompressing) {\n if (this.stream) {\n this.stream.end();\n }\n // Remove the filename from registry when disposed\n LogFileRotationTransport.activeFilenames.delete(this.filename);\n } else {\n setTimeout(checkAndEnd, 100);\n }\n };\n checkAndEnd();\n }\n }\n}\n"]}