224 lines
4.6 KiB
Go
224 lines
4.6 KiB
Go
package logger
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
// Level represents log severity
|
|
type Level int
|
|
|
|
const (
|
|
LevelDebug Level = iota
|
|
LevelInfo
|
|
LevelWarn
|
|
LevelError
|
|
)
|
|
|
|
var levelNames = map[Level]string{
|
|
LevelDebug: "DEBUG",
|
|
LevelInfo: "INFO",
|
|
LevelWarn: "WARN",
|
|
LevelError: "ERROR",
|
|
}
|
|
|
|
// ParseLevel parses a level string into a Level
|
|
func ParseLevel(s string) Level {
|
|
switch s {
|
|
case "debug", "DEBUG":
|
|
return LevelDebug
|
|
case "info", "INFO":
|
|
return LevelInfo
|
|
case "warn", "WARN", "warning", "WARNING":
|
|
return LevelWarn
|
|
case "error", "ERROR":
|
|
return LevelError
|
|
default:
|
|
return LevelInfo
|
|
}
|
|
}
|
|
|
|
var (
|
|
defaultLogger *Logger
|
|
once sync.Once
|
|
currentLevel Level = LevelInfo
|
|
)
|
|
|
|
// Logger wraps the standard log.Logger with optional file output and levels
|
|
type Logger struct {
|
|
*log.Logger
|
|
fileWriter io.WriteCloser
|
|
}
|
|
|
|
// SetLevel sets the global log level
|
|
func SetLevel(level Level) {
|
|
currentLevel = level
|
|
}
|
|
|
|
// GetLevel returns the current log level
|
|
func GetLevel() Level {
|
|
return currentLevel
|
|
}
|
|
|
|
// InitStdout initializes the logger to only write to stdout
|
|
func InitStdout() {
|
|
once.Do(func() {
|
|
log.SetOutput(os.Stdout)
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
defaultLogger = &Logger{
|
|
Logger: log.Default(),
|
|
fileWriter: nil,
|
|
}
|
|
})
|
|
}
|
|
|
|
// InitWithFile initializes the logger with both file and stdout output
|
|
// The file is truncated on each start
|
|
func InitWithFile(logPath string) error {
|
|
var err error
|
|
once.Do(func() {
|
|
defaultLogger, err = NewWithFile(logPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
// Replace standard log output with the multi-writer
|
|
multiWriter := io.MultiWriter(os.Stdout, defaultLogger.fileWriter)
|
|
log.SetOutput(multiWriter)
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
})
|
|
return err
|
|
}
|
|
|
|
// NewWithFile creates a new logger that writes to both stdout and a log file
|
|
// The file is truncated on each start
|
|
func NewWithFile(logPath string) (*Logger, error) {
|
|
// Ensure log directory exists
|
|
logDir := filepath.Dir(logPath)
|
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create/truncate the log file
|
|
fileWriter, err := os.Create(logPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create multi-writer that writes to both stdout and file
|
|
multiWriter := io.MultiWriter(os.Stdout, fileWriter)
|
|
|
|
// Create logger with standard flags
|
|
logger := log.New(multiWriter, "", log.LstdFlags|log.Lshortfile)
|
|
|
|
return &Logger{
|
|
Logger: logger,
|
|
fileWriter: fileWriter,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the file writer
|
|
func (l *Logger) Close() error {
|
|
if l.fileWriter != nil {
|
|
return l.fileWriter.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetDefault returns the default logger instance
|
|
func GetDefault() *Logger {
|
|
return defaultLogger
|
|
}
|
|
|
|
// logf logs a formatted message at the given level
|
|
func logf(level Level, format string, v ...interface{}) {
|
|
if level < currentLevel {
|
|
return
|
|
}
|
|
prefix := fmt.Sprintf("[%s] ", levelNames[level])
|
|
msg := fmt.Sprintf(format, v...)
|
|
log.Print(prefix + msg)
|
|
}
|
|
|
|
// logln logs a message at the given level
|
|
func logln(level Level, v ...interface{}) {
|
|
if level < currentLevel {
|
|
return
|
|
}
|
|
prefix := fmt.Sprintf("[%s] ", levelNames[level])
|
|
msg := fmt.Sprint(v...)
|
|
log.Print(prefix + msg)
|
|
}
|
|
|
|
// Debug logs a debug message
|
|
func Debug(v ...interface{}) {
|
|
logln(LevelDebug, v...)
|
|
}
|
|
|
|
// Debugf logs a formatted debug message
|
|
func Debugf(format string, v ...interface{}) {
|
|
logf(LevelDebug, format, v...)
|
|
}
|
|
|
|
// Info logs an info message
|
|
func Info(v ...interface{}) {
|
|
logln(LevelInfo, v...)
|
|
}
|
|
|
|
// Infof logs a formatted info message
|
|
func Infof(format string, v ...interface{}) {
|
|
logf(LevelInfo, format, v...)
|
|
}
|
|
|
|
// Warn logs a warning message
|
|
func Warn(v ...interface{}) {
|
|
logln(LevelWarn, v...)
|
|
}
|
|
|
|
// Warnf logs a formatted warning message
|
|
func Warnf(format string, v ...interface{}) {
|
|
logf(LevelWarn, format, v...)
|
|
}
|
|
|
|
// Error logs an error message
|
|
func Error(v ...interface{}) {
|
|
logln(LevelError, v...)
|
|
}
|
|
|
|
// Errorf logs a formatted error message
|
|
func Errorf(format string, v ...interface{}) {
|
|
logf(LevelError, format, v...)
|
|
}
|
|
|
|
// Fatal logs an error message and exits
|
|
func Fatal(v ...interface{}) {
|
|
logln(LevelError, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalf logs a formatted error message and exits
|
|
func Fatalf(format string, v ...interface{}) {
|
|
logf(LevelError, format, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// --- Backwards compatibility (maps to Info level) ---
|
|
|
|
// Printf logs a formatted message at Info level
|
|
func Printf(format string, v ...interface{}) {
|
|
logf(LevelInfo, format, v...)
|
|
}
|
|
|
|
// Print logs a message at Info level
|
|
func Print(v ...interface{}) {
|
|
logln(LevelInfo, v...)
|
|
}
|
|
|
|
// Println logs a message at Info level
|
|
func Println(v ...interface{}) {
|
|
logln(LevelInfo, v...)
|
|
}
|