mcpdog
Version:
MCPDog - Universal MCP Server Manager with Web Interface
185 lines • 6.04 kB
JavaScript
import { EventEmitter } from 'events';
export class ServerLogManager extends EventEmitter {
logs = new Map();
maxLogsPerServer = 1000;
logStats = new Map();
constructor(maxLogsPerServer = 1000) {
super();
this.maxLogsPerServer = maxLogsPerServer;
}
/**
* Add log entry
*/
addLog(serverName, level, message, source = 'system') {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level,
message,
source,
serverName
};
// Get or create server log array
if (!this.logs.has(serverName)) {
this.logs.set(serverName, []);
}
const serverLogs = this.logs.get(serverName);
serverLogs.push(logEntry);
// Limit log count
if (serverLogs.length > this.maxLogsPerServer) {
serverLogs.splice(0, serverLogs.length - this.maxLogsPerServer);
}
// Update statistics
this.updateStats(serverName, logEntry);
// Emit log event
this.emit('log-added', { serverName, logEntry });
// If it's an error, emit error event
if (level === 'error') {
this.emit('server-error', { serverName, message, timestamp });
}
}
/**
* Get server logs
*/
getLogs(serverName, limit) {
const logs = this.logs.get(serverName) || [];
if (limit && limit > 0) {
return logs.slice(-limit);
}
return [...logs];
}
/**
* Get recent logs for all servers
*/
getAllRecentLogs(limit = 50) {
const allLogs = [];
for (const [serverName, logs] of this.logs) {
allLogs.push(...logs);
}
// Sort by time and limit quantity
return allLogs
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
.slice(0, limit);
}
/**
* Get server statistics
*/
getStats(serverName) {
return this.logStats.get(serverName);
}
/**
* Get all server statistics
*/
getAllStats() {
return new Map(this.logStats);
}
/**
* Clear server logs
*/
clearLogs(serverName) {
this.logs.delete(serverName);
this.logStats.delete(serverName);
this.emit('logs-cleared', { serverName });
}
/**
* Clear all logs
*/
clearAllLogs() {
this.logs.clear();
this.logStats.clear();
this.emit('all-logs-cleared');
}
/**
* Log server connection status changes
*/
updateConnectionStatus(serverName, isConnected, error) {
const stats = this.getOrCreateStats(serverName);
const wasConnected = stats.isConnected;
stats.isConnected = isConnected;
stats.lastActivity = new Date().toISOString();
if (!isConnected) {
stats.connectionAttempts++;
if (error) {
stats.lastError = error;
this.addLog(serverName, 'error', `Connection failed: ${error}`, 'system');
}
}
else if (!wasConnected) {
this.addLog(serverName, 'info', 'Server connected successfully', 'system');
}
this.emit('connection-status-changed', { serverName, isConnected, stats });
}
/**
* Log server output
*/
addServerOutput(serverName, data, source) {
const lines = data.toString().split('\n').filter(line => line.trim() !== '');
for (const line of lines) {
const level = source === 'stderr' ? 'error' : 'info';
this.addLog(serverName, level, line.trim(), source);
}
}
/**
* Search logs
*/
searchLogs(serverName, query, options) {
const logs = this.logs.get(serverName) || [];
const searchQuery = query.toLowerCase();
let filteredLogs = logs.filter(log => {
const matchesQuery = log.message.toLowerCase().includes(searchQuery);
const matchesLevel = !options?.level || log.level === options.level;
const matchesSource = !options?.source || log.source === options.source;
return matchesQuery && matchesLevel && matchesSource;
});
if (options?.limit && options.limit > 0) {
filteredLogs = filteredLogs.slice(-options.limit);
}
return filteredLogs;
}
/**
* Export logs as text format
*/
exportLogs(serverName, format = 'text') {
const logs = this.logs.get(serverName) || [];
if (format === 'json') {
return JSON.stringify(logs, null, 2);
}
// Text format
return logs.map(log => {
const timestamp = new Date(log.timestamp).toLocaleString();
const source = log.source ? `[${log.source.toUpperCase()}]` : '';
const level = `[${log.level.toUpperCase()}]`;
return `${timestamp} ${level} ${source} ${log.message}`;
}).join('\n');
}
updateStats(serverName, logEntry) {
const stats = this.getOrCreateStats(serverName);
stats.totalLogs++;
stats.lastActivity = logEntry.timestamp;
switch (logEntry.level) {
case 'error':
stats.errorCount++;
stats.lastError = logEntry.message;
break;
case 'warn':
stats.warnCount++;
break;
}
}
getOrCreateStats(serverName) {
if (!this.logStats.has(serverName)) {
this.logStats.set(serverName, {
totalLogs: 0,
errorCount: 0,
warnCount: 0,
lastActivity: new Date().toISOString(),
isConnected: false,
connectionAttempts: 0
});
}
return this.logStats.get(serverName);
}
}
// Global log manager instance
export const globalLogManager = new ServerLogManager();
//# sourceMappingURL=server-log-manager.js.map