summaryrefslogtreecommitdiff
path: root/logger.go
blob: 595b3f8fcaf752caee03c7ae4120319bddc6ddbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package main

import (
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// LogLevel represents the severity of a log message
type LogLevel string

const (
	LogLevelInfo    LogLevel = "info"
	LogLevelWarning LogLevel = "warning"
	LogLevelError   LogLevel = "error"
	LogLevelSuccess LogLevel = "success"
	LogLevelDebug   LogLevel = "debug"
	LogLevelCUDA    LogLevel = "cuda"
)

// LogMessage represents a log entry
type LogMessage struct {
	Timestamp string   `json:"timestamp"`
	Level     LogLevel `json:"level"`
	Message   string   `json:"message"`
	Source    string   `json:"source"`
}

// Logger handles WebSocket connections and broadcasts log messages
type Logger struct {
	clients    map[*websocket.Conn]bool
	broadcast  chan LogMessage
	register   chan *websocket.Conn
	unregister chan *websocket.Conn
	mutex      sync.RWMutex
	history    []LogMessage
	maxHistory int
}

// Global logger instance
var globalLogger *Logger

// Initialize the logger
func initLogger() {
	globalLogger = &Logger{
		clients:    make(map[*websocket.Conn]bool),
		broadcast:  make(chan LogMessage, 256),
		register:   make(chan *websocket.Conn),
		unregister: make(chan *websocket.Conn),
		history:    make([]LogMessage, 0),
		maxHistory: 100,
	}

	go globalLogger.run()

	// Initial system info
	LogInfo("System", "Logger initialized")
	LogInfo("System", fmt.Sprintf("Operating System: Windows"))
	LogInfo("System", fmt.Sprintf("Application: CUDA Torus Visualization"))
}

// WebSocket upgrader
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		origin := r.Header.Get("Origin")
		return origin == "" || origin == "http://localhost:8080" || origin == "http://127.0.0.1:8080"
	},
}

// WebSocket handler
func wsLogHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("WebSocket upgrade error:", err)
		return
	}

	globalLogger.register <- conn

	// Send log history to new client
	globalLogger.mutex.RLock()
	for _, msg := range globalLogger.history {
		if err := conn.WriteJSON(msg); err != nil {
			globalLogger.mutex.RUnlock()
			globalLogger.unregister <- conn
			conn.Close()
			return
		}
	}
	globalLogger.mutex.RUnlock()

	// Keep connection alive and handle disconnection
	defer func() {
		globalLogger.unregister <- conn
		conn.Close()
	}()

	// Read messages from client (ping/pong)
	for {
		_, _, err := conn.ReadMessage()
		if err != nil {
			break
		}
	}
}

// Run the logger hub
func (l *Logger) run() {
	for {
		select {
		case client := <-l.register:
			l.mutex.Lock()
			l.clients[client] = true
			l.mutex.Unlock()

			LogInfo("WebSocket", "New client connected to log stream")

		case client := <-l.unregister:
			l.mutex.Lock()
			if _, ok := l.clients[client]; ok {
				delete(l.clients, client)
				client.Close()
				LogInfo("WebSocket", "Client disconnected from log stream")
			}
			l.mutex.Unlock()

		case message := <-l.broadcast:
			// Add to history
			l.mutex.Lock()
			l.history = append(l.history, message)
			if len(l.history) > l.maxHistory {
				l.history = l.history[1:]
			}
			l.mutex.Unlock()

			// Broadcast to all clients
			l.mutex.RLock()
			for client := range l.clients {
				err := client.WriteJSON(message)
				if err != nil {
					delete(l.clients, client)
					client.Close()
				}
			}
			l.mutex.RUnlock()
		}
	}
}

// LogInfo logs an info message
func LogInfo(source, message string) {
	logMessage(LogLevelInfo, source, message)
	fmt.Printf("[INFO] %s: %s\n", source, message)
}

// LogWarning logs a warning message
func LogWarning(source, message string) {
	logMessage(LogLevelWarning, source, message)
	fmt.Printf("[WARNING] %s: %s\n", source, message)
}

// LogError logs an error message
func LogError(source, message string) {
	logMessage(LogLevelError, source, message)
	fmt.Printf("[ERROR] %s: %s\n", source, message)
}

// LogSuccess logs a success message
func LogSuccess(source, message string) {
	logMessage(LogLevelSuccess, source, message)
	fmt.Printf("[SUCCESS] %s: %s\n", source, message)
}

// LogDebug logs a debug message
func LogDebug(source, message string) {
	logMessage(LogLevelDebug, source, message)
	fmt.Printf("[DEBUG] %s: %s\n", source, message)
}

// LogCUDA logs a CUDA-specific message
func LogCUDA(source, message string) {
	logMessage(LogLevelCUDA, source, message)
	fmt.Printf("[CUDA] %s: %s\n", source, message)
}

// Helper function to send log messages
func logMessage(level LogLevel, source, message string) {
	if globalLogger == nil {
		return
	}

	logMsg := LogMessage{
		Timestamp: time.Now().Format("15:04:05.000"),
		Level:     level,
		Message:   message,
		Source:    source,
	}

	select {
	case globalLogger.broadcast <- logMsg:
	default:
		// Channel is full, drop the message
	}
}