summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Knight <cknight1234@gmail.com>2020-06-18 11:50:18 +0100
committerGitHub <noreply@github.com>2020-06-18 12:50:18 +0200
commit940f8e8433ae5ec74b2642438849089a0433e512 (patch)
tree7611524db839edc816c5c77fdfeb711cfce6a757
parent78a311aa5f6c56a750aaf3a7d3e8f911acf348d1 (diff)
feat(std/log): expose logger name to LogRecord (#6316)
-rw-r--r--std/log/README.md18
-rw-r--r--std/log/handlers_test.ts133
-rw-r--r--std/log/logger.ts40
-rw-r--r--std/log/logger_test.ts31
-rw-r--r--std/log/mod.ts4
-rw-r--r--std/log/mod_test.ts52
6 files changed, 243 insertions, 35 deletions
diff --git a/std/log/README.md b/std/log/README.md
index 87055015c..b5e99525a 100644
--- a/std/log/README.md
+++ b/std/log/README.md
@@ -79,6 +79,7 @@ class LogRecord {
readonly datetime: Date;
readonly level: number;
readonly levelName: string;
+ readonly loggerName: string;
}
```
@@ -208,13 +209,21 @@ await log.setup({
return msg;
}
}),
+
+ anotherFmt: new log.handlers.ConsoleHandler("DEBUG", {
+ formatter: "[{loggerName}] - {levelName} {msg}"
+ }),
},
loggers: {
default: {
- level: "DEBUG",
- handlers: ["stringFmt", "functionFmt"],
+ level: "DEBUG",
+ handlers: ["stringFmt", "functionFmt"],
},
+ dataLogger: {
+ level: "INFO",
+ handlers: ["anotherFmt"],
+ }
}
})
@@ -223,6 +232,11 @@ log.debug("Hello, world!", 1, "two", [3, 4, 5]);
// results in:
[DEBUG] Hello, world! // output from "stringFmt" handler
10 Hello, world!, arg0: 1, arg1: two, arg3: [3, 4, 5] // output from "functionFmt" formatter
+
+// calling
+log.getLogger("dataLogger").error("oh no!");
+// results in:
+[dataLogger] - ERROR oh no! // output from anotherFmt handler
```
#### Custom handlers
diff --git a/std/log/handlers_test.ts b/std/log/handlers_test.ts
index f7714dae3..7ff4c4c90 100644
--- a/std/log/handlers_test.ts
+++ b/std/log/handlers_test.ts
@@ -62,7 +62,12 @@ Deno.test("simpleHandler", function (): void {
for (const levelName of LogLevelNames) {
const level = getLevelByName(levelName as LevelName);
handler.handle(
- new LogRecord(`${levelName.toLowerCase()}-test`, [], level)
+ new LogRecord({
+ msg: `${levelName.toLowerCase()}-test`,
+ args: [],
+ level: level,
+ loggerName: "default",
+ })
);
}
@@ -77,7 +82,14 @@ Deno.test("testFormatterAsString", function (): void {
formatter: "test {levelName} {msg}",
});
- handler.handle(new LogRecord("Hello, world!", [], LogLevels.DEBUG));
+ handler.handle(
+ new LogRecord({
+ msg: "Hello, world!",
+ args: [],
+ level: LogLevels.DEBUG,
+ loggerName: "default",
+ })
+ );
assertEquals(handler.messages, ["test DEBUG Hello, world!"]);
});
@@ -88,7 +100,14 @@ Deno.test("testFormatterAsFunction", function (): void {
`fn formatter ${logRecord.levelName} ${logRecord.msg}`,
});
- handler.handle(new LogRecord("Hello, world!", [], LogLevels.ERROR));
+ handler.handle(
+ new LogRecord({
+ msg: "Hello, world!",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
assertEquals(handler.messages, ["fn formatter ERROR Hello, world!"]);
});
@@ -102,12 +121,26 @@ Deno.test({
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("Hello World", [], LogLevels.WARNING));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "Hello World",
+ args: [],
+ level: LogLevels.WARNING,
+ loggerName: "default",
+ })
+ );
await fileHandler.destroy();
const firstFileSize = (await Deno.stat(LOG_FILE)).size;
await fileHandler.setup();
- fileHandler.handle(new LogRecord("Hello World", [], LogLevels.WARNING));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "Hello World",
+ args: [],
+ level: LogLevels.WARNING,
+ loggerName: "default",
+ })
+ );
await fileHandler.destroy();
const secondFileSize = (await Deno.stat(LOG_FILE)).size;
@@ -210,13 +243,34 @@ Deno.test({
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ ); // 'ERROR AAA\n' = 10 bytes
fileHandler.flush();
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
fileHandler.flush();
assertEquals((await Deno.stat(LOG_FILE)).size, 20);
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
fileHandler.flush();
// Rollover occurred. Log file now has 1 record, rollover file has the original 2
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
@@ -239,9 +293,30 @@ Deno.test({
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ ); // 'ERROR AAA\n' = 10 bytes
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
await fileHandler.destroy();
@@ -277,7 +352,14 @@ Deno.test({
mode: "a",
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ ); // 'ERROR AAA\n' = 10 bytes
await fileHandler.destroy();
const decoder = new TextDecoder();
@@ -349,7 +431,14 @@ Deno.test({
mode: "w",
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ ); // 'ERROR AAA\n' = 10 bytes
assertEquals((await Deno.stat(LOG_FILE)).size, 0);
dispatchEvent(new Event("unload"));
@@ -402,13 +491,27 @@ Deno.test({
});
await fileHandler.setup();
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.ERROR,
+ loggerName: "default",
+ })
+ );
// ERROR won't trigger immediate flush
const fileSize = (await Deno.stat(LOG_FILE)).size;
assertEquals(fileSize, 0);
- fileHandler.handle(new LogRecord("AAA", [], LogLevels.CRITICAL));
+ fileHandler.handle(
+ new LogRecord({
+ msg: "AAA",
+ args: [],
+ level: LogLevels.CRITICAL,
+ loggerName: "default",
+ })
+ );
// CRITICAL will trigger immediate flush
const fileSize2 = (await Deno.stat(LOG_FILE)).size;
diff --git a/std/log/logger.ts b/std/log/logger.ts
index 78599da91..004088d9a 100644
--- a/std/log/logger.ts
+++ b/std/log/logger.ts
@@ -7,19 +7,28 @@ import {
} from "./levels.ts";
import { BaseHandler } from "./handlers.ts";
+export interface LogRecordOptions {
+ msg: string;
+ args: unknown[];
+ level: number;
+ loggerName: string;
+}
+
export class LogRecord {
readonly msg: string;
#args: unknown[];
#datetime: Date;
readonly level: number;
readonly levelName: string;
+ readonly loggerName: string;
- constructor(msg: string, args: unknown[], level: number) {
- this.msg = msg;
- this.#args = [...args];
- this.level = level;
+ constructor(options: LogRecordOptions) {
+ this.msg = options.msg;
+ this.#args = [...options.args];
+ this.level = options.level;
+ this.loggerName = options.loggerName;
this.#datetime = new Date();
- this.levelName = getLevelName(level);
+ this.levelName = getLevelName(options.level);
}
get args(): unknown[] {
return [...this.#args];
@@ -29,17 +38,27 @@ export class LogRecord {
}
}
+export interface LoggerOptions {
+ handlers?: BaseHandler[];
+}
+
export class Logger {
level: number;
levelName: LevelName;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handlers: any[];
+ loggerName: string;
- constructor(levelName: LevelName, handlers?: BaseHandler[]) {
+ constructor(
+ loggerName: string,
+ levelName: LevelName,
+ options: LoggerOptions = {}
+ ) {
+ this.loggerName = loggerName;
this.level = getLevelByName(levelName);
this.levelName = levelName;
- this.handlers = handlers || [];
+ this.handlers = options.handlers || [];
}
/** If the level of the logger is greater than the level to log, then nothing
@@ -66,7 +85,12 @@ export class Logger {
} else {
logMessage = this.asString(msg);
}
- const record: LogRecord = new LogRecord(logMessage, args, level);
+ const record: LogRecord = new LogRecord({
+ msg: logMessage,
+ args: args,
+ level: level,
+ loggerName: this.loggerName,
+ });
this.handlers.forEach((handler): void => {
handler.handle(record);
diff --git a/std/log/logger_test.ts b/std/log/logger_test.ts
index 2425c15e6..f7b438cba 100644
--- a/std/log/logger_test.ts
+++ b/std/log/logger_test.ts
@@ -18,22 +18,39 @@ class TestHandler extends BaseHandler {
}
}
+Deno.test({
+ name: "Logger names can be output in logs",
+ fn() {
+ const handlerNoName = new TestHandler("DEBUG");
+ const handlerWithLoggerName = new TestHandler("DEBUG", {
+ formatter: "[{loggerName}] {levelName} {msg}",
+ });
+
+ const logger = new Logger("config", "DEBUG", {
+ handlers: [handlerNoName, handlerWithLoggerName],
+ });
+ logger.debug("hello");
+ assertEquals(handlerNoName.messages[0], "DEBUG hello");
+ assertEquals(handlerWithLoggerName.messages[0], "[config] DEBUG hello");
+ },
+});
+
Deno.test("simpleLogger", function (): void {
const handler = new TestHandler("DEBUG");
- let logger = new Logger("DEBUG");
+ let logger = new Logger("default", "DEBUG");
assertEquals(logger.level, LogLevels.DEBUG);
assertEquals(logger.levelName, "DEBUG");
assertEquals(logger.handlers, []);
- logger = new Logger("DEBUG", [handler]);
+ logger = new Logger("default", "DEBUG", { handlers: [handler] });
assertEquals(logger.handlers, [handler]);
});
Deno.test("customHandler", function (): void {
const handler = new TestHandler("DEBUG");
- const logger = new Logger("DEBUG", [handler]);
+ const logger = new Logger("default", "DEBUG", { handlers: [handler] });
const inlineData: string = logger.debug("foo", 1, 2);
@@ -50,7 +67,7 @@ Deno.test("customHandler", function (): void {
Deno.test("logFunctions", function (): void {
const doLog = (level: LevelName): TestHandler => {
const handler = new TestHandler(level);
- const logger = new Logger(level, [handler]);
+ const logger = new Logger("default", level, { handlers: [handler] });
const debugData = logger.debug("foo");
const infoData = logger.info("bar");
const warningData = logger.warning("baz");
@@ -101,7 +118,7 @@ Deno.test(
"String resolver fn will not execute if msg will not be logged",
function (): void {
const handler = new TestHandler("ERROR");
- const logger = new Logger("ERROR", [handler]);
+ const logger = new Logger("default", "ERROR", { handlers: [handler] });
let called = false;
const expensiveFunction = (): string => {
@@ -121,7 +138,7 @@ Deno.test(
Deno.test("String resolver fn resolves as expected", function (): void {
const handler = new TestHandler("ERROR");
- const logger = new Logger("ERROR", [handler]);
+ const logger = new Logger("default", "ERROR", { handlers: [handler] });
const expensiveFunction = (x: number): string => {
return "expensive function result " + x;
};
@@ -136,7 +153,7 @@ Deno.test(
"All types map correctly to log strings and are returned as is",
function (): void {
const handler = new TestHandler("DEBUG");
- const logger = new Logger("DEBUG", [handler]);
+ const logger = new Logger("default", "DEBUG", { handlers: [handler] });
const sym = Symbol();
const syma = Symbol("a");
const fn = (): string => {
diff --git a/std/log/mod.ts b/std/log/mod.ts
index f59a84449..a1a863d4a 100644
--- a/std/log/mod.ts
+++ b/std/log/mod.ts
@@ -65,7 +65,7 @@ export function getLogger(name?: string): Logger {
}
const result = state.loggers.get(name);
if (!result) {
- const logger = new Logger("NOTSET", []);
+ const logger = new Logger(name, "NOTSET", { handlers: [] });
state.loggers.set(name, logger);
return logger;
}
@@ -191,7 +191,7 @@ export async function setup(config: LogConfig): Promise<void> {
});
const levelName = loggerConfig.level || DEFAULT_LEVEL;
- const logger = new Logger(levelName, handlers);
+ const logger = new Logger(loggerName, levelName, { handlers: handlers });
state.loggers.set(loggerName, logger);
}
}
diff --git a/std/log/mod_test.ts b/std/log/mod_test.ts
index 98ac093c8..9de5d76af 100644
--- a/std/log/mod_test.ts
+++ b/std/log/mod_test.ts
@@ -1,7 +1,24 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
-import { getLogger, debug, info, warning, error, critical } from "./mod.ts";
+import {
+ getLogger,
+ debug,
+ info,
+ warning,
+ error,
+ critical,
+ setup,
+} from "./mod.ts";
import { Logger } from "./logger.ts";
+import { BaseHandler } from "./handlers.ts";
+
+class TestHandler extends BaseHandler {
+ public messages: string[] = [];
+
+ public log(str: string): void {
+ this.messages.push(str);
+ }
+}
let logger: Logger | null = null;
try {
@@ -39,3 +56,36 @@ Deno.test("default loggers work as expected", function (): void {
assertEquals(criticalData, "foo");
assertEquals(criticalResolver, "bar");
});
+
+Deno.test({
+ name: "Logging config works as expected with logger names",
+ async fn() {
+ const consoleHandler = new TestHandler("DEBUG");
+ const anotherConsoleHandler = new TestHandler("DEBUG", {
+ formatter: "[{loggerName}] {levelName} {msg}",
+ });
+ await setup({
+ handlers: {
+ console: consoleHandler,
+ anotherConsole: anotherConsoleHandler,
+ },
+
+ loggers: {
+ // configure default logger available via short-hand methods above
+ default: {
+ level: "DEBUG",
+ handlers: ["console"],
+ },
+
+ tasks: {
+ level: "ERROR",
+ handlers: ["anotherConsole"],
+ },
+ },
+ });
+ getLogger().debug("hello");
+ getLogger("tasks").error("world");
+ assertEquals(consoleHandler.messages[0], "DEBUG hello");
+ assertEquals(anotherConsoleHandler.messages[0], "[tasks] ERROR world");
+ },
+});