sync/utils/logger.go
2025-05-20 13:01:02 -04:00

116 lines
3.2 KiB
Go

package utils
import (
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/wailsapp/wails/v2/pkg/logger"
)
var (
baseTargets []logger.Logger
once sync.Once
initErr error
)
// InstancedLogger prefixes each message with its instance-specific prefix and a timestamp,
// while writing to shared log outputs.
type InstancedLogger struct {
prefix string
targets []logger.Logger
}
// NewLogger returns a logger instance that writes to the shared logs
//
// but prefixes every message with [prefix].
func NewLogger(prefix string) (*InstancedLogger, error) {
once.Do(func() {
baseTargets, initErr = createBaseTargets()
})
if initErr != nil {
return nil, initErr
}
return &InstancedLogger{prefix: prefix, targets: baseTargets}, nil
}
// createBaseTargets initializes the shared log file and console loggers once.
func createBaseTargets() ([]logger.Logger, error) {
cfgDir, err := os.UserConfigDir()
if err != nil {
return nil, fmt.Errorf("cannot determine user config dir: %w", err)
}
logDir := filepath.Join(cfgDir, "FanslySync", "logs")
if err := os.MkdirAll(logDir, 0o755); err != nil {
return nil, fmt.Errorf("cannot create log directory: %w", err)
}
// Prune logs older than 14 days
cutoff := time.Now().Add(-14 * 24 * time.Hour)
entries, _ := os.ReadDir(logDir)
for _, e := range entries {
if e.IsDir() || e.Name() == "runtime_latest.log" {
continue
}
info, err := e.Info()
if err != nil {
continue
}
if info.ModTime().Before(cutoff) {
_ = os.Remove(filepath.Join(logDir, e.Name()))
}
}
// Remove old runtime_latest.log if it exists
runtimePath := filepath.Join(logDir, "runtime_latest.log")
if _, err := os.Stat(runtimePath); err == nil {
_ = os.Remove(runtimePath)
}
// Prepare file paths
ts := time.Now().Format("2006-01-02_15-04-05")
tsPath := filepath.Join(logDir, fmt.Sprintf("runtime_%s.log", ts))
latestPath := filepath.Join(logDir, "runtime_latest.log")
// Create loggers
tsLogger := logger.NewFileLogger(tsPath)
latestLogger := logger.NewFileLogger(latestPath)
termLogger := logger.NewDefaultLogger()
return []logger.Logger{tsLogger, latestLogger, termLogger}, nil
}
// log dispatches a timestamped, prefixed message to all shared targets.
func (l *InstancedLogger) log(level, message string) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
fullMsg := fmt.Sprintf("%s [%s] %s", timestamp, l.prefix, message)
for _, t := range l.targets {
switch level {
case "Print":
t.Print(fullMsg)
case "Trace":
t.Trace(fullMsg)
case "Debug":
t.Debug(fullMsg)
case "Info":
t.Info(fullMsg)
case "Warning":
t.Warning(fullMsg)
case "Error":
t.Error(fullMsg)
case "Fatal":
t.Fatal(fullMsg)
}
}
}
func (l *InstancedLogger) Print(message string) { l.log("Print", message) }
func (l *InstancedLogger) Trace(message string) { l.log("Trace", message) }
func (l *InstancedLogger) Debug(message string) { l.log("Debug", message) }
func (l *InstancedLogger) Info(message string) { l.log("Info", message) }
func (l *InstancedLogger) Warning(message string) { l.log("Warning", message) }
func (l *InstancedLogger) Error(message string) { l.log("Error", message) }
func (l *InstancedLogger) Fatal(message string) { l.log("Fatal", message) }