Skip to content

Commit

Permalink
Add the timestamp configuration and relevant tests
Browse files Browse the repository at this point in the history
  • Loading branch information
msalafia committed Jun 19, 2022
1 parent 20b6a3e commit 81e37dc
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 18 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const configuration = {
{
name: "bar",
level: "log",
tag: `[${new Date(Date.now()).toLocaleTimeString()}] BAR -`,
tag: `BAR -`,
timestamp: () => `[${new Date(Date.now()).toLocaleTimeString()}]`,
},
],
};
Expand All @@ -49,7 +50,7 @@ let dlog = createDeblog(configuration);

In the example above, we are configuring the deblog instance saved in the variable `dlog` in order to expose two custom logging methods named `foo` and `bar`, respectively. We are also specifying that the log `foo` will provide a logging level of **debug**, whilst the log `bar` will provide a logging level of **log**. The logging levels here are just the names of the methods that will be called on the **console** object wrapped by the deblog instance. Therefore, `console.debug()` and `console.log()` will be called for each call of `dlog.foo()` and `dlog.bar()`, respectively.

Both logging methods are configured with a `tag` that will be printed at the beginning of each print in the console. As you can see for the **foo** log, you can customize a tag as you prefer. Furthermore, both logs are enabled using the `enabled` property in the configuration. Omitting such a property will enable the log by default.
Both logging methods are configured with a `tag` that will be printed at the beginning of each print in the console. As you can see for the **foo** log, you can define a custom function that returns a string representing a timestamp that will be printed before the tag. Furthermore, both logs are enabled using the `enabled` property in the configuration. Omitting such a property will enable the log by default.

Once created, a Deblog instance can be used like this:

Expand Down Expand Up @@ -119,6 +120,7 @@ This will produce the following output:
name: string,
level: "log" | "debug" | "info" | "warn" | "error",
tag?: string,
timestamp?: boolean | (() => string)
enabled?: boolean //default: true
}
]
Expand All @@ -136,6 +138,7 @@ A log configuration is structured with the following properties:
- `name`: A string representing the name of the logging method that will be exposed by the deblog instance.
- `level`: The method that will be called on the console object. The only allowed values are `"log" | "debug" | "info" | "warn" | "error"`. For commodity, you can import and use the enumeration `LogLevels` to assign a proper value to this property.
- `tag`: A string to be attached at the beginning of the log line printed in the console.
- `timestamp`: It is used to configure a timestamp before the tag and the message of the log. When `true` is provided, a timestamp is generated using `[${(new Date()).toLocaleTimeString()}]`. You can also provide a function returning a string to customize your timestamp format or decide whatever to return. The default value is `false`.
- `enabled`: Specifies if the logging method will print in the console when it will be called.

## Deblog Repository
Expand Down
22 changes: 21 additions & 1 deletion src/create_deblog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,28 @@ export function createDeblog<T extends IDynamicLogs>(config: IDeblogConfig): IDe

let flag = log.enabled ?? true;
let defFlag = flag;

if (typeof log.timestamp !== "undefined" && typeof log.timestamp !== "function" && typeof log.timestamp !== "boolean") {
throw new Error("Invalid timestamp configuration. The only types allowed are boolean and () => string.");
}

