From f6caa6f6b3b5e75da3eec3ab0b14baab125317a4 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Sun, 7 Sep 2025 07:26:35 +0200 Subject: [PATCH] Add a web logger --- pmolog/webloger.go | 152 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 pmolog/webloger.go diff --git a/pmolog/webloger.go b/pmolog/webloger.go new file mode 100644 index 00000000..97f5dec6 --- /dev/null +++ b/pmolog/webloger.go @@ -0,0 +1,152 @@ +package pmolog + +import ( + "fmt" + "net/http" + "sync" + + "github.com/sirupsen/logrus" +) + +// Structure pour gérer les connexions SSE +type SSEBroker struct { + clients map[chan string]bool + mutex sync.Mutex +} + +// Initialiser le broker SSE +var broker = &SSEBroker{ + clients: make(map[chan string]bool), +} + +// Hook personnalisé pour Logrus qui envoie les logs aux clients SSE +type SSELogHook struct{} + +func (hook *SSELogHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +func (hook *SSELogHook) Fire(entry *logrus.Entry) error { + // Formater le log + logLine := fmt.Sprintf("[%s] %s", entry.Level.String(), entry.Message) + + // Envoyer le log à tous les clients connectés + broker.mutex.Lock() + for client := range broker.clients { + // Non-bloquant pour éviter qu'un client lent ne bloque tout + select { + case client <- logLine: + default: + // Client saturé, on skip + } + } + broker.mutex.Unlock() + + return nil +} + +// Handler SSE qui envoie les logs en temps réel +func sseHandler(w http.ResponseWriter, r *http.Request) { + // Configurer les en-têtes SSE + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + w.Header().Set("Access-Control-Allow-Origin", "*") + + // Créer un canal pour ce client + messageChan := make(chan string, 10) + + // Ajouter le client au broker + broker.mutex.Lock() + broker.clients[messageChan] = true + broker.mutex.Unlock() + + // Envoyer un message de bienvenue + fmt.Fprintf(w, "data: %s\n\n", "Connexion établie. Attente des logs...") + w.(http.Flusher).Flush() + + // Envoyer les logs au client au fur et à mesure + for { + select { + case msg := <-messageChan: + fmt.Fprintf(w, "data: %s\n\n", msg) + w.(http.Flusher).Flush() + case <-r.Context().Done(): + // Supprimer le client quand la connexion est fermée + broker.mutex.Lock() + delete(broker.clients, messageChan) + broker.mutex.Unlock() + close(messageChan) + return + } + } +} + +// Page HTML pour afficher les logs +var indexHTML = ` + + + + Logs en temps réel + + + +

Logs en temps réel

+
+ + + + +` + +// Handler pour servir la page HTML +func indexHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, indexHTML) +} + +func LoggerWeb(mux *http.ServeMux) { + // Ajouter le hook SSE à Logrus + logrus.AddHook(&SSELogHook{}) + + // Configurer Logrus pour le développement + logrus.SetFormatter(&logrus.TextFormatter{ + ForceColors: true, + FullTimestamp: true, + }) + + mux.HandleFunc("/log", indexHandler) + mux.HandleFunc("/log-sse", sseHandler) + + logrus.Info("Web logger connected") + +}