Skip to content

Commit

Permalink
Restructure the unit tests for logger
Browse files Browse the repository at this point in the history
  • Loading branch information
nazarhussain committed Aug 14, 2023
1 parent 664820a commit f076f05
Show file tree
Hide file tree
Showing 17 changed files with 385 additions and 336 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {expect} from "chai";
import sinon from "sinon";
// eslint-disable-next-line import/no-relative-packages
import {shouldDeleteLogFile} from "../../../cli/src/util/logger.js";
import {shouldDeleteLogFile} from "../../../src/util/logger.js";

describe("shouldDeleteLogFile", function () {
const prefix = "beacon";
Expand Down
2 changes: 2 additions & 0 deletions packages/logger/.mocharc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
colors: true
node-option:
- "loader=ts-node/esm"
require:
- ./test/setup.ts
36 changes: 31 additions & 5 deletions packages/logger/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,37 @@ import winston from "winston";
import Transport from "winston-transport";
import {LogLevel, Logger} from "@lodestar/utils";
import {createWinstonLogger} from "./winston.js";
import {LEVEL, MESSAGE, TimestampFormat, WinstonLogInfo} from "./interface.js";

export type BrowserLoggerOpts = {
/**
* Module prefix for all logs
*/
module?: string;
level: LogLevel;
/**
* Rendering format for logs, defaults to "human"
*/
format?: "human" | "json";
/**
* Enables relative to genesis timestamp format
* ```
* timestampFormat = {
* format: TimestampFormatCode.EpochSlot,
* genesisTime: args.logFormatGenesisTime,
* secondsPerSlot: config.SECONDS_PER_SLOT,
* slotsPerEpoch: SLOTS_PER_EPOCH,
* }
* ```
*/
timestampFormat?: TimestampFormat;
};

export function getBrowserLogger(opts: BrowserLoggerOpts): Logger {
return createWinstonLogger({level: opts.level, module: opts.module ?? ""}, [new BrowserConsole({level: opts.level})]);
return createWinstonLogger(
{level: opts.level, module: opts.module ?? "", format: opts.format, timestampFormat: opts.timestampFormat},
[new BrowserConsole({level: opts.level})]
);
}

class BrowserConsole extends Transport {
Expand Down Expand Up @@ -37,19 +60,22 @@ class BrowserConsole extends Transport {
this.level = opts?.level && this.levels.hasOwnProperty(opts.level) ? opts.level : "info";
}

log(method: string | number, message: unknown): void {
log(info: WinstonLogInfo, callback: () => void): void {
setTimeout(() => {
this.emit("logged", method);
this.emit("logged", info);
}, 0);

const val = this.levels[method as LogLevel];
const mappedMethod = this.methods[method as LogLevel];
const val = this.levels[info[LEVEL]];
const mappedMethod = this.methods[info[LEVEL]];
const message = info[MESSAGE];

if (val <= this.levels[this.level as LogLevel]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, no-console
console[mappedMethod](message);
}

callback();
}
}
9 changes: 8 additions & 1 deletion packages/logger/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Logger} from "@lodestar/utils";
import {LogLevel} from "@lodestar/utils";
import {BrowserLoggerOpts, getBrowserLogger} from "./browser.js";
import {getEmptyLogger} from "./empty.js";
import {LogFormat, TimestampFormat} from "./interface.js";

export function getEnvLogLevel(): LogLevel | null {
if (process == null) return null;
Expand All @@ -13,9 +14,15 @@ export function getEnvLogLevel(): LogLevel | null {

export function getEnvLogger(opts?: Partial<BrowserLoggerOpts>): Logger {
const level = opts?.level ?? getEnvLogLevel();
const format = (opts?.format ?? process.env["LOG_FORMAT"]) as LogFormat;
const timestampFormat =
opts?.timestampFormat ??
((process.env["LOG_TIMESTAMP_FORMAT"]
? {format: process.env["LOG_TIMESTAMP_FORMAT"]}
: undefined) as TimestampFormat);

if (level != null) {
return getBrowserLogger({...opts, level});
return getBrowserLogger({...opts, level, format, timestampFormat});
}

return getEmptyLogger();
Expand Down
17 changes: 13 additions & 4 deletions packages/logger/src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import {LEVEL, MESSAGE} from "triple-beam";
import {LogLevel, Logger, LogHandler, LogData} from "@lodestar/utils";
export {LogLevel, Logger, LogHandler, LogData};

export {LogLevel, Logger, LogHandler, LogData, LEVEL, MESSAGE};

export const logLevelNum: {[K in LogLevel]: number} = {
[LogLevel.error]: 0,
Expand All @@ -23,9 +26,9 @@ export type EpochSlotOpts = {
slotsPerEpoch: number;
};
export enum TimestampFormatCode {
DateRegular,
Hidden,
EpochSlot,
DateRegular = "regular",
Hidden = "hidden",
EpochSlot = "epoch",
}
export type TimestampFormat =
| {format: TimestampFormatCode.DateRegular}
Expand All @@ -38,3 +41,9 @@ export interface LoggerOptions {
format?: LogFormat;
timestampFormat?: TimestampFormat;
}

export interface WinstonLogInfo {
module: string;
[LEVEL]: LogLevel;
[MESSAGE]: string;
}
20 changes: 5 additions & 15 deletions packages/logger/src/utils/consoleTransport.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import winston, {Logger} from "winston";
// eslint-disable-next-line import/no-extraneous-dependencies
import {LEVEL} from "triple-beam";
import {LogLevel} from "../interface.js";
import {Logger, transports} from "winston";
import {LEVEL, LogLevel, WinstonLogInfo} from "../interface.js";

interface DefaultMeta {
module: string;
}

interface LogInfo extends DefaultMeta {
[LEVEL]: LogLevel;
}

export class ConsoleDynamicLevel extends winston.transports.Console {
export class ConsoleDynamicLevel extends transports.Console {
private readonly levelByModule = new Map<string, LogLevel>();
private readonly defaultLevel: LogLevel;

// Define property from TransportStream that's not in the types
private readonly levels!: Record<LogLevel, number>;
private parent?: Logger;

constructor(opts: {defaultLevel: LogLevel} & winston.transports.ConsoleTransportOptions) {
constructor(opts: {defaultLevel: LogLevel} & transports.ConsoleTransportOptions) {
super(opts);

this.defaultLevel = opts.defaultLevel;
Expand All @@ -36,7 +26,7 @@ export class ConsoleDynamicLevel extends winston.transports.Console {
return this.levelByModule.delete(module);
}

_write(info: LogInfo, enc: BufferEncoding, callback: (error?: Error | null | undefined) => void): void {
_write(info: WinstonLogInfo, enc: BufferEncoding, callback: (error?: Error | null | undefined) => void): void {
const moduleLevel = this.levelByModule.get(info.module) ?? this.defaultLevel;

// Min number is highest prio log level
Expand Down
48 changes: 48 additions & 0 deletions packages/logger/test/fixtures/loggerFormats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {LodestarError} from "@lodestar/utils";
import {LogData, LogFormat} from "../../src/index.js";

type TestCase = {
id: string;
message: string;
context?: LogData;
error?: Error;
output: {[P in LogFormat]: string};
};

/* eslint-disable quotes */
export const formatsTestCases: (TestCase | (() => TestCase))[] = [
{
id: "regular log with metadata",
message: "foo bar",
context: {meta: "data"},
output: {
human: "[] \u001b[33mwarn\u001b[39m: foo bar meta=data",
json: '{"context":{"meta":"data"},"level":"warn","message":"foo bar","module":""}',
},
},

{
id: "regular log with big int metadata",
message: "big int",
context: {data: BigInt(1)},
output: {
human: "[] \u001b[33mwarn\u001b[39m: big int data=1",
json: '{"context":{"data":"1"},"level":"warn","message":"big int","module":""}',
},
},

() => {
const error = new LodestarError({code: "SAMPLE_ERROR", data: {foo: "bar"}});
error.stack = "$STACK";
return {
id: "error with metadata",
opts: {format: "human", module: "SAMPLE"},
message: "foo bar",
error: error,
output: {
human: `[] \u001b[33mwarn\u001b[39m: foo bar code=SAMPLE_ERROR, data=foo=bar\n${error.stack}`,
json: '{"error":{"code":"SAMPLE_ERROR","data":{"foo":"bar"},"stack":"$STACK"},"level":"warn","message":"foo bar","module":""}',
},
};
},
];
25 changes: 25 additions & 0 deletions packages/logger/test/unit/browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {expect} from "chai";
import {LogLevel} from "@lodestar/utils";
import {TimestampFormatCode, logFormats} from "../../src/index.js";
import {formatsTestCases} from "../fixtures/loggerFormats.js";
import {stubLoggerForConsole} from "../utils/chai.js";
import {getBrowserLogger} from "../../src/browser.js";

describe("browser logger", () => {
describe("format and options", () => {
for (const testCase of formatsTestCases) {
const {id, message, context, error, output} = typeof testCase === "function" ? testCase() : testCase;
for (const format of logFormats) {
it(`${id} ${format} output`, async () => {
const logger = stubLoggerForConsole(
getBrowserLogger({level: LogLevel.info, format, timestampFormat: {format: TimestampFormatCode.Hidden}})
);

logger.warn(message, context, error);
logger.restoreStubs();
expect(logger.getLogs()).deep.equals([output[format]]);
});
}
}
});
});
28 changes: 28 additions & 0 deletions packages/logger/test/unit/env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {expect} from "chai";
import {LogLevel} from "@lodestar/utils";
import {TimestampFormatCode, logFormats} from "../../src/index.js";
import {formatsTestCases} from "../fixtures/loggerFormats.js";
import {stubLoggerForConsole} from "../utils/chai.js";
import {getEnvLogger} from "../../src/env.js";

describe("env logger", () => {
describe("format and options", () => {
for (const testCase of formatsTestCases) {
const {id, message, context, error, output} = typeof testCase === "function" ? testCase() : testCase;
for (const format of logFormats) {
it(`${id} ${format} output`, async () => {
// Set env variables
process.env.LOG_LEVEL = LogLevel.info;
process.env.LOG_FORMAT = format;
process.env.LOG_TIMESTAMP_FORMAT = TimestampFormatCode.Hidden;

const logger = stubLoggerForConsole(getEnvLogger());

logger.warn(message, context, error);
logger.restoreStubs();
expect(logger.getLogs()).deep.equals([output[format]]);
});
}
}
});
});
115 changes: 0 additions & 115 deletions packages/logger/test/unit/logger/winston.test.ts

This file was deleted.

Loading

0 comments on commit f076f05

Please sign in to comment.