style: format logger

This commit is contained in:
2026-05-31 23:03:37 +09:00
parent d6efb4baea
commit fe9e97baad

View File

@@ -1,17 +1,25 @@
import chalk, { type ChalkInstance } from "chalk"; import chalk, { type ChalkInstance } from "chalk";
import { appendFileSync, existsSync, mkdirSync, createWriteStream, type WriteStream } from "fs"; import { existsSync, mkdirSync, createWriteStream, type WriteStream } from "fs";
import { dirname } from "path"; import { dirname } from "path";
import { config } from "@/config";
export type LogLevel = "debug" | "info" | "success" | "warn" | "error" | "fatal"; export type LogLevel =
| "debug"
| "info"
| "success"
| "warn"
| "error"
| "fatal";
const LEVELS: Record<LogLevel, { rank: number; color: ChalkInstance; stderr: boolean }> = { const LEVELS: Record<
LogLevel,
{ rank: number; color: ChalkInstance; stderr: boolean }
> = {
debug: { rank: 0, color: chalk.gray, stderr: false }, debug: { rank: 0, color: chalk.gray, stderr: false },
info: { rank: 1, color: chalk.blue, stderr: false }, info: { rank: 1, color: chalk.blue, stderr: false },
success: { rank: 2, color: chalk.green, stderr: false }, success: { rank: 2, color: chalk.green, stderr: false },
warn: { rank: 3, color: chalk.yellow, stderr: true }, warn: { rank: 3, color: chalk.yellow, stderr: true },
error: { rank: 4, color: chalk.red, stderr: true }, error: { rank: 4, color: chalk.red, stderr: true },
fatal: { rank: 5, color: chalk.bgRed.white,stderr: true }, fatal: { rank: 5, color: chalk.bgRed.white, stderr: true },
}; };
const ICONS: Record<LogLevel, string> = { const ICONS: Record<LogLevel, string> = {
@@ -76,13 +84,21 @@ class Logger {
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`; return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
} }
private format(level: LogLevel, message: string): { console: string; file: string } { private format(
level: LogLevel,
message: string,
): { console: string; file: string } {
const ts = this.timestamps ? `[${this.formatTimestamp()}]` : ""; const ts = this.timestamps ? `[${this.formatTimestamp()}]` : "";
const tag = this.tag ? `[${this.tag}]` : ""; const tag = this.tag ? `[${this.tag}]` : "";
const icon = ICONS[level]; const icon = ICONS[level];
const levelStr = level.toUpperCase(); const levelStr = level.toUpperCase();
const consoleParts = [ts, tag, this.colors ? LEVELS[level].color(icon) : icon, message].filter(Boolean); const consoleParts = [
ts,
tag,
this.colors ? LEVELS[level].color(icon) : icon,
message,
].filter(Boolean);
const consoleLine = consoleParts.join(" "); const consoleLine = consoleParts.join(" ");
const fileParts = [ts, tag, `[${levelStr}]`, message].filter(Boolean); const fileParts = [ts, tag, `[${levelStr}]`, message].filter(Boolean);
@@ -92,18 +108,23 @@ class Logger {
} }
private formatJson(level: LogLevel, message: string): string { private formatJson(level: LogLevel, message: string): string {
return JSON.stringify({ return (
JSON.stringify({
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
level, level,
tag: this.tag, tag: this.tag,
message, message,
}) + "\n"; }) + "\n"
);
} }
private write(level: LogLevel, message: string) { private write(level: LogLevel, message: string) {
if (!this.shouldLog(level)) return; if (!this.shouldLog(level)) return;
const { console: consoleLine, file: fileLine } = this.format(level, message); const { console: consoleLine, file: fileLine } = this.format(
level,
message,
);
const jsonLine = this.formatJson(level, message); const jsonLine = this.formatJson(level, message);
if (!this.silent) { if (!this.silent) {
@@ -116,14 +137,25 @@ class Logger {
} }
} }
debug(message: string) { this.write("debug", message); } debug(message: string) {
info(message: string) { this.write("info", message); } this.write("debug", message);
success(message: string) { this.write("success", message); } }
warn(message: string) { this.write("warn", message); } info(message: string) {
error(message: string) { this.write("error", message); } this.write("info", message);
fatal(message: string) { this.write("fatal", message); } }
success(message: string) {
this.write("success", message);
}
warn(message: string) {
this.write("warn", message);
}
error(message: string) {
this.write("error", message);
}
fatal(message: string) {
this.write("fatal", message);
}
/** Create a child logger with an additional tag */
child(tag: string): Logger { child(tag: string): Logger {
const combined = this.tag ? `${this.tag}:${tag}` : tag; const combined = this.tag ? `${this.tag}:${tag}` : tag;
return new Logger({ return new Logger({
@@ -137,7 +169,6 @@ class Logger {
}); });
} }
/** Update options at runtime */
configure(options: Partial<LoggerOptions>) { configure(options: Partial<LoggerOptions>) {
if (options.level !== undefined) this.level = options.level; if (options.level !== undefined) this.level = options.level;
if (options.timestamps !== undefined) this.timestamps = options.timestamps; if (options.timestamps !== undefined) this.timestamps = options.timestamps;
@@ -159,16 +190,13 @@ class Logger {
} }
} }
/** Close file stream gracefully */
close() { close() {
this.fileStream?.end(); this.fileStream?.end();
} }
} }
/** Default global logger instance */
export const logger = new Logger(); export const logger = new Logger();
/** Create a new logger instance with custom options */
export function createLogger(options: LoggerOptions): Logger { export function createLogger(options: LoggerOptions): Logger {
return new Logger(options); return new Logger(options);
} }