码桶
发现社区成员的开源项目
logger.js2.1 KB
/**
* 日志落地模块
* 拦截 console.log/info/warn/error/debug,在保持原样输出到终端的同时,
* 追加写入到 data/logs/app-YYYY-MM-DD.log(按天滚动)。
* 供「日志控制台」页面读取展示。
*/
const fs = require('fs');
const path = require('path');
const LOG_DIR = path.join(__dirname, '..', 'data', 'logs');
try { fs.mkdirSync(LOG_DIR, { recursive: true }); } catch (e) { /* ignore */ }
function pad(n) { return String(n).padStart(2, '0'); }
function dateStamp(d) {
d = d || new Date();
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
function timeStamp(d) {
d = d || new Date();
return `${dateStamp(d)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
let currentDate = dateStamp();
function logFileFor(date) {
return path.join(LOG_DIR, `app-${date}.log`);
}
function serialize(a) {
if (typeof a === 'string') return a;
if (a instanceof Error) return a.stack || a.message;
try { return JSON.stringify(a); } catch (e) { try { return String(a); } catch (e2) { return '[unserializable]'; } }
}
function append(level, args) {
try {
const d = dateStamp();
if (d !== currentDate) currentDate = d; // 跨天滚动到新文件
const msg = args.map(serialize).join(' ');
const line = `[${timeStamp()}] [${level}] ${msg}\n`;
fs.appendFile(logFileFor(currentDate), line, () => {});
} catch (e) { /* 写入失败不影响主流程 */ }
}
// 保存原始方法,避免递归
const orig = {
log: console.log.bind(console),
info: console.info.bind(console),
warn: console.warn.bind(console),
error: console.error.bind(console),
debug: console.debug.bind(console),
};
function wrap(level, origFn) {
return function (...args) {
try { origFn(...args); } catch (e) { /* 原输出异常也不影响 */ }
append(level, args);
};
}
console.log = wrap('LOG', orig.log);
console.info = wrap('INFO', orig.info);
console.warn = wrap('WARN', orig.warn);
console.error = wrap('ERROR', orig.error);
console.debug = wrap('DEBUG', orig.debug);
module.exports = { LOG_DIR, dateStamp, logFileFor };