152 lines
4.3 KiB
TypeScript
152 lines
4.3 KiB
TypeScript
import chalk from "chalk";
|
|
import type { TransformableInfo } from "logform";
|
|
import winston, { format, transports } from "winston";
|
|
|
|
/* -------------------------------------------------
|
|
* Bun-style console formatter
|
|
* ------------------------------------------------- */
|
|
function formatLikeConsole(input: unknown, colors: boolean): string {
|
|
const inspect = (value: unknown) =>
|
|
Bun.inspect(value, {
|
|
colors,
|
|
compact: false,
|
|
depth: 6,
|
|
});
|
|
|
|
// console.log(...args)
|
|
if (Array.isArray(input)) {
|
|
return input.map(inspect).join(" ");
|
|
}
|
|
|
|
if (input instanceof Error) {
|
|
const stack = input.stack ?? input.message;
|
|
return colors ? chalk.red(stack) : stack;
|
|
}
|
|
|
|
if (typeof input === "string") {
|
|
return input;
|
|
}
|
|
|
|
return inspect(input);
|
|
}
|
|
|
|
/* -------------------------------------------------
|
|
* Console format (colored, human-readable)
|
|
* ------------------------------------------------- */
|
|
const customConsoleFormat = format.printf((info: TransformableInfo) => {
|
|
const { timestamp, level, message, service, codePath } = info;
|
|
|
|
const coloredService = service ? chalk.magenta(service) : "";
|
|
const coloredCodePath = codePath ? chalk.grey(`@${codePath}`) : "";
|
|
const colon = service || codePath ? ": " : "";
|
|
|
|
const renderedMessage = formatLikeConsole(message, true);
|
|
|
|
return `${timestamp} [${level}] ${coloredService}${coloredCodePath}${colon}${renderedMessage}`;
|
|
});
|
|
|
|
/* -------------------------------------------------
|
|
* Timestamp
|
|
* ------------------------------------------------- */
|
|
const timestampFormat = format.timestamp({
|
|
format: "YYYY-MM-DD HH:mm:ss.SSSZZ",
|
|
});
|
|
|
|
/* -------------------------------------------------
|
|
* File transport factory (no colors)
|
|
* ------------------------------------------------- */
|
|
const createTransport = (level: string, filename: string) => {
|
|
const MB = 1_000_000;
|
|
let maxsize: number | undefined;
|
|
let maxFiles: number | undefined;
|
|
const tailable = false;
|
|
|
|
if (level === "silly") {
|
|
maxsize = 500 * MB;
|
|
} else if (level === "warn") {
|
|
maxsize = 10 * MB;
|
|
maxFiles = 5;
|
|
}
|
|
|
|
function replacer(_: unknown, value: unknown) {
|
|
if (typeof value === "bigint") {
|
|
return value.toString();
|
|
}
|
|
if (value instanceof Error) {
|
|
return {
|
|
message: value.message,
|
|
name: value.name,
|
|
stack: value.stack,
|
|
};
|
|
}
|
|
return value;
|
|
}
|
|
|
|
return new transports.File({
|
|
filename,
|
|
format: format.combine(timestampFormat, format.json({ replacer })),
|
|
level,
|
|
maxFiles,
|
|
maxsize,
|
|
tailable,
|
|
});
|
|
};
|
|
|
|
/* -------------------------------------------------
|
|
* Paths
|
|
* ------------------------------------------------- */
|
|
const sillyLogPath = process.env["LOG_VERBOSE"] ?? "logs/verbose.log";
|
|
const warnLogPath = process.env["LOG_WARN"] ?? "logs/warn.log";
|
|
const errorLogPath = process.env["LOG_ERROR"] ?? "logs/error.log";
|
|
|
|
/* -------------------------------------------------
|
|
* Winston logger
|
|
* ------------------------------------------------- */
|
|
const winstonLogger = winston.createLogger({
|
|
levels: winston.config.npm.levels,
|
|
transports: [
|
|
new transports.Console({
|
|
format: format.combine(timestampFormat, format.colorize(), customConsoleFormat),
|
|
level: "debug",
|
|
}),
|
|
createTransport("silly", sillyLogPath),
|
|
createTransport("warn", warnLogPath),
|
|
createTransport("error", errorLogPath),
|
|
],
|
|
});
|
|
|
|
/* -------------------------------------------------
|
|
* Public logger API
|
|
* ------------------------------------------------- */
|
|
const logger = {
|
|
debug: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.debug(message as string, { codePath, service });
|
|
},
|
|
|
|
error: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.error(message as string, { codePath, service });
|
|
},
|
|
|
|
info: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.info(message as string, { codePath, service });
|
|
},
|
|
|
|
log: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.info(message as string, { codePath, service });
|
|
},
|
|
|
|
silly: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.silly(message as string, { codePath, service });
|
|
},
|
|
|
|
verbose: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.verbose(message as string, { codePath, service });
|
|
},
|
|
|
|
warn: (message: unknown, service?: string, codePath?: string) => {
|
|
winstonLogger.warn(message as string, { codePath, service });
|
|
},
|
|
};
|
|
|
|
export default logger;
|