2020-01-02 15:13:47 -05:00
|
|
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
2020-02-11 11:24:27 -05:00
|
|
|
const { test } = Deno;
|
2020-05-29 02:39:33 -04:00
|
|
|
import {
|
|
|
|
assert,
|
|
|
|
assertEquals,
|
|
|
|
assertThrowsAsync,
|
|
|
|
assertNotEquals,
|
|
|
|
} from "../testing/asserts.ts";
|
2020-04-25 05:13:26 -04:00
|
|
|
import {
|
|
|
|
LogLevels,
|
|
|
|
LogLevelNames,
|
|
|
|
getLevelName,
|
|
|
|
getLevelByName,
|
|
|
|
LevelName,
|
|
|
|
} from "./levels.ts";
|
2020-04-09 07:45:24 -04:00
|
|
|
import { BaseHandler, FileHandler, RotatingFileHandler } from "./handlers.ts";
|
|
|
|
import { LogRecord } from "./logger.ts";
|
|
|
|
import { existsSync } from "../fs/exists.ts";
|
|
|
|
|
|
|
|
const LOG_FILE = "./test_log.file";
|
2019-01-27 10:21:00 -05:00
|
|
|
|
|
|
|
class TestHandler extends BaseHandler {
|
|
|
|
public messages: string[] = [];
|
|
|
|
|
|
|
|
public log(str: string): void {
|
|
|
|
this.messages.push(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 06:33:09 -04:00
|
|
|
test("simpleHandler", function (): void {
|
2019-01-27 10:21:00 -05:00
|
|
|
const cases = new Map<number, string[]>([
|
2019-01-28 11:17:00 -05:00
|
|
|
[
|
2020-04-25 05:13:26 -04:00
|
|
|
LogLevels.DEBUG,
|
2019-01-28 11:17:00 -05:00
|
|
|
[
|
|
|
|
"DEBUG debug-test",
|
|
|
|
"INFO info-test",
|
|
|
|
"WARNING warning-test",
|
|
|
|
"ERROR error-test",
|
2020-03-28 13:03:49 -04:00
|
|
|
"CRITICAL critical-test",
|
|
|
|
],
|
2019-01-28 11:17:00 -05:00
|
|
|
],
|
|
|
|
[
|
2020-04-25 05:13:26 -04:00
|
|
|
LogLevels.INFO,
|
2019-01-28 11:17:00 -05:00
|
|
|
[
|
|
|
|
"INFO info-test",
|
|
|
|
"WARNING warning-test",
|
|
|
|
"ERROR error-test",
|
2020-03-28 13:03:49 -04:00
|
|
|
"CRITICAL critical-test",
|
|
|
|
],
|
2019-01-28 11:17:00 -05:00
|
|
|
],
|
|
|
|
[
|
2020-04-25 05:13:26 -04:00
|
|
|
LogLevels.WARNING,
|
2020-03-28 13:03:49 -04:00
|
|
|
["WARNING warning-test", "ERROR error-test", "CRITICAL critical-test"],
|
2019-01-28 11:17:00 -05:00
|
|
|
],
|
2020-04-25 05:13:26 -04:00
|
|
|
[LogLevels.ERROR, ["ERROR error-test", "CRITICAL critical-test"]],
|
|
|
|
[LogLevels.CRITICAL, ["CRITICAL critical-test"]],
|
2019-01-27 10:21:00 -05:00
|
|
|
]);
|
|
|
|
|
|
|
|
for (const [testCase, messages] of cases.entries()) {
|
|
|
|
const testLevel = getLevelName(testCase);
|
|
|
|
const handler = new TestHandler(testLevel);
|
|
|
|
|
2020-04-25 05:13:26 -04:00
|
|
|
for (const levelName of LogLevelNames) {
|
|
|
|
const level = getLevelByName(levelName as LevelName);
|
2020-04-09 07:45:24 -04:00
|
|
|
handler.handle(
|
|
|
|
new LogRecord(`${levelName.toLowerCase()}-test`, [], level)
|
|
|
|
);
|
2019-01-27 10:21:00 -05:00
|
|
|
}
|
|
|
|
|
2019-03-06 19:42:24 -05:00
|
|
|
assertEquals(handler.level, testCase);
|
|
|
|
assertEquals(handler.levelName, testLevel);
|
|
|
|
assertEquals(handler.messages, messages);
|
2019-01-27 10:21:00 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-04-28 06:33:09 -04:00
|
|
|
test("testFormatterAsString", function (): void {
|
2019-01-27 10:21:00 -05:00
|
|
|
const handler = new TestHandler("DEBUG", {
|
2020-03-28 13:03:49 -04:00
|
|
|
formatter: "test {levelName} {msg}",
|
2019-01-27 10:21:00 -05:00
|
|
|
});
|
|
|
|
|
2020-04-25 05:13:26 -04:00
|
|
|
handler.handle(new LogRecord("Hello, world!", [], LogLevels.DEBUG));
|
2019-01-27 10:21:00 -05:00
|
|
|
|
2019-03-06 19:42:24 -05:00
|
|
|
assertEquals(handler.messages, ["test DEBUG Hello, world!"]);
|
2019-01-27 10:21:00 -05:00
|
|
|
});
|
|
|
|
|
2020-04-28 06:33:09 -04:00
|
|
|
test("testFormatterAsFunction", function (): void {
|
2019-01-27 10:21:00 -05:00
|
|
|
const handler = new TestHandler("DEBUG", {
|
2019-04-24 07:41:23 -04:00
|
|
|
formatter: (logRecord): string =>
|
2020-04-09 07:45:24 -04:00
|
|
|
`fn formatter ${logRecord.levelName} ${logRecord.msg}`,
|
2019-01-27 10:21:00 -05:00
|
|
|
});
|
|
|
|
|
2020-04-25 05:13:26 -04:00
|
|
|
handler.handle(new LogRecord("Hello, world!", [], LogLevels.ERROR));
|
2020-04-09 07:45:24 -04:00
|
|
|
|
|
|
|
assertEquals(handler.messages, ["fn formatter ERROR Hello, world!"]);
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "FileHandler with mode 'w' will wipe clean existing log file",
|
|
|
|
async fn() {
|
|
|
|
const fileHandler = new FileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
|
|
|
|
await fileHandler.setup();
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("Hello World", [], LogLevels.WARNING));
|
2020-04-09 07:45:24 -04:00
|
|
|
await fileHandler.destroy();
|
|
|
|
const firstFileSize = (await Deno.stat(LOG_FILE)).size;
|
|
|
|
|
|
|
|
await fileHandler.setup();
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("Hello World", [], LogLevels.WARNING));
|
2020-04-09 07:45:24 -04:00
|
|
|
await fileHandler.destroy();
|
|
|
|
const secondFileSize = (await Deno.stat(LOG_FILE)).size;
|
|
|
|
|
|
|
|
assertEquals(secondFileSize, firstFileSize);
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "FileHandler with mode 'x' will throw if log file already exists",
|
|
|
|
async fn() {
|
2020-06-01 18:31:17 -04:00
|
|
|
const fileHandler = new FileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
mode: "x",
|
|
|
|
});
|
|
|
|
Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("hello world"));
|
|
|
|
|
2020-05-28 17:08:47 -04:00
|
|
|
await assertThrowsAsync(async () => {
|
|
|
|
await fileHandler.setup();
|
|
|
|
}, Deno.errors.AlreadyExists);
|
2020-06-01 18:31:17 -04:00
|
|
|
|
|
|
|
await fileHandler.destroy();
|
|
|
|
|
2020-04-09 07:45:24 -04:00
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name:
|
|
|
|
"RotatingFileHandler with mode 'w' will wipe clean existing log file and remove others",
|
|
|
|
async fn() {
|
|
|
|
Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("hello world"));
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".1",
|
|
|
|
new TextEncoder().encode("hello world")
|
|
|
|
);
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".2",
|
|
|
|
new TextEncoder().encode("hello world")
|
|
|
|
);
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".3",
|
|
|
|
new TextEncoder().encode("hello world")
|
|
|
|
);
|
|
|
|
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 50,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
await fileHandler.destroy();
|
|
|
|
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 0);
|
|
|
|
assert(!existsSync(LOG_FILE + ".1"));
|
|
|
|
assert(!existsSync(LOG_FILE + ".2"));
|
|
|
|
assert(!existsSync(LOG_FILE + ".3"));
|
|
|
|
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name:
|
|
|
|
"RotatingFileHandler with mode 'x' will throw if any log file already exists",
|
|
|
|
async fn() {
|
2020-06-01 18:31:17 -04:00
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".3",
|
|
|
|
new TextEncoder().encode("hello world")
|
|
|
|
);
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 50,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "x",
|
|
|
|
});
|
2020-04-09 07:45:24 -04:00
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
await fileHandler.setup();
|
|
|
|
},
|
|
|
|
Deno.errors.AlreadyExists,
|
|
|
|
"Backup log file " + LOG_FILE + ".3 already exists"
|
|
|
|
);
|
2020-06-01 18:31:17 -04:00
|
|
|
|
|
|
|
fileHandler.destroy();
|
2020-04-09 07:45:24 -04:00
|
|
|
Deno.removeSync(LOG_FILE + ".3");
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
2020-06-01 18:31:17 -04:00
|
|
|
name: "RotatingFileHandler with first rollover, monitor step by step",
|
2020-04-09 07:45:24 -04:00
|
|
|
async fn() {
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 25,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
|
2020-06-01 18:31:17 -04:00
|
|
|
fileHandler.flush();
|
2020-04-09 07:45:24 -04:00
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
|
2020-06-01 18:31:17 -04:00
|
|
|
fileHandler.flush();
|
2020-04-09 07:45:24 -04:00
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 20);
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
|
2020-06-01 18:31:17 -04:00
|
|
|
fileHandler.flush();
|
2020-04-09 07:45:24 -04:00
|
|
|
// Rollover occurred. Log file now has 1 record, rollover file has the original 2
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE + ".1")).size, 20);
|
2020-06-01 18:31:17 -04:00
|
|
|
await fileHandler.destroy();
|
|
|
|
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
Deno.removeSync(LOG_FILE + ".1");
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "RotatingFileHandler with first rollover, check all at once",
|
|
|
|
async fn() {
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 25,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
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));
|
2020-04-09 07:45:24 -04:00
|
|
|
|
|
|
|
await fileHandler.destroy();
|
|
|
|
|
2020-06-01 18:31:17 -04:00
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE + ".1")).size, 20);
|
|
|
|
|
2020-04-09 07:45:24 -04:00
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
Deno.removeSync(LOG_FILE + ".1");
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "RotatingFileHandler with all backups rollover",
|
|
|
|
async fn() {
|
|
|
|
Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("original log file"));
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".1",
|
|
|
|
new TextEncoder().encode("original log.1 file")
|
|
|
|
);
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".2",
|
|
|
|
new TextEncoder().encode("original log.2 file")
|
|
|
|
);
|
|
|
|
Deno.writeFileSync(
|
|
|
|
LOG_FILE + ".3",
|
|
|
|
new TextEncoder().encode("original log.3 file")
|
|
|
|
);
|
|
|
|
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 2,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "a",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
2020-04-25 05:13:26 -04:00
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
|
2020-04-09 07:45:24 -04:00
|
|
|
await fileHandler.destroy();
|
|
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
assertEquals(decoder.decode(Deno.readFileSync(LOG_FILE)), "ERROR AAA\n");
|
|
|
|
assertEquals(
|
|
|
|
decoder.decode(Deno.readFileSync(LOG_FILE + ".1")),
|
|
|
|
"original log file"
|
|
|
|
);
|
|
|
|
assertEquals(
|
|
|
|
decoder.decode(Deno.readFileSync(LOG_FILE + ".2")),
|
|
|
|
"original log.1 file"
|
|
|
|
);
|
|
|
|
assertEquals(
|
|
|
|
decoder.decode(Deno.readFileSync(LOG_FILE + ".3")),
|
|
|
|
"original log.2 file"
|
|
|
|
);
|
|
|
|
assert(!existsSync(LOG_FILE + ".4"));
|
|
|
|
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
Deno.removeSync(LOG_FILE + ".1");
|
|
|
|
Deno.removeSync(LOG_FILE + ".2");
|
|
|
|
Deno.removeSync(LOG_FILE + ".3");
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "RotatingFileHandler maxBytes cannot be less than 1",
|
|
|
|
async fn() {
|
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 0,
|
|
|
|
maxBackupCount: 3,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
},
|
|
|
|
Error,
|
|
|
|
"maxBytes cannot be less than 1"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
2019-01-27 10:21:00 -05:00
|
|
|
|
2020-04-09 07:45:24 -04:00
|
|
|
test({
|
|
|
|
name: "RotatingFileHandler maxBackupCount cannot be less than 1",
|
|
|
|
async fn() {
|
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
maxBytes: 50,
|
|
|
|
maxBackupCount: 0,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
},
|
|
|
|
Error,
|
|
|
|
"maxBackupCount cannot be less than 1"
|
|
|
|
);
|
|
|
|
},
|
2019-01-27 10:21:00 -05:00
|
|
|
});
|
2020-05-29 02:39:33 -04:00
|
|
|
|
|
|
|
test({
|
2020-06-01 18:31:17 -04:00
|
|
|
name: "Window unload flushes buffer",
|
|
|
|
async fn() {
|
|
|
|
const fileHandler = new FileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR)); // 'ERROR AAA\n' = 10 bytes
|
|
|
|
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 0);
|
|
|
|
dispatchEvent(new Event("unload"));
|
|
|
|
assertEquals((await Deno.stat(LOG_FILE)).size, 10);
|
|
|
|
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "RotatingFileHandler: rotate on byte length, not msg length",
|
2020-05-29 02:39:33 -04:00
|
|
|
async fn() {
|
|
|
|
const fileHandler = new RotatingFileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
2020-06-01 18:31:17 -04:00
|
|
|
maxBytes: 7,
|
2020-05-29 02:39:33 -04:00
|
|
|
maxBackupCount: 1,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
|
|
|
|
const msg = "。";
|
|
|
|
const msgLength = msg.length;
|
|
|
|
const msgByteLength = new TextEncoder().encode(msg).byteLength;
|
2020-06-01 18:31:17 -04:00
|
|
|
assertNotEquals(msgLength, msgByteLength);
|
|
|
|
assertEquals(msgLength, 1);
|
|
|
|
assertEquals(msgByteLength, 3);
|
|
|
|
|
|
|
|
fileHandler.log(msg); // logs 4 bytes (including '\n')
|
|
|
|
fileHandler.log(msg); // max bytes is 7, but this would be 8. Rollover.
|
|
|
|
|
|
|
|
await fileHandler.destroy();
|
|
|
|
|
|
|
|
const fileSize1 = (await Deno.stat(LOG_FILE)).size;
|
|
|
|
const fileSize2 = (await Deno.stat(LOG_FILE + ".1")).size;
|
|
|
|
|
|
|
|
assertEquals(fileSize1, msgByteLength + 1);
|
|
|
|
assertEquals(fileSize2, msgByteLength + 1);
|
|
|
|
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
Deno.removeSync(LOG_FILE + ".1");
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
test({
|
|
|
|
name: "FileHandler: Critical logs trigger immediate flush",
|
|
|
|
async fn() {
|
|
|
|
const fileHandler = new FileHandler("WARNING", {
|
|
|
|
filename: LOG_FILE,
|
|
|
|
mode: "w",
|
|
|
|
});
|
|
|
|
await fileHandler.setup();
|
|
|
|
|
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.ERROR));
|
|
|
|
|
|
|
|
// ERROR won't trigger immediate flush
|
|
|
|
const fileSize = (await Deno.stat(LOG_FILE)).size;
|
|
|
|
assertEquals(fileSize, 0);
|
|
|
|
|
|
|
|
fileHandler.handle(new LogRecord("AAA", [], LogLevels.CRITICAL));
|
2020-05-29 02:39:33 -04:00
|
|
|
|
2020-06-01 18:31:17 -04:00
|
|
|
// CRITICAL will trigger immediate flush
|
|
|
|
const fileSize2 = (await Deno.stat(LOG_FILE)).size;
|
|
|
|
// ERROR record is 10 bytes, CRITICAL is 13 bytes
|
|
|
|
assertEquals(fileSize2, 23);
|
2020-05-29 02:39:33 -04:00
|
|
|
|
|
|
|
await fileHandler.destroy();
|
|
|
|
Deno.removeSync(LOG_FILE);
|
|
|
|
},
|
|
|
|
});
|