59 lines
2.1 KiB
JavaScript
59 lines
2.1 KiB
JavaScript
const fs = require('fs');
|
|
const { LOG_FILE } = require('../logger');
|
|
|
|
// Read the application/event log (JSON lines written by the Winston file transport) for the admin
|
|
// Application Logs viewer. Reads the current log file, parses the most recent lines, and returns them
|
|
// newest-first with optional category/level/search filtering plus the available filter facets.
|
|
|
|
const SCAN_LINES = 8000; // how many trailing lines to parse (the file is size-rotated, ~5MB)
|
|
|
|
function readLogs(filters = {}, limit = 500) {
|
|
let lines = [];
|
|
try {
|
|
lines = fs.readFileSync(LOG_FILE, 'utf8').split('\n').filter(Boolean);
|
|
} catch {
|
|
return { logs: [], total: 0, categories: [], levels: [] };
|
|
}
|
|
|
|
// Parse the trailing window, newest first.
|
|
const parsed = [];
|
|
for (let i = lines.length - 1, scanned = 0; i >= 0 && scanned < SCAN_LINES; i -= 1, scanned += 1) {
|
|
try {
|
|
const obj = JSON.parse(lines[i]);
|
|
parsed.push({
|
|
timestamp: obj.timestamp || null,
|
|
level: obj.level || 'info',
|
|
category: obj.category || 'system',
|
|
message: obj.message || '',
|
|
meta: stripMeta(obj)
|
|
});
|
|
} catch {
|
|
// skip malformed lines
|
|
}
|
|
}
|
|
|
|
const categories = [...new Set(parsed.map((p) => p.category))].sort();
|
|
const levels = [...new Set(parsed.map((p) => p.level))].sort();
|
|
|
|
let filtered = parsed;
|
|
if (filters.category) filtered = filtered.filter((p) => p.category === filters.category);
|
|
if (filters.level) filtered = filtered.filter((p) => p.level === filters.level);
|
|
if (filters.q) {
|
|
const needle = String(filters.q).toLowerCase();
|
|
filtered = filtered.filter((p) => `${p.message} ${JSON.stringify(p.meta)}`.toLowerCase().includes(needle));
|
|
}
|
|
|
|
return { logs: filtered.slice(0, Math.min(2000, Number(limit) || 500)), total: filtered.length, categories, levels };
|
|
}
|
|
|
|
// Everything on a log line except the standard fields is "meta" (the structured detail).
|
|
function stripMeta(obj) {
|
|
const meta = {};
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
if (!['timestamp', 'level', 'category', 'message'].includes(key)) meta[key] = value;
|
|
}
|
|
return meta;
|
|
}
|
|
|
|
module.exports = { readLogs };
|