280 lines
9.7 KiB
JavaScript
280 lines
9.7 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.LogSourceManager = void 0;
|
|
const fs = __importStar(require("fs"));
|
|
const path = __importStar(require("path"));
|
|
const zlib = __importStar(require("zlib"));
|
|
const child_process_1 = require("child_process");
|
|
class LogSourceManager {
|
|
configManager;
|
|
sources = new Map();
|
|
constructor(configManager) {
|
|
this.configManager = configManager;
|
|
this.initializeSources();
|
|
}
|
|
initializeSources() {
|
|
const configSources = this.configManager.getSources();
|
|
for (const source of configSources) {
|
|
this.addSource(source);
|
|
}
|
|
}
|
|
addSource(sourcePath) {
|
|
if (sourcePath.includes('*')) {
|
|
const resolvedPaths = this.resolveGlob(sourcePath);
|
|
for (const rp of resolvedPaths) {
|
|
this.addSingleSource(rp);
|
|
}
|
|
}
|
|
else {
|
|
this.addSingleSource(sourcePath);
|
|
}
|
|
this.configManager.updateSources(Array.from(this.sources.keys()));
|
|
}
|
|
addSingleSource(sourcePath) {
|
|
const resolved = path.resolve(sourcePath);
|
|
const type = this.detectType(resolved);
|
|
let size = 0;
|
|
let lastModified = null;
|
|
let exists = false;
|
|
try {
|
|
const stats = fs.statSync(resolved);
|
|
size = stats.size;
|
|
lastModified = stats.mtime.toISOString();
|
|
exists = true;
|
|
}
|
|
catch {
|
|
// File doesn't exist yet
|
|
}
|
|
this.sources.set(resolved, {
|
|
path: resolved,
|
|
displayName: path.basename(resolved),
|
|
type,
|
|
size,
|
|
lastModified,
|
|
exists,
|
|
});
|
|
}
|
|
removeSource(sourcePath) {
|
|
const resolved = path.resolve(sourcePath);
|
|
this.sources.delete(resolved);
|
|
this.configManager.updateSources(Array.from(this.sources.keys()));
|
|
}
|
|
getSources() {
|
|
for (const [key, source] of this.sources) {
|
|
try {
|
|
const stats = fs.statSync(key);
|
|
source.size = stats.size;
|
|
source.lastModified = stats.mtime.toISOString();
|
|
source.exists = true;
|
|
}
|
|
catch {
|
|
source.exists = false;
|
|
}
|
|
}
|
|
return Array.from(this.sources.values());
|
|
}
|
|
detectType(filePath) {
|
|
const ext = path.extname(filePath).toLowerCase();
|
|
switch (ext) {
|
|
case '.gz':
|
|
case '.gzip': return 'gzip';
|
|
case '.bz2':
|
|
case '.bzip2': return 'bzip2';
|
|
case '.xz': return 'xz';
|
|
case '.zst':
|
|
case '.zstd': return 'zstd';
|
|
default: return 'text';
|
|
}
|
|
}
|
|
async readLog(sourcePath) {
|
|
const resolved = path.resolve(sourcePath);
|
|
const source = this.sources.get(resolved);
|
|
if (!source)
|
|
throw new Error(`Source not found: ${sourcePath}`);
|
|
const config = this.configManager.getConfig();
|
|
const content = this.readFileContent(resolved, source.type);
|
|
const lines = content.split('\n');
|
|
const maxLines = config.maxLines;
|
|
const startIndex = lines.length > maxLines ? lines.length - maxLines : 0;
|
|
const entries = [];
|
|
for (let i = startIndex; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
if (line.trim() === '')
|
|
continue;
|
|
entries.push({
|
|
line,
|
|
lineNumber: i + 1,
|
|
source: resolved,
|
|
timestamp: this.extractTimestamp(line, config.timestampRegex),
|
|
level: this.extractLevel(line),
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
async readAllLogs() {
|
|
const allEntries = [];
|
|
for (const [sourcePath] of this.sources) {
|
|
try {
|
|
const entries = await this.readLog(sourcePath);
|
|
allEntries.push(...entries);
|
|
}
|
|
catch (err) {
|
|
allEntries.push({
|
|
line: `[ERROR] Could not read ${sourcePath}: ${err.message}`,
|
|
lineNumber: 0,
|
|
source: sourcePath,
|
|
timestamp: new Date().toISOString(),
|
|
level: 'ERROR',
|
|
});
|
|
}
|
|
}
|
|
allEntries.sort((a, b) => {
|
|
if (a.timestamp && b.timestamp)
|
|
return a.timestamp.localeCompare(b.timestamp);
|
|
return 0;
|
|
});
|
|
return allEntries;
|
|
}
|
|
readFileContent(filePath, type) {
|
|
switch (type) {
|
|
case 'gzip': return this.readGzip(filePath);
|
|
case 'bzip2': return this.readBzip2(filePath);
|
|
case 'xz': return this.readXz(filePath);
|
|
case 'zstd': return this.readZstd(filePath);
|
|
default: return fs.readFileSync(filePath, 'utf-8');
|
|
}
|
|
}
|
|
readGzip(filePath) {
|
|
const compressed = fs.readFileSync(filePath);
|
|
return zlib.gunzipSync(compressed).toString('utf-8');
|
|
}
|
|
readBzip2(filePath) {
|
|
try {
|
|
return (0, child_process_1.execSync)(`bzcat "${filePath}"`, { maxBuffer: 100 * 1024 * 1024 }).toString('utf-8');
|
|
}
|
|
catch {
|
|
throw new Error('Cannot decompress bzip2. Make sure bzip2 is installed.');
|
|
}
|
|
}
|
|
readXz(filePath) {
|
|
try {
|
|
return (0, child_process_1.execSync)(`xzcat "${filePath}"`, { maxBuffer: 100 * 1024 * 1024 }).toString('utf-8');
|
|
}
|
|
catch {
|
|
throw new Error('Cannot decompress xz. Make sure xz-utils is installed.');
|
|
}
|
|
}
|
|
readZstd(filePath) {
|
|
try {
|
|
return (0, child_process_1.execSync)(`zstdcat "${filePath}"`, { maxBuffer: 100 * 1024 * 1024 }).toString('utf-8');
|
|
}
|
|
catch {
|
|
throw new Error('Cannot decompress zstd. Make sure zstd is installed.');
|
|
}
|
|
}
|
|
extractTimestamp(line, customRegex) {
|
|
const patterns = [
|
|
/(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)/,
|
|
/^([A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})/,
|
|
/\[(\d{2}\/[A-Z][a-z]{2}\/\d{4}:\d{2}:\d{2}:\d{2}\s[+-]\d{4})\]/,
|
|
/(\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2})/,
|
|
];
|
|
if (customRegex) {
|
|
try {
|
|
const match = line.match(new RegExp(customRegex));
|
|
if (match)
|
|
return match[1] || match[0];
|
|
}
|
|
catch { /* skip */ }
|
|
}
|
|
for (const pattern of patterns) {
|
|
const match = line.match(pattern);
|
|
if (match)
|
|
return match[1];
|
|
}
|
|
return null;
|
|
}
|
|
extractLevel(line) {
|
|
const upper = line.toUpperCase();
|
|
const levels = ['FATAL', 'ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG', 'TRACE', 'VERBOSE'];
|
|
for (const level of levels) {
|
|
if (new RegExp(`\\b${level}\\b`).test(upper)) {
|
|
return level === 'WARNING' ? 'WARN' : level;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
resolveGlob(pattern) {
|
|
const dir = path.dirname(pattern);
|
|
const filePattern = path.basename(pattern);
|
|
const results = [];
|
|
try {
|
|
const files = fs.readdirSync(dir);
|
|
const regex = new RegExp('^' + filePattern.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
for (const file of files) {
|
|
if (regex.test(file)) {
|
|
const fullPath = path.join(dir, file);
|
|
try {
|
|
if (fs.statSync(fullPath).isFile())
|
|
results.push(fullPath);
|
|
}
|
|
catch { /* skip */ }
|
|
}
|
|
}
|
|
}
|
|
catch { /* dir doesn't exist */ }
|
|
return results;
|
|
}
|
|
async getFileStats(sourcePath) {
|
|
const resolved = path.resolve(sourcePath);
|
|
try {
|
|
const stats = fs.statSync(resolved);
|
|
const source = this.sources.get(resolved);
|
|
const content = this.readFileContent(resolved, source?.type || 'text');
|
|
return {
|
|
size: stats.size,
|
|
lastModified: stats.mtime.toISOString(),
|
|
lineCount: content.split('\n').length,
|
|
type: source?.type || 'text',
|
|
};
|
|
}
|
|
catch {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
exports.LogSourceManager = LogSourceManager;
|
|
//# sourceMappingURL=logSourceManager.js.map
|