Files
FlexDXClusterGui/log.go
2025-10-19 10:15:11 +02:00

164 lines
3.6 KiB
Go

package main
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"time"
log "github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
var Log *log.Logger
var logFile *os.File
var logWriter *syncWriter
var logCtx context.Context
var logCancel context.CancelFunc
// syncWriter écrit de manière synchrone (pas de buffer)
type syncWriter struct {
file *os.File
mutex sync.Mutex
}
func (w *syncWriter) Write(p []byte) (n int, err error) {
w.mutex.Lock()
defer w.mutex.Unlock()
n, err = w.file.Write(p)
if err == nil {
w.file.Sync() // Force l'écriture immédiate sur disque
}
return n, err
}
func NewLog() *log.Logger {
// ✅ Vérifier que Cfg existe
if Cfg == nil {
panic("Config not initialized! Call NewConfig() before NewLog()")
}
// ✅ Chemin du log à côté de l'exe
exe, _ := os.Executable()
exePath := filepath.Dir(exe)
logPath := filepath.Join(exePath, "flexradio.log")
if Cfg.General.DeleteLogFileAtStart {
if _, err := os.Stat(logPath); err == nil {
os.Remove(logPath)
}
}
logCtx, logCancel = context.WithCancel(context.Background())
var w io.Writer
if Cfg.General.LogToFile {
f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("Cannot open log file %s: %v", logPath, err))
}
logFile = f
logWriter = &syncWriter{file: f}
// ✅ IMPORTANT: Vérifier si Stdout est disponible (mode console vs GUI)
if isConsoleAvailable() {
// Mode console : log vers fichier ET console
w = io.MultiWriter(os.Stdout, logWriter)
} else {
// Mode GUI (windowsgui) : log SEULEMENT vers fichier
w = logWriter
}
} else {
// Log uniquement vers console (si disponible)
if isConsoleAvailable() {
w = os.Stdout
} else {
// Pas de console, pas de log fichier -> log vers null
w = io.Discard
}
}
Log = &log.Logger{
Out: w,
Formatter: &prefixed.TextFormatter{
DisableColors: !isConsoleAvailable(),
TimestampFormat: "02-01-2006 15:04:05",
FullTimestamp: true,
ForceFormatting: true,
DisableSorting: true, // ✅ Ajoute
QuoteEmptyFields: true, // ✅ Ajoute
SpacePadding: 0, // ✅ Ajoute (pas d'espace)
},
Hooks: make(log.LevelHooks),
}
if Cfg.General.LogLevel == "DEBUG" {
Log.Level = log.DebugLevel
} else if Cfg.General.LogLevel == "INFO" {
Log.Level = log.InfoLevel
} else if Cfg.General.LogLevel == "WARN" {
Log.Level = log.WarnLevel
} else {
Log.Level = log.InfoLevel
}
logBuffer = NewLogBuffer(500) // Garde les 500 derniers logs
// Log.AddHook(&LogHook{buffer: logBuffer})
// ✅ Premier vrai log
Log.Infof("Logger initialized - Level: %s, ToFile: %v, LogPath: %s",
Cfg.General.LogLevel, Cfg.General.LogToFile, logPath)
return Log
}
func InitLogHook() {
if logBuffer == nil {
logBuffer = NewLogBuffer(500)
}
Log.AddHook(&LogHook{buffer: logBuffer})
Log.Info("Log hook initialized and broadcasting enabled")
}
// ✅ Détecter si on a une console (fonctionne sur Windows)
func isConsoleAvailable() bool {
// Si Stdout est nil ou invalide, on n'a pas de console
stat, err := os.Stdout.Stat()
if err != nil {
return false
}
// Si c'est un char device, on a une console
return (stat.Mode() & os.ModeCharDevice) != 0
}
// ✅ Fonction pour fermer proprement le log
func CloseLog() {
if Log != nil {
Log.Info("Closing log file...")
}
if logCancel != nil {
logCancel()
}
time.Sleep(200 * time.Millisecond) // Donne le temps d'écrire
if logWriter != nil {
logWriter.mutex.Lock()
if logFile != nil {
logFile.Sync()
logFile.Close()
logFile = nil
}
logWriter.mutex.Unlock()
}
}