{"version":3,"file":"index.cjs","names":["LoggerlessTransport","FileStreamRotator"],"sources":["../src/LogFileRotationTransport.ts"],"sourcesContent":["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"],"mappings":"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"}