Files
LogMaster/dist-server/logSourceManager.js
Kevin 44cd9ab001
Some checks failed
LogMaster CI/CD / build-and-test (push) Has been cancelled
LogMaster CI/CD / docker (push) Has been cancelled
Initial commit: LogMaster with Viewer Collector Monitor modes
2026-05-04 13:50:15 +02:00

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