import { type LevelName, LogLevels } from "./levels.ts";import type { LogRecord } from "./logger.ts";import { BaseHandler, type BaseHandlerOptions } from "./base_handler.ts";import { writeAllSync } from "../io/write_all.ts";
const PAGE_SIZE = 4096;export type LogMode = "a" | "w" | "x";
export interface FileHandlerOptions extends BaseHandlerOptions { filename: string; mode?: LogMode;}
export class FileHandler extends BaseHandler { protected _file: Deno.FsFile | undefined; protected _buf: Uint8Array = new Uint8Array(PAGE_SIZE); protected _pointer = 0; protected _filename: string; protected _mode: LogMode; protected _openOptions: Deno.OpenOptions; protected _encoder: TextEncoder = new TextEncoder(); #unloadCallback = (() => { this.destroy(); }).bind(this);
constructor(levelName: LevelName, options: FileHandlerOptions) { super(levelName, options); this._filename = options.filename; this._mode = options.mode ? options.mode : "a"; this._openOptions = { createNew: this._mode === "x", create: this._mode !== "x", append: this._mode === "a", truncate: this._mode !== "a", write: true, }; }
override setup() { this._file = Deno.openSync(this._filename, this._openOptions); this.#resetBuffer();
addEventListener("unload", this.#unloadCallback); }
override handle(logRecord: LogRecord) { super.handle(logRecord);
if (logRecord.level > LogLevels.ERROR) { this.flush(); } }
override log(msg: string) { const bytes = this._encoder.encode(msg + "\n"); if (bytes.byteLength > this._buf.byteLength - this._pointer) { this.flush(); } if (bytes.byteLength > this._buf.byteLength) { writeAllSync(this._file!, bytes); } else { this._buf.set(bytes, this._pointer); this._pointer += bytes.byteLength; } }
flush() { if (this._pointer > 0 && this._file) { let written = 0; while (written < this._pointer) { written += this._file.writeSync( this._buf.subarray(written, this._pointer), ); } this.#resetBuffer(); } }
#resetBuffer() { this._pointer = 0; }
override destroy() { this.flush(); this._file?.close(); this._file = undefined; removeEventListener("unload", this.#unloadCallback); }}