152 lines
3.3 KiB
JavaScript
152 lines
3.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { logLevel } = require('../config');
|
|
const { getBackendLogDir, getFallbackLogRootDir } = require('./logPathService');
|
|
|
|
const LEVELS = {
|
|
debug: 10,
|
|
info: 20,
|
|
warn: 30,
|
|
error: 40
|
|
};
|
|
|
|
const ACTIVE_LEVEL = LEVELS[String(logLevel || 'info').toLowerCase()] || LEVELS.info;
|
|
|
|
function ensureLogDir(logDirPath) {
|
|
try {
|
|
fs.mkdirSync(logDirPath, { recursive: true });
|
|
return true;
|
|
} catch (_error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function resolveWritableBackendLogDir() {
|
|
const preferred = getBackendLogDir();
|
|
if (ensureLogDir(preferred)) {
|
|
return preferred;
|
|
}
|
|
|
|
const fallback = path.join(getFallbackLogRootDir(), 'backend');
|
|
if (fallback !== preferred && ensureLogDir(fallback)) {
|
|
return fallback;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getDailyFileName() {
|
|
const d = new Date();
|
|
const y = d.getFullYear();
|
|
const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
return `backend-${y}-${m}-${day}.log`;
|
|
}
|
|
|
|
function safeJson(value) {
|
|
try {
|
|
return JSON.stringify(value);
|
|
} catch (error) {
|
|
return JSON.stringify({ serializationError: error.message });
|
|
}
|
|
}
|
|
|
|
function truncateString(value, maxLen = 3000) {
|
|
const str = String(value);
|
|
if (str.length <= maxLen) {
|
|
return str;
|
|
}
|
|
return `${str.slice(0, maxLen)}...[truncated ${str.length - maxLen} chars]`;
|
|
}
|
|
|
|
function sanitizeMeta(meta) {
|
|
if (!meta || typeof meta !== 'object') {
|
|
return meta;
|
|
}
|
|
|
|
const out = Array.isArray(meta) ? [] : {};
|
|
|
|
for (const [key, val] of Object.entries(meta)) {
|
|
if (val instanceof Error) {
|
|
out[key] = {
|
|
name: val.name,
|
|
message: val.message,
|
|
stack: val.stack
|
|
};
|
|
continue;
|
|
}
|
|
|
|
if (typeof val === 'string') {
|
|
out[key] = truncateString(val, 5000);
|
|
continue;
|
|
}
|
|
|
|
out[key] = val;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function writeLine(line) {
|
|
const backendLogDir = resolveWritableBackendLogDir();
|
|
if (!backendLogDir) {
|
|
return;
|
|
}
|
|
const daily = path.join(backendLogDir, getDailyFileName());
|
|
const latest = path.join(backendLogDir, 'backend-latest.log');
|
|
|
|
fs.appendFile(daily, `${line}\n`, (_error) => null);
|
|
fs.appendFile(latest, `${line}\n`, (_error) => null);
|
|
}
|
|
|
|
function emit(level, scope, message, meta = null) {
|
|
const normLevel = String(level || 'info').toLowerCase();
|
|
const lvl = LEVELS[normLevel] || LEVELS.info;
|
|
if (lvl < ACTIVE_LEVEL) {
|
|
return;
|
|
}
|
|
|
|
const timestamp = new Date().toISOString();
|
|
const payload = {
|
|
timestamp,
|
|
level: normLevel,
|
|
scope,
|
|
message,
|
|
meta: sanitizeMeta(meta)
|
|
};
|
|
|
|
const line = safeJson(payload);
|
|
writeLine(line);
|
|
|
|
const print = `[${timestamp}] [${normLevel.toUpperCase()}] [${scope}] ${message}`;
|
|
if (normLevel === 'error') {
|
|
console.error(print, payload.meta ? payload.meta : '');
|
|
} else if (normLevel === 'warn') {
|
|
console.warn(print, payload.meta ? payload.meta : '');
|
|
} else {
|
|
console.log(print, payload.meta ? payload.meta : '');
|
|
}
|
|
}
|
|
|
|
function child(scope) {
|
|
return {
|
|
debug(message, meta) {
|
|
emit('debug', scope, message, meta);
|
|
},
|
|
info(message, meta) {
|
|
emit('info', scope, message, meta);
|
|
},
|
|
warn(message, meta) {
|
|
emit('warn', scope, message, meta);
|
|
},
|
|
error(message, meta) {
|
|
emit('error', scope, message, meta);
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
child,
|
|
emit
|
|
};
|