mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Added a new log viewer component to display real-time logs. - Integrated WebSocket support for broadcasting log entries to connected clients. - Updated main application to initialize logging and handle log directory creation. - Enhanced error handling for log loading and broadcasting. - Introduced new routes for accessing the log viewer and WebSocket stream.
235 lines
6.3 KiB
Go
235 lines
6.3 KiB
Go
package scheduler
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
// LogLevel represents the verbosity level of logging
|
|
type LogLevel int
|
|
|
|
const (
|
|
// LogLevelError only logs errors
|
|
LogLevelError LogLevel = iota
|
|
// LogLevelInfo logs info and errors
|
|
LogLevelInfo
|
|
// LogLevelDebug logs everything including debug messages
|
|
LogLevelDebug
|
|
)
|
|
|
|
// BroadcastFunc is a function type that can be used to broadcast logs
|
|
type BroadcastFunc func(level, message, source string)
|
|
|
|
// String returns the string representation of a log level
|
|
func (l LogLevel) String() string {
|
|
switch l {
|
|
case LogLevelError:
|
|
return "error"
|
|
case LogLevelInfo:
|
|
return "info"
|
|
case LogLevelDebug:
|
|
return "debug"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ParseLogLevel parses a string into a LogLevel
|
|
func ParseLogLevel(level string) LogLevel {
|
|
switch strings.ToLower(level) {
|
|
case "error":
|
|
return LogLevelError
|
|
case "info":
|
|
return LogLevelInfo
|
|
case "debug":
|
|
return LogLevelDebug
|
|
default:
|
|
return LogLevelInfo // Default to info level
|
|
}
|
|
}
|
|
|
|
// Logger handles log output to file and console
|
|
type Logger struct {
|
|
Info *log.Logger
|
|
Error *log.Logger
|
|
Debug *log.Logger
|
|
file *lumberjack.Logger
|
|
logLevel LogLevel
|
|
useBroadcast bool
|
|
broadcastFn BroadcastFunc
|
|
}
|
|
|
|
// SetBroadcastFunc sets the function to use for broadcasting logs
|
|
func (l *Logger) SetBroadcastFunc(fn BroadcastFunc) {
|
|
l.broadcastFn = fn
|
|
l.useBroadcast = fn != nil
|
|
|
|
// Log the setting of the broadcast function to help with troubleshooting
|
|
if fn != nil {
|
|
fmt.Fprintf(os.Stderr, "[SCHEDULER-LOGGER] Broadcast function set successfully, logs will be streamed to WebSocket clients\n")
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "[SCHEDULER-LOGGER] Broadcast function cleared or set to nil\n")
|
|
}
|
|
}
|
|
|
|
// LogInfo logs an info message if the log level allows it
|
|
func (l *Logger) LogInfo(format string, v ...interface{}) {
|
|
if l.logLevel >= LogLevelInfo {
|
|
msg := fmt.Sprintf(format, v...)
|
|
l.Info.Println(msg)
|
|
|
|
// If broadcasting is enabled, call the broadcast function
|
|
if l.useBroadcast && l.broadcastFn != nil {
|
|
// Add debug output
|
|
fmt.Fprintf(os.Stderr, "[SCHEDULER-LOGGER-BROADCAST] INFO: %s\n", msg)
|
|
l.broadcastFn("info", msg, "scheduler")
|
|
}
|
|
}
|
|
}
|
|
|
|
// LogError logs an error message if the log level allows it
|
|
func (l *Logger) LogError(format string, v ...interface{}) {
|
|
if l.logLevel >= LogLevelError {
|
|
msg := fmt.Sprintf(format, v...)
|
|
l.Error.Println(msg)
|
|
|
|
// If broadcasting is enabled, call the broadcast function
|
|
if l.useBroadcast && l.broadcastFn != nil {
|
|
// Add debug output
|
|
fmt.Fprintf(os.Stderr, "[SCHEDULER-LOGGER-BROADCAST] ERROR: %s\n", msg)
|
|
l.broadcastFn("error", msg, "scheduler")
|
|
}
|
|
}
|
|
}
|
|
|
|
// LogDebug logs a debug message if the log level allows it
|
|
func (l *Logger) LogDebug(format string, v ...interface{}) {
|
|
if l.logLevel >= LogLevelDebug {
|
|
msg := fmt.Sprintf(format, v...)
|
|
l.Debug.Println(msg)
|
|
|
|
// If broadcasting is enabled, call the broadcast function
|
|
if l.useBroadcast && l.broadcastFn != nil {
|
|
// Add debug output
|
|
fmt.Fprintf(os.Stderr, "[SCHEDULER-LOGGER-BROADCAST] DEBUG: %s\n", msg)
|
|
l.broadcastFn("debug", msg, "scheduler")
|
|
}
|
|
}
|
|
}
|
|
|
|
// NewLogger creates a new logger that writes to both file and console
|
|
func NewLogger() *Logger {
|
|
// Get data directory from environment or use default
|
|
dataDir := os.Getenv("DATA_DIR")
|
|
if dataDir == "" {
|
|
dataDir = "./data"
|
|
}
|
|
|
|
// Ensure logs directory exists
|
|
logsDir := filepath.Join(dataDir, "logs")
|
|
if envLogsDir := os.Getenv("LOGS_DIR"); envLogsDir != "" {
|
|
logsDir = envLogsDir
|
|
}
|
|
|
|
if err := os.MkdirAll(logsDir, 0755); err != nil {
|
|
fmt.Printf("Error creating logs directory: %v\n", err)
|
|
}
|
|
|
|
// Get log rotation settings from environment or use defaults
|
|
maxSize := 10 // Default: 10MB
|
|
if envSize := os.Getenv("LOG_MAX_SIZE"); envSize != "" {
|
|
if size, err := strconv.Atoi(envSize); err == nil && size > 0 {
|
|
maxSize = size
|
|
}
|
|
}
|
|
|
|
maxBackups := 5 // Default: keep 5 backups
|
|
if envBackups := os.Getenv("LOG_MAX_BACKUPS"); envBackups != "" {
|
|
if backups, err := strconv.Atoi(envBackups); err == nil && backups >= 0 {
|
|
maxBackups = backups
|
|
}
|
|
}
|
|
|
|
maxAge := 30 // Default: 30 days
|
|
if envAge := os.Getenv("LOG_MAX_AGE"); envAge != "" {
|
|
if age, err := strconv.Atoi(envAge); err == nil && age >= 0 {
|
|
maxAge = age
|
|
}
|
|
}
|
|
|
|
compress := true // Default: compress logs
|
|
if envCompress := os.Getenv("LOG_COMPRESS"); envCompress == "false" {
|
|
compress = false
|
|
}
|
|
|
|
// Get log level from environment or use default
|
|
logLevel := LogLevelInfo // Default to info level
|
|
if envLogLevel := os.Getenv("LOG_LEVEL"); envLogLevel != "" {
|
|
logLevel = ParseLogLevel(envLogLevel)
|
|
}
|
|
|
|
// Check if we should enable WebSocket broadcasting
|
|
useBroadcast := true
|
|
if envBroadcast := os.Getenv("LOG_BROADCAST"); envBroadcast == "false" {
|
|
useBroadcast = false
|
|
}
|
|
|
|
// Setup log rotation
|
|
logFile := &lumberjack.Logger{
|
|
Filename: filepath.Join(logsDir, "scheduler.log"),
|
|
MaxSize: maxSize,
|
|
MaxBackups: maxBackups,
|
|
MaxAge: maxAge,
|
|
Compress: compress,
|
|
}
|
|
|
|
// Create multi-writer for both file and console
|
|
consoleAndFile := io.MultiWriter(os.Stdout, logFile)
|
|
|
|
// Create loggers with different prefixes
|
|
logger := &Logger{
|
|
Info: log.New(consoleAndFile, "INFO: ", log.Ldate|log.Ltime),
|
|
Error: log.New(consoleAndFile, "ERROR: ", log.Ldate|log.Ltime),
|
|
Debug: log.New(consoleAndFile, "DEBUG: ", log.Ldate|log.Ltime),
|
|
file: logFile,
|
|
logLevel: logLevel,
|
|
useBroadcast: useBroadcast,
|
|
broadcastFn: nil, // Will be set later
|
|
}
|
|
|
|
// Log rotation settings and log level
|
|
if logLevel >= LogLevelInfo {
|
|
logger.Info.Printf("Log rotation configured: file=%s, maxSize=%dMB, maxBackups=%d, maxAge=%d days, compress=%v, logLevel=%s, useBroadcast=%v",
|
|
filepath.Join(logsDir, "scheduler.log"), maxSize, maxBackups, maxAge, compress, logLevel.String(), useBroadcast)
|
|
}
|
|
|
|
if logLevel >= LogLevelDebug {
|
|
logger.Debug.Printf("Log rotation details: file=%s, maxSize=%dMB, maxBackups=%d, maxAge=%d days, compress=%v",
|
|
filepath.Join(logsDir, "scheduler.log"), maxSize, maxBackups, maxAge, compress)
|
|
}
|
|
|
|
return logger
|
|
}
|
|
|
|
// Close closes the log file
|
|
func (l *Logger) Close() {
|
|
if l.file != nil {
|
|
l.file.Close()
|
|
}
|
|
}
|
|
|
|
// RotateLogs manually triggers log rotation
|
|
func (l *Logger) RotateLogs() error {
|
|
if l.file != nil {
|
|
return l.file.Rotate()
|
|
}
|
|
return nil
|
|
}
|