From 45c674c5cc1f5b10cdc17a33cea8d0cf32110b79 Mon Sep 17 00:00:00 2001 From: Dogukan Karasakal Date: Tue, 5 Nov 2024 22:19:32 +0100 Subject: [PATCH 1/5] docs(log): document FileHandler --- _tools/check_docs.ts | 1 + log/file_handler.ts | 191 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 182 insertions(+), 10 deletions(-) diff --git a/_tools/check_docs.ts b/_tools/check_docs.ts index f104275e8eb7..e2457d645029 100644 --- a/_tools/check_docs.ts +++ b/_tools/check_docs.ts @@ -73,6 +73,7 @@ const ENTRY_POINTS = [ "../json/mod.ts", "../jsonc/mod.ts", "../log/base_handler.ts", + "../log/file_handler.ts", "../log/warn.ts", "../log/critical.ts", "../log/debug.ts", diff --git a/log/file_handler.ts b/log/file_handler.ts index c6e1dad1f85f..3799d8088b56 100644 --- a/log/file_handler.ts +++ b/log/file_handler.ts @@ -13,11 +13,25 @@ import { pointerSymbol, } from "./_file_handler_symbols.ts"; +/** Supported log modes for FileHandlerOptions {@linkcode FileHandlerOptions.mode}. */ export type LogMode = "a" | "w" | "x"; +/** Options for {@linkcode FileHandler}. */ export interface FileHandlerOptions extends BaseHandlerOptions { + /** + * The filename to output to. + */ filename: string; /** + * Log mode for the handler. Behavior of the log modes is as follows: + * + * - `'a'` - Default mode. Appends new log messages to the end of an existing log + * file, or create a new log file if none exists. + * - `'w'` - Upon creation of the handler, any existing log file will be removed + * and a new one created. + * - `'x'` - This will create a new log file and throw an error if one already + * exists. + * * @default {"a"} */ mode?: LogMode; @@ -30,35 +44,111 @@ export interface FileHandlerOptions extends BaseHandlerOptions { } /** - * This handler will output to a file using an optional mode (default is `a`, - * e.g. append). The file will grow indefinitely. It uses a buffer for writing + * This handler will output to a file using an optional mod. + * The file will grow indefinitely. It uses a buffer for writing * to file. Logs can be manually flushed with `fileHandler.flush()`. Log * messages with a log level greater than error are immediately flushed. Logs * are also flushed on process completion. * - * Behavior of the log modes is as follows: + * This handler requires `--allow-write` permission on the log file. * - * - `'a'` - Default mode. Appends new log messages to the end of an existing log - * file, or create a new log file if none exists. - * - `'w'` - Upon creation of the handler, any existing log file will be removed - * and a new one created. - * - `'x'` - This will create a new log file and throw an error if one already - * exists. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; * - * This handler requires `--allow-write` permission on the log file. + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` */ export class FileHandler extends BaseHandler { + /** Opened file to append logs to. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [fileSymbol]: Deno.FsFile | undefined; + /** Buffer used to write to file. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [bufSymbol]: Uint8Array; + /** Current position for pointer. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [pointerSymbol] = 0; + /** Filename associated with the file being logged. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [filenameSymbol]: string; + /** Current log mode. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [modeSymbol]: LogMode; + /** File open options. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [openOptionsSymbol]: Deno.OpenOptions; + /** Text encoder. + * @example Usage + * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * ``` + * **/ [encoderSymbol]: TextEncoder = new TextEncoder(); #unloadCallback = (() => { this.destroy(); }).bind(this); + /** + * Constructs a new FileHandler instance. + * + * @param levelName The level name to log messages at. + * @param options Options for the handler. + */ constructor(levelName: LevelName, options: FileHandlerOptions) { super(levelName, options); this[filenameSymbol] = options.filename; @@ -74,6 +164,15 @@ export class FileHandler extends BaseHandler { this[bufSymbol] = new Uint8Array(options.bufferSize ?? 4096); } + /** + * Sets up the file handler by opening the specified file and initializing resources. + * + * @example Usage + * ```ts no-assert + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); // Opens the file and prepares the handler for logging. + * ``` + */ override setup() { this[fileSymbol] = Deno.openSync( this[filenameSymbol], @@ -84,6 +183,33 @@ export class FileHandler extends BaseHandler { addEventListener("unload", this.#unloadCallback); } + /** + * Handles a log record and flushes the buffer if the log level is higher than error. + * + * @param logRecord Log record to handle. + * + * @example Usage + * ```ts + * import { FileHandler } from "@std/log/file-handler"; + * import { assertInstanceOf } from "@std/assert/instance-of"; + * import { LogLevels } from "./levels.ts"; + * import { LogRecord } from "./logger.ts"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * + * // Flushes the buffer immediately and logs "CRITICAL This log is very critical indeed." into the file. + * handler.handle( + * new LogRecord({ + * msg: "This log is very critical indeed.", + * args: [], + * level: LogLevels.CRITICAL, + * loggerName: "INFO", + * }), + * ); + * assertInstanceOf(handler, FileHandler); + * ``` + */ override handle(logRecord: LogRecord) { super.handle(logRecord); @@ -93,6 +219,22 @@ export class FileHandler extends BaseHandler { } } + /** + * Logs a message by adding it to the buffer, with flushing as needed. + * + * @param msg The message to log. + * + * @example Usage + * ```ts + * import { FileHandler } from "@std/log/file-handler"; + * import { assertInstanceOf } from "@std/assert/instance-of"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * assertInstanceOf(handler, FileHandler); + * ``` + */ log(msg: string) { const bytes = this[encoderSymbol].encode(msg + "\n"); if (bytes.byteLength > this[bufSymbol].byteLength - this[pointerSymbol]) { @@ -106,6 +248,21 @@ export class FileHandler extends BaseHandler { } } + /** + * Immediately writes the contents of the buffer to the previously opened file. + * + * @example Usage + * ```ts + * import { FileHandler } from "@std/log/file-handler"; + * import { assertInstanceOf } from "@std/assert/instance-of"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.log('Hello, world!'); + * handler.flush(); // Writes buffered log messages to the file immediately. + * assertInstanceOf(handler, FileHandler); + * ``` + */ flush() { if (this[pointerSymbol] > 0 && this[fileSymbol]) { let written = 0; @@ -122,6 +279,20 @@ export class FileHandler extends BaseHandler { this[pointerSymbol] = 0; } + /** + * Destroys the handler, performing any cleanup that is required and closes the file handler. + * + * @example Usage + * ```ts + * import { FileHandler } from "@std/log/file-handler"; + * import { assertInstanceOf } from "@std/assert/instance-of"; + * + * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); + * handler.setup(); + * handler.destroy(); + * assertInstanceOf(handler, FileHandler); + * ``` + */ override destroy() { this.flush(); this[fileSymbol]?.close(); From 29866bcfeb87ff782ab83dacab61b61dd63f484d Mon Sep 17 00:00:00 2001 From: Dogukan Karasakal Date: Tue, 5 Nov 2024 22:30:08 +0100 Subject: [PATCH 2/5] docs(log): minor update to FileHandler docs --- log/file_handler.ts | 48 ++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/log/file_handler.ts b/log/file_handler.ts index 3799d8088b56..84551203b065 100644 --- a/log/file_handler.ts +++ b/log/file_handler.ts @@ -44,11 +44,10 @@ export interface FileHandlerOptions extends BaseHandlerOptions { } /** - * This handler will output to a file using an optional mod. - * The file will grow indefinitely. It uses a buffer for writing - * to file. Logs can be manually flushed with `fileHandler.flush()`. Log - * messages with a log level greater than error are immediately flushed. Logs - * are also flushed on process completion. + * A file-based log handler that writes log messages to a specified file with buffering and optional modes. + * The logs are buffered for optimized performance, writing to the file only + * when the buffer is full, on manual .flush() call, during logging of a critical message or when process ends. + * It is important to note that the file can grow indefinitely. * * This handler requires `--allow-write` permission on the log file. * @@ -58,7 +57,9 @@ export interface FileHandlerOptions extends BaseHandlerOptions { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` */ export class FileHandler extends BaseHandler { @@ -69,7 +70,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [fileSymbol]: Deno.FsFile | undefined; @@ -80,7 +83,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [bufSymbol]: Uint8Array; @@ -91,7 +96,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [pointerSymbol] = 0; @@ -102,7 +109,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [filenameSymbol]: string; @@ -113,7 +122,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [modeSymbol]: LogMode; @@ -124,7 +135,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [openOptionsSymbol]: Deno.OpenOptions; @@ -135,7 +148,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); // Buffers the message, or writes it to the file depending on buffer state + * handler.flush(); // Manually flushes the buffer + * handler.destroy(); // Closes the file and removes listeners * ``` * **/ [encoderSymbol]: TextEncoder = new TextEncoder(); @@ -207,6 +222,7 @@ export class FileHandler extends BaseHandler { * loggerName: "INFO", * }), * ); + * * assertInstanceOf(handler, FileHandler); * ``` */ @@ -231,7 +247,9 @@ export class FileHandler extends BaseHandler { * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); - * handler.log('Hello, world!'); // Appends Hello, world! to logs.txt + * handler.log('Hello, world!'); + * handler.flush(); + * * assertInstanceOf(handler, FileHandler); * ``` */ @@ -260,6 +278,7 @@ export class FileHandler extends BaseHandler { * handler.setup(); * handler.log('Hello, world!'); * handler.flush(); // Writes buffered log messages to the file immediately. + * * assertInstanceOf(handler, FileHandler); * ``` */ @@ -290,6 +309,7 @@ export class FileHandler extends BaseHandler { * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); * handler.destroy(); + * * assertInstanceOf(handler, FileHandler); * ``` */ From 28e6c7ab70b130ee3b2d2d7d06c90745cdad4ff8 Mon Sep 17 00:00:00 2001 From: Dogukan Karasakal Date: Tue, 5 Nov 2024 22:33:54 +0100 Subject: [PATCH 3/5] docs(log): add missing import --- log/file_handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/log/file_handler.ts b/log/file_handler.ts index 84551203b065..ced8e62ccb81 100644 --- a/log/file_handler.ts +++ b/log/file_handler.ts @@ -184,6 +184,7 @@ export class FileHandler extends BaseHandler { * * @example Usage * ```ts no-assert + * import { FileHandler } from "@std/log/file-handler"; * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); // Opens the file and prepares the handler for logging. * ``` @@ -222,7 +223,7 @@ export class FileHandler extends BaseHandler { * loggerName: "INFO", * }), * ); - * + * * assertInstanceOf(handler, FileHandler); * ``` */ From ad2bbea9ccfc51c8907ed24aebb3e00eb10408f8 Mon Sep 17 00:00:00 2001 From: Dogukan Karasakal Date: Tue, 5 Nov 2024 22:38:17 +0100 Subject: [PATCH 4/5] docs(log): close file in jsdocs test --- log/file_handler.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/log/file_handler.ts b/log/file_handler.ts index ced8e62ccb81..19a32eae1748 100644 --- a/log/file_handler.ts +++ b/log/file_handler.ts @@ -185,8 +185,10 @@ export class FileHandler extends BaseHandler { * @example Usage * ```ts no-assert * import { FileHandler } from "@std/log/file-handler"; + * * const handler = new FileHandler("INFO", { filename: "./logs.txt" }); * handler.setup(); // Opens the file and prepares the handler for logging. + * handler.destroy(); * ``` */ override setup() { @@ -223,6 +225,7 @@ export class FileHandler extends BaseHandler { * loggerName: "INFO", * }), * ); + * handler.destroy(); * * assertInstanceOf(handler, FileHandler); * ``` @@ -250,6 +253,7 @@ export class FileHandler extends BaseHandler { * handler.setup(); * handler.log('Hello, world!'); * handler.flush(); + * handler.destroy(); * * assertInstanceOf(handler, FileHandler); * ``` @@ -279,7 +283,8 @@ export class FileHandler extends BaseHandler { * handler.setup(); * handler.log('Hello, world!'); * handler.flush(); // Writes buffered log messages to the file immediately. - * + * handler.destroy(); + * * assertInstanceOf(handler, FileHandler); * ``` */ From d41d472eae39057f3e58da72dab9bd36de99153a Mon Sep 17 00:00:00 2001 From: Dogukan Karasakal Date: Tue, 5 Nov 2024 22:40:23 +0100 Subject: [PATCH 5/5] docs(log): run fmt --- log/file_handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/file_handler.ts b/log/file_handler.ts index 19a32eae1748..1fa3d0e47e08 100644 --- a/log/file_handler.ts +++ b/log/file_handler.ts @@ -284,7 +284,7 @@ export class FileHandler extends BaseHandler { * handler.log('Hello, world!'); * handler.flush(); // Writes buffered log messages to the file immediately. * handler.destroy(); - * + * * assertInstanceOf(handler, FileHandler); * ``` */