//TODO: continue from here. refactor arguments array and probably change the tests.
let tempLog = <TLog><unknown>(function () {
flag && console[log.level](`${log.tag}`, ...arguments);

let ts: string | undefined;
if (typeof log.timestamp !== "undefined") {
if (typeof log.timestamp === "function") {
ts = log.timestamp();
} else if (typeof log.timestamp === "boolean") {
ts = `[${(new Date()).toLocaleTimeString()}]`;
}
}

let args: string[] = [];
ts && args.push(ts);
log.tag && args.push(log.tag);
args = [...args, ...arguments];
flag && console[log.level](...args);
});
tempLog.enable = () => (flag = true);
tempLog.disable = () => (flag = false);
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type TLogLevel = LogLevels | "log" | "debug" | "info" | "warn" | "error"
export interface ILogConfig {
name: string;
level: TLogLevel;
timestamp?: boolean | (() => string);
tag?: string;
enabled?: boolean;
}
Expand Down
98 changes: 83 additions & 15 deletions test/deblog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,29 +157,29 @@ describe("test createDeblog for proper deblog creation", () => {
deblog.yell("I am yelling in console.info");
deblog.quiet("I am quiet in console.log");

expect(mockedConsoleLog).toBeCalledWith("undefined", "I am barking in console.log");
expect(mockedConsoleInfo).toBeCalledWith("undefined", "I am yelling in console.info");
expect(mockedConsoleLog).toBeCalledWith("undefined", "I am quiet in console.log");
expect(mockedConsoleLog).toBeCalledWith("I am barking in console.log");
expect(mockedConsoleInfo).toBeCalledWith("I am yelling in console.info");
expect(mockedConsoleLog).toBeCalledWith("I am quiet in console.log");

deblog.disableAllBut("bark");

deblog.bark("Do bark me");
deblog.yell("Don't yell me");
deblog.quiet("Don't quiet me");

expect(mockedConsoleLog).toBeCalledWith("undefined", "Do bark me");
expect(mockedConsoleInfo).not.toBeCalledWith("undefined", "Don't yell me");
expect(mockedConsoleLog).not.toBeCalledWith("undefined", "Don't quiet me");
expect(mockedConsoleLog).toBeCalledWith("Do bark me");
expect(mockedConsoleInfo).not.toBeCalledWith("Don't yell me");
expect(mockedConsoleLog).not.toBeCalledWith("Don't quiet me");

deblog.restoreAll();

deblog.bark("Bark still logs");
deblog.yell("Yell info logs again");
deblog.quiet("Quite logs again");

expect(mockedConsoleLog).toBeCalledWith("undefined", "Bark still logs");
expect(mockedConsoleInfo).toBeCalledWith("undefined", "Yell info logs again");
expect(mockedConsoleLog).toBeCalledWith("undefined", "Quite logs again");
expect(mockedConsoleLog).toBeCalledWith("Bark still logs");
expect(mockedConsoleInfo).toBeCalledWith("Yell info logs again");
expect(mockedConsoleLog).toBeCalledWith("Quite logs again");
jest.restoreAllMocks();

});
Expand All @@ -203,21 +203,21 @@ describe("test createDeblog for proper deblog creation", () => {
deblog.yell("Yell!");
deblog.quiet("Quiet");

expect(mockedConsoleLog).toBeCalledWith("undefined", "Bark!");
expect(mockedConsoleLog).toBeCalledWith("undefined", "Yell!");
expect(mockedConsoleLog).not.toBeCalledWith("undefined", "Quiet!");
expect(mockedConsoleLog).toBeCalledWith("Bark!");
expect(mockedConsoleLog).toBeCalledWith("Yell!");
expect(mockedConsoleLog).not.toBeCalledWith("Quiet!");

deblog.quiet.enable();

deblog.quiet("Quiet again!");
expect(mockedConsoleLog).toBeCalledWith("undefined", "Quiet again!");
expect(mockedConsoleLog).toBeCalledWith("Quiet again!");

deblog.yell.disable();
deblog.bark("Bark again!");
deblog.yell("Yell again");

expect(mockedConsoleLog).toBeCalledWith("undefined", "Bark again!");
expect(mockedConsoleLog).not.toBeCalledWith("undefined", "Yell again");
expect(mockedConsoleLog).toBeCalledWith("Bark again!");
expect(mockedConsoleLog).not.toBeCalledWith("Yell again");
jest.restoreAllMocks();
});
});
Expand Down Expand Up @@ -271,4 +271,72 @@ describe("test persistence of deblogs", () => {
allDebs = getDeblogs();
expect(allDebs).toHaveLength(0);
});
});

describe("test timestamp feature", () => {
it("should add a timestamp (comformant to LocaleTimeString) to the log if the configuration is a boolean", () => {

jest.spyOn(console, "log").mockImplementation(() => {});

const config: IDeblogConfig = {
logs: [
{ name: "bark", level: "log", tag: "[BARK]", timestamp: true },
]
}

const deblog = createDeblog(config);

deblog.bark("What's the time?");
expect(console.log).toBeCalledWith(expect.stringMatching(/^\[\d{2}:\d{2}:\d{2}\]$/), "[BARK]", "What's the time?");
jest.restoreAllMocks();
});

it("should add a timestamp to the log if a function returning a string as configuration is provided", () => {

jest.spyOn(console, "log").mockImplementation(() => {});

const config: IDeblogConfig = {
logs: [
{ name: "bark", level: "log", tag: "[BARK]", timestamp: () => `[${new Date().toISOString()}]` },
]
}

const deblog = createDeblog(config);

deblog.bark("What's the time?");
expect(console.log).toBeCalledWith(expect.stringMatching(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/), "[BARK]", "What's the time?");
jest.restoreAllMocks();
});

it("should throw is a timestamp configuration that its not a boolean or function is provided", () => {

const config: IDeblogConfig = {
logs: [
{ name: "bark", level: "log", tag: "[BARK]", timestamp: <any>123456 }
]
}

expect(() => createDeblog(config)).toThrowError("Invalid timestamp configuration. The only types allowed are boolean and () => string.");
});

it("shoud generate two different timestamp if a boolean configuration is provided", () => {
let logMock = jest.spyOn(console, "log").mockImplementation(() => {});

const config: IDeblogConfig = {
logs: [
{ name: "bark", level: "log", tag: "[BARK]", timestamp: true },
]
}

const deblog = createDeblog(config);

deblog.bark("First Bark");
jest.useFakeTimers({ now: Date.now() + 5000 });
deblog.bark("second Bark");
jest.useRealTimers();

expect(logMock.mock.calls[0][0]).not.toBe(logMock.mock.calls[1][0]);

jest.restoreAllMocks();
});
});

0 comments on commit 81e37dc

Please sign in to comment.