mirror of https://github.com/jetkvm/kvm.git
				
				
				
			
		
			
				
	
	
		
			319 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
 | 
						|
<head>
 | 
						|
    <meta charset="UTF-8">
 | 
						|
    <title>Server Sent Event</title>
 | 
						|
    <style>
 | 
						|
        .main-container {
 | 
						|
            display: flex;
 | 
						|
            flex-direction: column;
 | 
						|
            gap: 10px;
 | 
						|
 | 
						|
            font-family: 'Hack', monospace;
 | 
						|
            font-size: 12px;
 | 
						|
        }
 | 
						|
 | 
						|
        #loading {
 | 
						|
            font-style: italic;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry {
 | 
						|
            font-size: 12px;
 | 
						|
            line-height: 1.2;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry > span {
 | 
						|
            min-width: 0;
 | 
						|
            overflow-wrap: break-word;
 | 
						|
            word-break: break-word;
 | 
						|
            margin-right: 10px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry > span:last-child {
 | 
						|
            margin-right: 0;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-trace .log-level {
 | 
						|
            color: blue;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-debug .log-level {
 | 
						|
            color: gray;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-info .log-level {
 | 
						|
            color: green;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-warn .log-level {
 | 
						|
            color: yellow;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-error .log-level,
 | 
						|
        .log-entry.log-entry-fatal .log-level,
 | 
						|
        .log-entry.log-entry-panic .log-level {
 | 
						|
            color: red;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-entry.log-entry-info .log-message,
 | 
						|
        .log-entry.log-entry-warn .log-message,
 | 
						|
        .log-entry.log-entry-error .log-message,
 | 
						|
        .log-entry.log-entry-fatal .log-message,
 | 
						|
        .log-entry.log-entry-panic .log-message {
 | 
						|
            font-weight: bold;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-timestamp {
 | 
						|
            color: #666;
 | 
						|
            min-width: 150px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-level {
 | 
						|
            font-size: 12px;
 | 
						|
            min-width: 50px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-scope {
 | 
						|
            font-size: 12px;
 | 
						|
            min-width: 40px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-component {
 | 
						|
            font-size: 12px;
 | 
						|
            min-width: 80px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-message {
 | 
						|
            font-size: 12px;
 | 
						|
            white-space: nowrap;
 | 
						|
            overflow: hidden;
 | 
						|
            text-overflow: ellipsis;
 | 
						|
            max-width: 500px;
 | 
						|
        }
 | 
						|
 | 
						|
        .log-extras {
 | 
						|
            color: #000;
 | 
						|
        }
 | 
						|
        .log-extras .log-extras-header {
 | 
						|
            font-weight: bold;
 | 
						|
            color:cornflowerblue;
 | 
						|
        }
 | 
						|
    </style>
 | 
						|
</head>
 | 
						|
 | 
						|
<body>
 | 
						|
    <div class="main-container">
 | 
						|
        <div id="header">
 | 
						|
            <span id="loading">
 | 
						|
            Connecting to log stream...
 | 
						|
            </span>
 | 
						|
 | 
						|
            <span id="stats">
 | 
						|
 | 
						|
            </span>
 | 
						|
        </div>
 | 
						|
        <div id="event-data">
 | 
						|
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
</body>
 | 
						|
 | 
						|
<script>
 | 
						|
    class LogStream {
 | 
						|
        constructor(url, eventDataElement, loadingElement, statsElement) {
 | 
						|
            this.url = url;
 | 
						|
            this.eventDataElement = eventDataElement;
 | 
						|
            this.loadingElement = loadingElement;
 | 
						|
            this.statsElement = statsElement;
 | 
						|
            this.stream = null;
 | 
						|
            this.reconnectAttempts = 0;
 | 
						|
            this.maxReconnectAttempts = 10;
 | 
						|
            this.reconnectDelay = 1000; // Start with 1 second
 | 
						|
            this.maxReconnectDelay = 30000; // Max 30 seconds
 | 
						|
            this.isConnecting = false;
 | 
						|
 | 
						|
            this.totalMessages = 0;
 | 
						|
            
 | 
						|
            this.connect();
 | 
						|
        }
 | 
						|
 | 
						|
        connect() {
 | 
						|
            if (this.isConnecting) return;
 | 
						|
            this.isConnecting = true;
 | 
						|
            
 | 
						|
            this.loadingElement.innerText = "Connecting to log stream...";
 | 
						|
            
 | 
						|
            this.stream = new EventSource(this.url);
 | 
						|
            
 | 
						|
            this.stream.onopen = () => {
 | 
						|
                this.isConnecting = false;
 | 
						|
                this.reconnectAttempts = 0;
 | 
						|
                this.reconnectDelay = 1000;
 | 
						|
                this.loadingElement.innerText = "Log stream connected.";
 | 
						|
 | 
						|
 | 
						|
                this.totalMessages = 0;
 | 
						|
                this.totalBytes = 0;
 | 
						|
            };
 | 
						|
            
 | 
						|
            this.stream.onmessage = (event) => {
 | 
						|
                this.totalBytes += event.data.length;
 | 
						|
                this.totalMessages++;
 | 
						|
 | 
						|
                const data = JSON.parse(event.data);
 | 
						|
                this.addLogEntry(data);
 | 
						|
                this.updateStats();
 | 
						|
            };
 | 
						|
            
 | 
						|
            this.stream.onerror = () => {
 | 
						|
                this.isConnecting = false;
 | 
						|
                this.loadingElement.innerText = "Log stream disconnected.";
 | 
						|
                this.stream.close();
 | 
						|
                this.handleReconnect();
 | 
						|
            };
 | 
						|
        }
 | 
						|
 | 
						|
        updateStats() {
 | 
						|
            this.statsElement.innerHTML = `Messages: <strong>${this.totalMessages}</strong>, Bytes: <strong>${this.totalBytes}</strong> `;
 | 
						|
        }
 | 
						|
        
 | 
						|
        handleReconnect() {
 | 
						|
            if (this.reconnectAttempts >= this.maxReconnectAttempts) {
 | 
						|
                this.loadingElement.innerText = "Failed to reconnect after multiple attempts";
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            
 | 
						|
            this.reconnectAttempts++;
 | 
						|
            this.reconnectDelay = Math.min(this.reconnectDelay * 1, this.maxReconnectDelay);
 | 
						|
            
 | 
						|
            this.loadingElement.innerText = `Reconnecting in ${this.reconnectDelay/1000} seconds... (Attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`;
 | 
						|
            
 | 
						|
            setTimeout(() => {
 | 
						|
                this.connect();
 | 
						|
            }, this.reconnectDelay);
 | 
						|
        }
 | 
						|
        
 | 
						|
        addLogEntry(data) {
 | 
						|
            const el = document.createElement("div");
 | 
						|
            el.className = "log-entry log-entry-" + data.level;
 | 
						|
 | 
						|
            const timestamp = document.createElement("span");
 | 
						|
            timestamp.className = "log-timestamp";
 | 
						|
            timestamp.innerText = data.time;
 | 
						|
            el.appendChild(timestamp);
 | 
						|
 | 
						|
            const level = document.createElement("span");
 | 
						|
            level.className = "log-level";
 | 
						|
            level.innerText = this.shortLogLevel(data.level);
 | 
						|
            el.appendChild(level);
 | 
						|
 | 
						|
            const scope = document.createElement("span");
 | 
						|
            scope.className = "log-scope";
 | 
						|
            scope.innerText = data.scope;
 | 
						|
            el.appendChild(scope);
 | 
						|
 | 
						|
            const component = document.createElement("span");
 | 
						|
            component.className = "log-component";
 | 
						|
            component.innerText = data.component;
 | 
						|
            el.appendChild(component);
 | 
						|
 | 
						|
            const message = document.createElement("span");
 | 
						|
            message.className = "log-message";
 | 
						|
            message.innerText = data.message;
 | 
						|
            el.appendChild(message);
 | 
						|
 | 
						|
            this.addLogExtras(el, data);
 | 
						|
 | 
						|
            this.eventDataElement.appendChild(el);
 | 
						|
 | 
						|
            window.scrollTo(0, document.body.scrollHeight);
 | 
						|
        }
 | 
						|
        
 | 
						|
        shortLogLevel(level) {
 | 
						|
            switch (level) {
 | 
						|
                case "trace":
 | 
						|
                    return "TRC";
 | 
						|
                case "debug":
 | 
						|
                    return "DBG";
 | 
						|
                case "info":
 | 
						|
                    return "INF";
 | 
						|
                case "warn":
 | 
						|
                    return "WRN";
 | 
						|
                case "error":
 | 
						|
                    return "ERR";
 | 
						|
                case "fatal":
 | 
						|
                    return "FTL";
 | 
						|
                case "panic":
 | 
						|
                    return "PNC";
 | 
						|
                default:
 | 
						|
                    return level;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        addLogExtras(el, data) {
 | 
						|
            const excludeKeys = [
 | 
						|
                "timestamp",
 | 
						|
                "time",
 | 
						|
                "level",
 | 
						|
                "scope",
 | 
						|
                "component",
 | 
						|
                "message",
 | 
						|
            ];
 | 
						|
            
 | 
						|
            const extras = {};
 | 
						|
            for (const key in data) {
 | 
						|
                if (excludeKeys.includes(key)) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                extras[key] = data[key];
 | 
						|
            }
 | 
						|
 | 
						|
            for (const key in extras) {
 | 
						|
                const extra = document.createElement("span");
 | 
						|
                extra.className = "log-extras log-extras-" + key;
 | 
						|
 | 
						|
                const extraKey = document.createElement("span");
 | 
						|
                extraKey.className = "log-extras-header";
 | 
						|
                extraKey.innerText = key + '=';
 | 
						|
                extra.appendChild(extraKey);
 | 
						|
 | 
						|
                const extraValue = document.createElement("span");
 | 
						|
                extraValue.className = "log-extras-value";
 | 
						|
                
 | 
						|
                let value = extras[key];
 | 
						|
                if (typeof value === 'object') {
 | 
						|
                    value = JSON.stringify(value);
 | 
						|
                }   
 | 
						|
                extraValue.innerText = value;
 | 
						|
                extra.appendChild(extraValue);
 | 
						|
 | 
						|
                el.appendChild(extra);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        disconnect() {
 | 
						|
            if (this.stream) {
 | 
						|
                this.stream.close();
 | 
						|
                this.stream = null;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Initialize the log stream when the page loads
 | 
						|
    document.addEventListener('DOMContentLoaded', () => {
 | 
						|
        const logStream = new LogStream(
 | 
						|
            "/developer/log-stream",
 | 
						|
            document.getElementById("event-data"),
 | 
						|
            document.getElementById("loading"),
 | 
						|
            document.getElementById("stats"),
 | 
						|
        );
 | 
						|
        
 | 
						|
        // Clean up when the page is unloaded
 | 
						|
        window.addEventListener('beforeunload', () => {
 | 
						|
            logStream.disconnect();
 | 
						|
        });
 | 
						|
    });
 | 
						|
</script>
 | 
						|
 | 
						|
</html> |