docs(log): document BaseHandler (#6067)

* docs(log): document `BaseHandler`

* fix

* fix
This commit is contained in:
Asher Gomez 2024-09-27 13:31:58 +10:00 committed by GitHub
parent fb2e30313a
commit e9716720b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 496 additions and 15 deletions

View File

@ -70,6 +70,7 @@ const ENTRY_POINTS = [
"../io/mod.ts",
"../json/mod.ts",
"../jsonc/mod.ts",
"../log/base_handler.ts",
"../log/warn.ts",
"../log/critical.ts",
"../log/debug.ts",

View File

@ -8,19 +8,83 @@ import {
} from "./levels.ts";
import type { LogRecord } from "./logger.ts";
export type { LevelName, LogLevel, LogRecord };
/**
* A function type that defines the structure of a formatter function.
*
* @param logRecord The log record that needs to be formatted.
* @returns A string representation of the log record.
*/
export type FormatterFunction = (logRecord: LogRecord) => string;
const DEFAULT_FORMATTER: FormatterFunction = ({ levelName, msg }) =>
`${levelName} ${msg}`;
/** Options for {@linkcode BaseHandler}. */
export interface BaseHandlerOptions {
/** A function that formats log records. */
formatter?: FormatterFunction;
}
/**
* A base class for all log handlers.
*
* This class is abstract and should not be instantiated directly. Instead, it
* should be extended by other classes that implement the `log` method.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* assertInstanceOf(handler, BaseHandler);
* ```
*/
export abstract class BaseHandler {
#levelName: LevelName;
#level: LogLevel;
/**
* The function that formats log records.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
* const formatted = handler.formatter(record);
* assertEquals(formatted, "INFO Hello, world!");
* ```
*/
formatter: FormatterFunction;
/**
* Constructs a new instance.
*
* @param levelName The name of the log level to handle.
* @param options Options for the handler.
*/
constructor(
levelName: LevelName,
options?: BaseHandlerOptions,
@ -31,23 +95,138 @@ export abstract class BaseHandler {
this.formatter = formatter;
}
/**
* Getter for the log level that this handler will handle.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* assertEquals(handler.level, LogLevels.INFO);
* ```
*
* @returns The log level to handle.
*/
get level(): LogLevel {
return this.#level;
}
/**
* Setter for the log level that this handler will handle.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* handler.level = LogLevels.DEBUG;
* assertEquals(handler.level, LogLevels.DEBUG);
* ```
*
* @param level The log level to handle.
*/
set level(level: LogLevel) {
this.#level = level;
this.#levelName = getLevelName(level);
}
/**
* Getter for the name of the log level that this handler will handle.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* assertEquals(handler.levelName, "INFO");
* ```
*
* @returns The name of the log level to handle.
*/
get levelName(): LevelName {
return this.#levelName;
}
/**
* Setter for the name of the log level that this handler will handle.
*
* @param levelName The name of the log level to handle.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* handler.levelName = "DEBUG";
* assertEquals(handler.levelName, "DEBUG");
* ```
*/
set levelName(levelName: LevelName) {
this.#levelName = levelName;
this.#level = getLevelByName(levelName);
}
/**
* Handles a log record.
*
* @param logRecord The log record to handle.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
* handler.handle(record);
*
* assertInstanceOf(handler, BaseHandler);
* ```
*/
handle(logRecord: LogRecord) {
if (this.level > logRecord.level) return;
@ -55,14 +234,143 @@ export abstract class BaseHandler {
this.log(msg);
}
/**
* Formats a log record.
*
* @param logRecord The log record to format.
* @returns A string representation of the log record.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
* const formatted = handler.format(record);
* assertEquals(formatted, "INFO Hello, world!");
* ```
*/
format(logRecord: LogRecord): string {
return this.formatter(logRecord);
}
/**
* Logs a message.
*
* This method should be implemented by subclasses to handle the log record.
*
* @param msg The message to log.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* const handler = new MyHandler("INFO");
* handler.log("Hello, world!"); // Prints "Hello, world!"
*
* assertInstanceOf(handler, BaseHandler);
* ```
*/
abstract log(msg: string): void;
/**
* Initializes the handler.
*
* This method is called when the handler is added to a logger. It can be
* used to perform any setup that is required by the handler.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
*
* override setup() {
* console.log("Handler setup!");
* }
* }
*
* const handler = new MyHandler("INFO");
* handler.setup(); // Prints "Handler setup!"
*
* assertInstanceOf(handler, BaseHandler);
* ```
*/
setup() {}
/**
* Destroys the handler, performing any cleanup that is required.
*
* This method is called when the handler is removed from a logger. It can be
* used to perform any cleanup that is required by the handler.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
*
* override destroy() {
* console.log("Handler destroyed!");
* }
* }
*
* const handler = new MyHandler("INFO");
* handler.destroy(); // Prints "Handler destroyed!"
* assertInstanceOf(handler, BaseHandler);
* ```
*/
destroy() {}
/**
* Automatically disposes of the handler when instantiated with the `using`
* keyword by calling the {@linkcode BaseHandler.destroy} method.
*
* @example Usage
* ```ts
* import { BaseHandler } from "@std/log/base-handler";
* import { LogRecord } from "@std/log/logger";
* import { assertInstanceOf } from "@std/assert/instance-of";
*
* class MyHandler extends BaseHandler {
* log(msg: string) {
* console.log(msg);
* }
* }
*
* using handler = new MyHandler("INFO");
* assertInstanceOf(handler, BaseHandler);
* ```
*/
[Symbol.dispose]() {
this.destroy();
}

View File

@ -1,12 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "@std/assert";
import * as log from "./mod.ts";
import {
getLevelByName,
getLevelName,
type LogLevel,
LogLevelNames,
LogLevels,
} from "./levels.ts";
import { LogRecord } from "./logger.ts";
import { TestHandler } from "./_test_handler.ts";
Deno.test("BaseHandler handles default setup", function () {
const cases = new Map<log.LogLevel, string[]>([
const cases = new Map<LogLevel, string[]>([
[
log.LogLevels.DEBUG,
LogLevels.DEBUG,
[
"DEBUG debug-test",
"INFO info-test",
@ -16,7 +23,7 @@ Deno.test("BaseHandler handles default setup", function () {
],
],
[
log.LogLevels.INFO,
LogLevels.INFO,
[
"INFO info-test",
"WARN warn-test",
@ -25,21 +32,21 @@ Deno.test("BaseHandler handles default setup", function () {
],
],
[
log.LogLevels.WARN,
LogLevels.WARN,
["WARN warn-test", "ERROR error-test", "CRITICAL critical-test"],
],
[log.LogLevels.ERROR, ["ERROR error-test", "CRITICAL critical-test"]],
[log.LogLevels.CRITICAL, ["CRITICAL critical-test"]],
[LogLevels.ERROR, ["ERROR error-test", "CRITICAL critical-test"]],
[LogLevels.CRITICAL, ["CRITICAL critical-test"]],
]);
for (const [testCase, messages] of cases.entries()) {
const testLevel = log.getLevelName(testCase);
const testLevel = getLevelName(testCase);
const handler = new TestHandler(testLevel);
for (const levelName of log.LogLevelNames) {
const level = log.getLevelByName(levelName);
for (const levelName of LogLevelNames) {
const level = getLevelByName(levelName);
handler.handle(
new log.LogRecord({
new LogRecord({
msg: `${levelName.toLowerCase()}-test`,
args: [],
level: level,
@ -60,10 +67,10 @@ Deno.test("BaseHandler handles formatter with empty msg", function () {
});
handler.handle(
new log.LogRecord({
new LogRecord({
msg: "",
args: [],
level: log.LogLevels.DEBUG,
level: LogLevels.DEBUG,
loggerName: "default",
}),
);
@ -78,10 +85,10 @@ Deno.test("BaseHandler handles formatter", function () {
});
handler.handle(
new log.LogRecord({
new LogRecord({
msg: "Hello, world!",
args: [],
level: log.LogLevels.ERROR,
level: LogLevels.ERROR,
loggerName: "default",
}),
);

View File

@ -4,6 +4,8 @@
/**
* Use this to retrieve the numeric log level by it's associated name.
* Defaults to INFO.
*
* @internal
*/
export const LogLevels = {
NOTSET: 0,

View File

@ -8,10 +8,17 @@ import type { BaseHandler } from "./base_handler.ts";
// deno-lint-ignore no-explicit-any
export type GenericFunction = (...args: any[]) => any;
/**
* Options for {@linkcode LogRecord}.
*/
export interface LogRecordOptions {
/** The message to log. */
msg: string;
/** The arguments to log. */
args: unknown[];
/** The log level of the message. */
level: LogLevel;
/** The name of the logger that created the log record. */
loggerName: string;
}
@ -32,15 +39,127 @@ export interface LogConfig {
/**
* An object that encapsulates provided message and arguments as well some
* metadata that can be later used when formatting a message.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.msg, "Hello, world!");
* assertEquals(record.args, ["foo", "bar"]);
* assertEquals(record.level, LogLevels.INFO);
* assertEquals(record.loggerName, "example");
* ```
*/
export class LogRecord {
/** The message to log.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.msg, "Hello, world!");
* ```
*/
readonly msg: string;
#args: unknown[];
#datetime: Date;
/**
* The numeric log level of the log record.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.level, LogLevels.INFO);
* ```
*/
readonly level: number;
/**
* The name of the log level of the log record.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.loggerName, "example");
* ```
*/
readonly levelName: string;
/**
* The name of the logger that created the log record.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.loggerName, "example");
* ```
*/
readonly loggerName: string;
/**
* Constructs a new instance.
*
* @param options The options to create a new log record.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
* ```
*/
constructor(options: LogRecordOptions) {
this.msg = options.msg;
this.#args = [...options.args];
@ -49,9 +168,53 @@ export class LogRecord {
this.#datetime = new Date();
this.levelName = getLevelName(options.level);
}
/**
* Getter for the arguments to log.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertEquals } from "@std/assert/equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertEquals(record.args, ["foo", "bar"]);
* ```
*
* @returns A copy of the arguments to log.
*/
get args(): unknown[] {
return [...this.#args];
}
/**
* Getter for the date and time the log record was created.
*
* @example Usage
* ```ts
* import { LogRecord } from "@std/log/logger";
* import { LogLevels } from "@std/log/levels";
* import { assertAlmostEquals } from "@std/assert/almost-equals";
*
* const record = new LogRecord({
* msg: "Hello, world!",
* args: ["foo", "bar"],
* level: LogLevels.INFO,
* loggerName: "example",
* });
*
* assertAlmostEquals(record.datetime.getTime(), Date.now(), 1);
* ```
*
* @returns The date and time the log record was created.
*/
get datetime(): Date {
return new Date(this.#datetime.getTime());
}