Skip to content

Commit

Permalink
add: date-based template variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Mitsunee committed Nov 13, 2024
1 parent a093c31 commit fcdd2c6
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 17 deletions.
145 changes: 128 additions & 17 deletions src/LogLevel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
import { padNum } from "./padNum";
import { padStr } from "./padStr";

const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];
const ordSuffix = ["st", "nd", "rd"];

type ColorFormatter = (str: string) => string;

interface MessageTemplate {
template: string;
utc?: boolean;
}

interface LogLevelOpts {
/**
* Name of the log level
Expand All @@ -25,12 +48,29 @@ interface LogLevelOpts {
color?: ColorFormatter;
}

function toTemplateObjs(
msgVal: string | MessageTemplate,
fileMsgVal?: string | MessageTemplate
) {
const msgTemplate: MessageTemplate =
typeof msgVal == "string"
? { template: msgVal, utc: false }
: { ...msgVal };

const fileMsgTemplate: MessageTemplate = fileMsgVal
? typeof fileMsgVal == "string"
? { template: fileMsgVal, utc: false }
: { ...fileMsgVal }
: msgTemplate;
return { msg: msgTemplate, file: fileMsgTemplate };
}

export class LogLevel {
#name: string;
#msgTemplate: string;
#fileMsgTemplate: string;
#templates: ReturnType<typeof toTemplateObjs>;
#padLen: number;
#color?: ColorFormatter;
#now: Date;

constructor({
name,
Expand All @@ -40,20 +80,87 @@ export class LogLevel {
color
}: LogLevelOpts) {
this.#name = name;
this.#msgTemplate = msgTemplate;
this.#fileMsgTemplate = fileMsgTemplate || msgTemplate;
this.#templates = toTemplateObjs(msgTemplate, fileMsgTemplate);
this.#padLen = padLen || 0;
this.#color = color;
this.#now = new Date();
}

#processTemplateVar(str: string /* , now: Date*/) {
switch (str) {
case "%name%":
return this.#name.toLowerCase();
case "%NAME%":
return this.#name.toUpperCase();
case "%Name%":
return this.#name;
#processTemplateVar(str: string, utc: boolean): string {
const now = this.#now;
const r = (str: string) => this.#processTemplateVar(`%${str}%`, utc); // call this method recursively

switch (str.toLowerCase()) {
case "%year%":
case "%#year%":
return now[utc ? "getUTCFullYear" : "getFullYear"]().toString();

case "%month%":
return (now[utc ? "getUTCMonth" : "getMonth"]() + 1).toString();
case "%#month%":
return padNum(r("month"), 2);

case "%date%":
return now[utc ? "getUTCDate" : "getDate"]().toString();
case "%#date%":
return padNum(r("date"), 2);

case "%hour%":
case "%hours%":
return now[utc ? "getUTCHours" : "getHours"]().toString();

case "%#hour%":
case "%#hours%":
return padNum(r("hour"), 2);

case "%min%":
case "%mins%":
case "%minute%":
case "%minutes%":
return now[utc ? "getUTCMinutes" : "getMinutes"]().toString();

case "%#min%":
case "%#mins%":
case "%#minute%":
case "%#minutes%":
return padNum(r("min"), 2);

case "%sec%":
case "%second%":
case "%seconds%":
return now[utc ? "getUTCSeconds" : "getSeconds"]().toString();

case "%#sec%":
case "%#second%":
case "%#seconds%":
return padNum(r("sec"), 2);

case "%day%":
return weekdays[now[utc ? "getUTCDay" : "getDay"]()];

case "%month_str%":
return months[now[utc ? "getUTCMonth" : "getMonth"]()];

case "%date_ord%": {
const date = now[utc ? "getUTCDate" : "getDate"]();
const suffix =
(4 <= date && date <= 20) || (24 <= date && date <= 30)
? "th"
: ordSuffix[(date % 10) - 1];
return date + suffix;
}

case "%iso%":
case "%iso_short%":
return `${r("year")}-${r("#month")}-${r("#date")}`;

case "%iso_full%":
case "%iso_long%":
return now.toISOString();

case "%time%":
return `${r("#hour")}:${r("#min")}:${r("#sec")}`;

default:
return str;
}
Expand All @@ -65,8 +172,9 @@ export class LogLevel {
let val = this.#name;

if (str.startsWith("%#")) direction = "left";
if (str.endsWith("#%"))
if (str.endsWith("#%")) {
direction = direction == "left" ? "center" : "right";
}

switch (tag) {
case "name":
Expand All @@ -82,17 +190,20 @@ export class LogLevel {
return direction ? padStr(val, this.#padLen, direction) : val;
}

#processPrefix(template: string, isFileLog?: boolean) {
//const now = new Date();
#processPrefix({ template, utc }: MessageTemplate, isFileLog?: boolean) {
this.#now = new Date();

return template
.replace(/%#?(?:name|Name|NAME)#?%/g, str =>
this.#processNameVar(str, isFileLog)
)
.replace(/%[a-z]+%/gi, str => this.#processTemplateVar(str /*, now*/));
.replace(/%#?[a-z_]+%/gi, str =>
this.#processTemplateVar(str, utc ?? false)
);
}

processTemplate(msg: string, isFileLog?: boolean) {
const template = isFileLog ? this.#fileMsgTemplate : this.#msgTemplate;
const template = this.#templates[isFileLog ? "file" : "msg"];
const prefix = this.#processPrefix(template, isFileLog);
return `${prefix} ${msg}`;
}
Expand Down
3 changes: 3 additions & 0 deletions src/padNum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function padNum(num: number | string, len: number) {
return num.toString().padStart(len, "0");
}
13 changes: 13 additions & 0 deletions tests/LogLevel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ test("processTemplate - Does not transform unknown variables", () => {
assert.is(level.processTemplate("foo"), "%wrong% test: foo");
});

test("processTemplate - Dates", () => {
const level = new LogLevel({
name: "test",
msgTemplate: { template: "[%iso% %time%] %name% -", utc: true }

Check failure on line 76 in tests/LogLevel.test.ts

View workflow job for this annotation

GitHub Actions / types

Type '{ template: string; utc: true; }' is not assignable to type 'string'.
});
assert.ok(
/^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}] test -/.test(
level.processTemplate("foo")
),
"test template with %iso% and %time% using regex"
);
});

test("processTemplate - name colour", () => {
const level = new LogLevel({
name: "test",
Expand Down

0 comments on commit fcdd2c6

Please sign in to comment.