@uisap/core
Version:
A modular Fastify-based framework inspired by Laravel
134 lines (125 loc) • 4.01 kB
JavaScript
import winston from "winston";
import DailyRotateFile from "winston-daily-rotate-file";
import { join } from "path";
export class Logger {
constructor(config = {}) {
this.logDir = config.logDir || join(process.cwd(), "logs");
this.level = config.level || process.env.LOG_LEVEL || "info";
const safeStringify = (obj, indent = 2) => {
const seen = new WeakSet();
return JSON.stringify(
obj,
(key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular]";
}
seen.add(value);
}
return value;
},
indent
);
};
const getCallerInfo = () => {
const err = new Error();
Error.captureStackTrace(err);
const stackLines = err.stack.split("\n");
for (let i = 2; i < stackLines.length; i++) {
const line = stackLines[i].trim();
if (
!line.includes("node_modules") &&
!line.includes("logger.js") &&
!line.includes("winston") &&
!line.includes("logform")
) {
const match = line.match(/([^/\\]+\.js):(\d+):(\d+)/);
return match ? `${match[1]}:${match[2]}` : line;
}
}
return "N/A";
};
const customFormat = winston.format.printf(
({ level, message, timestamp, stack, ...metadata }) => {
const logMessageText =
typeof message === "object" ? safeStringify(message) : message;
let logLine = `${timestamp} [${level.toUpperCase()}] ${logMessageText}`;
const caller = metadata.caller || getCallerInfo();
if (caller !== "N/A" && !metadata.caller) {
logLine += ` | Caller: ${caller}`;
} else if (metadata.caller) {
logLine += ` | Caller: ${metadata.caller}`;
}
const metaKeys = Object.keys(metadata).filter(
(key) =>
key !== "level" &&
key !== "message" &&
key !== "timestamp" &&
key !== "caller" &&
key !== "stack"
);
if (metaKeys.length > 0) {
const contextParts = metaKeys.map((key) => {
const value = metadata[key];
return `${key}=${
typeof value === "object" ? safeStringify(value) : value
}`;
});
logLine += ` | ${contextParts.join(" ")}`;
}
if (stack && (level === "error" || metadata.level === "error")) {
logLine += `\n Stack: ${stack}`;
}
return logLine;
}
);
this.winstonLogger = winston.createLogger({
level: this.level,
format: winston.format.combine(
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.errors({ stack: true }),
customFormat
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
customFormat
),
}),
new DailyRotateFile({
filename: join(this.logDir, "app-%DATE%.log"),
datePattern: "YYYY-MM-DD",
maxSize: "10m",
maxFiles: "7d",
level: "info",
zippedArchive: true,
}),
new DailyRotateFile({
filename: join(this.logDir, "error-%DATE%.log"),
datePattern: "YYYY-MM-DD",
maxSize: "10m",
maxFiles: "7d",
level: "error",
zippedArchive: true,
}),
],
});
this.winstonLogger.exitOnError = false;
}
info(message, context = {}) {
this.winstonLogger.info(message, context);
}
error(message, context = {}) {
this.winstonLogger.error(message, context);
}
warn(message, context = {}) {
this.winstonLogger.warn(message, context);
}
debug(message, context = {}) {
this.winstonLogger.debug(message, context);
}
child(bindings) {
return this.winstonLogger.child(bindings);
}
}