diff --git a/log/handlers_test.ts b/log/handlers_test.ts new file mode 100644 index 000000000..3cb34fca6 --- /dev/null +++ b/log/handlers_test.ts @@ -0,0 +1,95 @@ +import { assertEqual, test } from "../testing/mod.ts"; +import { LogRecord, Logger } from "./logger.ts"; +import { LogLevel, getLevelName, getLevelByName } from "./levels.ts"; +import { BaseHandler } from "./handlers.ts"; + +class TestHandler extends BaseHandler { + public messages: string[] = []; + + public log(str: string): void { + this.messages.push(str); + } +} + +test(function simpleHandler() { + const cases = new Map([ + [LogLevel.DEBUG, [ + "DEBUG debug-test", + "INFO info-test", + "WARNING warning-test", + "ERROR error-test", + "CRITICAL critical-test" + ]], + [LogLevel.INFO, [ + "INFO info-test", + "WARNING warning-test", + "ERROR error-test", + "CRITICAL critical-test" + ]], + [LogLevel.WARNING, [ + "WARNING warning-test", + "ERROR error-test", + "CRITICAL critical-test" + ]], + [LogLevel.ERROR, [ + "ERROR error-test", + "CRITICAL critical-test" + ]], + [LogLevel.CRITICAL, [ + "CRITICAL critical-test" + ]] + ]); + + for (const [testCase, messages] of cases.entries()) { + const testLevel = getLevelName(testCase); + const handler = new TestHandler(testLevel); + + for (const levelName in LogLevel) { + const level = getLevelByName(levelName); + handler.handle({ + msg: `${levelName.toLowerCase()}-test`, + args: [], + datetime: new Date(), + level: level, + levelName: levelName + }); + } + + assertEqual(handler.level, testCase); + assertEqual(handler.levelName, testLevel); + assertEqual(handler.messages, messages); + } +}); + +test(function testFormatterAsString() { + const handler = new TestHandler("DEBUG", { + formatter: "test {levelName} {msg}" + }); + + handler.handle({ + msg: "Hello, world!", + args: [], + datetime: new Date(), + level: LogLevel.DEBUG, + levelName: "DEBUG" + }); + + assertEqual(handler.messages, ["test DEBUG Hello, world!"]); +}); + +test(function testFormatterAsFunction() { + const handler = new TestHandler("DEBUG", { + formatter: logRecord => + `fn formmatter ${logRecord.levelName} ${logRecord.msg}` + }); + + handler.handle({ + msg: "Hello, world!", + args: [], + datetime: new Date(), + level: LogLevel.ERROR, + levelName: "ERROR" + }); + + assertEqual(handler.messages, ["fn formmatter ERROR Hello, world!"]); +}); diff --git a/log/logger_test.ts b/log/logger_test.ts new file mode 100644 index 000000000..7a5484105 --- /dev/null +++ b/log/logger_test.ts @@ -0,0 +1,104 @@ +import { assertEqual, test } from "../testing/mod.ts"; +import { LogRecord, Logger } from "./logger.ts"; +import { LogLevel } from "./levels.ts"; +import { BaseHandler } from "./handlers.ts"; + +class TestHandler extends BaseHandler { + public messages: string[] = []; + public records: LogRecord[] = []; + + handle(record: LogRecord): void { + this.records.push({...record, datetime: null }); + super.handle(record); + } + + public log(str: string): void { + this.messages.push(str); + } +} + +test(function simpleLogger() { + const handler = new TestHandler("DEBUG"); + let logger = new Logger("DEBUG"); + + assertEqual(logger.level, LogLevel.DEBUG); + assertEqual(logger.levelName, "DEBUG"); + assertEqual(logger.handlers, []); + + logger = new Logger("DEBUG", [handler]); + + assertEqual(logger.handlers, [handler]); +}); + +test(function customHandler() { + const handler = new TestHandler("DEBUG"); + const logger = new Logger("DEBUG", [handler]); + + logger.debug("foo", 1, 2); + + assertEqual(handler.records, [ + { + msg: "foo", + args: [1, 2], + datetime: null, + level: LogLevel.DEBUG, + levelName: "DEBUG" + } + ]); + + assertEqual(handler.messages, ["DEBUG foo"]); +}); + +test(function logFunctions() { + let handler: TestHandler; + + const doLog = (level: string) => { + handler = new TestHandler(level); + let logger = new Logger(level, [handler]); + logger.debug("foo"); + logger.info("bar"); + logger.warning("baz"); + logger.error("boo"); + logger.critical("doo"); + }; + + doLog("DEBUG"); + + assertEqual(handler.messages, [ + "DEBUG foo", + "INFO bar", + "WARNING baz", + "ERROR boo", + "CRITICAL doo" + ]); + + doLog("INFO"); + + assertEqual(handler.messages, [ + "INFO bar", + "WARNING baz", + "ERROR boo", + "CRITICAL doo" + ]); + + doLog("WARNING"); + + assertEqual(handler.messages, [ + "WARNING baz", + "ERROR boo", + "CRITICAL doo" + ]); + + doLog("ERROR"); + + assertEqual(handler.messages, [ + "ERROR boo", + "CRITICAL doo" + ]); + + doLog("CRITICAL"); + + assertEqual(handler.messages, [ + "CRITICAL doo" + ]); +}); diff --git a/log/mod.ts b/log/mod.ts index f332711b3..596b72a37 100644 --- a/log/mod.ts +++ b/log/mod.ts @@ -21,24 +21,20 @@ export interface LogConfig { } const DEFAULT_LEVEL = "INFO"; -const DEFAULT_NAME = ""; const DEFAULT_CONFIG: LogConfig = { - handlers: {}, + handlers: { + "default": new ConsoleHandler(DEFAULT_LEVEL) + }, loggers: { - "": { - level: "INFO", - handlers: [""] + "default": { + level: DEFAULT_LEVEL, + handlers: ["default"] } } }; -const defaultHandler = new ConsoleHandler("INFO"); -const defaultLogger = new Logger("INFO", [defaultHandler]); - const state = { - defaultHandler, - defaultLogger, handlers: new Map(), loggers: new Map(), config: DEFAULT_CONFIG @@ -52,19 +48,19 @@ export const handlers = { }; export const debug = (msg: string, ...args: any[]) => - defaultLogger.debug(msg, ...args); + getLogger('default').debug(msg, ...args); export const info = (msg: string, ...args: any[]) => - defaultLogger.info(msg, ...args); + getLogger('default').info(msg, ...args); export const warning = (msg: string, ...args: any[]) => - defaultLogger.warning(msg, ...args); + getLogger('default').warning(msg, ...args); export const error = (msg: string, ...args: any[]) => - defaultLogger.error(msg, ...args); + getLogger('default').error(msg, ...args); export const critical = (msg: string, ...args: any[]) => - defaultLogger.critical(msg, ...args); + getLogger('default').critical(msg, ...args); export function getLogger(name?: string) { if (!name) { - return defaultLogger; + return state.loggers.get('default'); } if (!state.loggers.has(name)) { @@ -81,7 +77,10 @@ export function getHandler(name: string) { } export async function setup(config: LogConfig) { - state.config = config; + state.config = { + handlers: {...DEFAULT_CONFIG.handlers, ...config.handlers}, + loggers: {...DEFAULT_CONFIG.loggers, ...config.loggers} + }; // tear down existing handlers state.handlers.forEach(handler => { diff --git a/log/test.ts b/log/test.ts index 2f3431348..d64809605 100644 --- a/log/test.ts +++ b/log/test.ts @@ -1,7 +1,8 @@ -import { remove, open, readAll } from "deno"; import { assertEqual, test } from "../testing/mod.ts"; import * as log from "./mod.ts"; -import { FileHandler } from "./handlers.ts"; +import { BaseHandler } from "./handlers.ts"; +import { LogRecord } from "./logger.ts"; +import { LogLevel } from "./levels.ts"; // constructor(levelName: string, options: HandlerOptions = {}) { // this.level = getLevelByName(levelName); @@ -11,85 +12,109 @@ import { FileHandler } from "./handlers.ts"; // } class TestHandler extends log.handlers.BaseHandler { - testOutput = ""; + public messages: string[] = []; log(msg: string) { - this.testOutput += `${msg}\n`; + this.messages.push(msg); } } -test(function testDefaultlogMethods() { - log.debug("Foobar"); - log.info("Foobar"); - log.warning("Foobar"); - log.error("Foobar"); - log.critical("Foobar"); +test(async function defaultHandlers() { + const loggers = { + DEBUG: log.debug, + INFO: log.info, + WARNING: log.warning, + ERROR: log.error, + CRITICAL: log.critical + }; - const logger = log.getLogger(""); - console.log(logger); + for (const levelName in LogLevel) { + if (levelName === "NOTSET") { + continue; + } + + const level = LogLevel[levelName]; + const logger = loggers[levelName]; + const handler = new TestHandler(level); + + await log.setup({ + handlers: { + default: handler + }, + loggers: { + default: { + level: levelName, + handlers: ["default"] + } + } + }); + + logger("foo"); + logger("bar", 1, 2); + + assertEqual(handler.messages, [ + `${levelName} foo`, + `${levelName} bar` + ]); + } }); -test(async function testDefaultFormatter() { +test(async function getLogger() { + const handler = new TestHandler("DEBUG"); + await log.setup({ handlers: { - test: new TestHandler("DEBUG") + default: handler }, - loggers: { - test: { + default: { level: "DEBUG", - handlers: ["test"] + handlers: ["default"] } } }); - const logger = log.getLogger("test"); - const handler = log.getHandler("test"); - logger.debug("Hello, world!"); - assertEqual(handler.testOutput, "DEBUG Hello, world!\n"); + const logger = log.getLogger(); + + assertEqual(logger.levelName, "DEBUG"); + assertEqual(logger.handlers, [ + handler + ]); }); -test(async function testFormatterAsString() { +test(async function getLoggerWithName() { + const fooHandler = new TestHandler("DEBUG"); + await log.setup({ handlers: { - test: new TestHandler("DEBUG", { - formatter: "test {levelName} {msg}" - }) + foo: fooHandler }, - loggers: { - test: { - level: "DEBUG", - handlers: ["test"] + bar: { + level: "INFO", + handlers: ["foo"] } } }); - const logger = log.getLogger("test"); - const handler = log.getHandler("test"); - logger.debug("Hello, world!"); - assertEqual(handler.testOutput, "test DEBUG Hello, world!\n"); + const logger = log.getLogger("bar"); + + assertEqual(logger.levelName, "INFO"); + assertEqual(logger.handlers, [ + fooHandler + ]); }); -test(async function testFormatterAsFunction() { +test(async function getLoggerUnknown() { await log.setup({ handlers: { - test: new TestHandler("DEBUG", { - formatter: logRecord => - `fn formmatter ${logRecord.levelName} ${logRecord.msg}` - }) }, - loggers: { - test: { - level: "DEBUG", - handlers: ["test"] - } } }); - const logger = log.getLogger("test"); - const handler = log.getHandler("test"); - logger.error("Hello, world!"); - assertEqual(handler.testOutput, "fn formmatter ERROR Hello, world!\n"); + const logger = log.getLogger("nonexistent"); + + assertEqual(logger.levelName, "NOTSET"); + assertEqual(logger.handlers, []); }); diff --git a/test.ts b/test.ts index 6850fc5af..9bb68e5ae 100755 --- a/test.ts +++ b/test.ts @@ -18,6 +18,8 @@ import "io/util_test.ts"; import "http/http_test.ts"; import "http/file_server_test.ts"; import "log/test.ts"; +import "log/handlers_test.ts"; +import "log/logger_test.ts"; import "media_types/test.ts"; import "testing/test.ts"; import "textproto/test.ts";