backea/internal/logging/logging.go
2025-03-28 19:57:44 +01:00

148 lines
2.8 KiB
Go

// internal/logging/logging.go
package logging
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"runtime"
"sync"
)
var (
// Global logger instance
logger *Logger
once sync.Once
)
// LogLevel represents the severity of a log message
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
FATAL
)
// Logger provides logging functionality
type Logger struct {
logLevel LogLevel
output io.Writer
stdLog *log.Logger
errLog *log.Logger
mu sync.Mutex
}
// Singleton
func InitLogger(level LogLevel, logFile string) {
once.Do(func() {
var output io.Writer = os.Stdout
if logFile != "" {
logDir := filepath.Dir(logFile)
if err := os.MkdirAll(logDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create log directory: %v\n", err)
os.Exit(1)
}
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open log file: %v\n", err)
os.Exit(1)
}
output = io.MultiWriter(os.Stdout, file)
}
logger = &Logger{
logLevel: level,
output: output,
stdLog: log.New(output, "", log.LstdFlags),
errLog: log.New(output, "ERROR: ", log.LstdFlags),
}
})
}
// Singleton
func GetLogger() *Logger {
if logger == nil {
InitLogger(INFO, "")
}
return logger
}
func (l *Logger) SetLogLevel(level LogLevel) {
l.mu.Lock()
defer l.mu.Unlock()
l.logLevel = level
}
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
if level < l.logLevel {
return
}
l.mu.Lock()
defer l.mu.Unlock()
// Get caller information
_, file, line, ok := runtime.Caller(2)
if !ok {
file = "unknown"
line = 0
}
// Format message with source location
message := fmt.Sprintf("%s:%d: %s", filepath.Base(file), line, fmt.Sprintf(format, v...))
if level >= ERROR {
l.errLog.Println(message)
} else {
l.stdLog.Println(message)
}
}
// Debug logs a debug message
func (l *Logger) Debug(format string, v ...interface{}) {
l.log(DEBUG, format, v...)
}
// Info logs an info message
func (l *Logger) Info(format string, v ...interface{}) {
l.log(INFO, format, v...)
}
// Warn logs a warning message
func (l *Logger) Warn(format string, v ...interface{}) {
l.log(WARN, format, v...)
}
// Error logs an error message
func (l *Logger) Error(format string, v ...interface{}) {
l.log(ERROR, format, v...)
}
// Fatal logs a fatal message and exits the program
func (l *Logger) Fatal(format string, v ...interface{}) {
l.log(FATAL, format, v...)
os.Exit(1)
}
// ErrorWithStack logs an error with its stack trace
func (l *Logger) ErrorWithStack(err error, format string, v ...interface{}) {
if err == nil {
return
}
message := fmt.Sprintf(format, v...)
buf := make([]byte, 1024)
n := runtime.Stack(buf, false)
l.Error("%s: %v\n%s", message, err, buf[:n])
}