mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 07:44:48 -05:00
feat(std/log): expose logger name to LogRecord (#6316)
This commit is contained in:
parent
78a311aa5f
commit
940f8e8433
6 changed files with 243 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue