150 lines
2.9 KiB
Go
150 lines
2.9 KiB
Go
package logging
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
logger *Logger
|
|
once sync.Once
|
|
timeFormat = "2006-01-02 15:04:05.000"
|
|
)
|
|
|
|
type Logger struct {
|
|
file *os.File
|
|
logger *log.Logger
|
|
}
|
|
|
|
// Initialize creates or gets the singleton logger instance
|
|
func Initialize() (*Logger, error) {
|
|
var err error
|
|
once.Do(func() {
|
|
logger, err = newLogger()
|
|
})
|
|
return logger, err
|
|
}
|
|
|
|
func newLogger() (*Logger, error) {
|
|
// Ensure logs directory exists
|
|
if err := os.MkdirAll("logs", 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create logs directory: %v", err)
|
|
}
|
|
|
|
// Open log file with date in name
|
|
logPath := filepath.Join("logs", fmt.Sprintf("acc-server-%s.log", time.Now().Format("2006-01-02")))
|
|
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open log file: %v", err)
|
|
}
|
|
|
|
// Create multi-writer for both file and console
|
|
multiWriter := io.MultiWriter(file, os.Stdout)
|
|
|
|
// Create logger with custom prefix
|
|
logger := &Logger{
|
|
file: file,
|
|
logger: log.New(multiWriter, "", 0),
|
|
}
|
|
|
|
return logger, nil
|
|
}
|
|
|
|
func (l *Logger) Close() error {
|
|
if l.file != nil {
|
|
return l.file.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *Logger) log(level, format string, v ...interface{}) {
|
|
// Get caller info
|
|
_, file, line, _ := runtime.Caller(2)
|
|
file = filepath.Base(file)
|
|
|
|
// Format message
|
|
msg := fmt.Sprintf(format, v...)
|
|
|
|
// Format final log line
|
|
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s",
|
|
time.Now().Format(timeFormat),
|
|
level,
|
|
file,
|
|
line,
|
|
msg,
|
|
)
|
|
|
|
l.logger.Println(logLine)
|
|
}
|
|
|
|
func (l *Logger) Info(format string, v ...interface{}) {
|
|
l.log("INFO", format, v...)
|
|
}
|
|
|
|
func (l *Logger) Error(format string, v ...interface{}) {
|
|
l.log("ERROR", format, v...)
|
|
}
|
|
|
|
func (l *Logger) Warn(format string, v ...interface{}) {
|
|
l.log("WARN", format, v...)
|
|
}
|
|
|
|
func (l *Logger) Debug(format string, v ...interface{}) {
|
|
l.log("DEBUG", format, v...)
|
|
}
|
|
|
|
func (l *Logger) Panic(format string) {
|
|
l.Panic("PANIC " + format)
|
|
}
|
|
|
|
// Global convenience functions
|
|
func Info(format string, v ...interface{}) {
|
|
if logger != nil {
|
|
logger.Info(format, v...)
|
|
}
|
|
}
|
|
|
|
func Error(format string, v ...interface{}) {
|
|
if logger != nil {
|
|
logger.Error(format, v...)
|
|
}
|
|
}
|
|
|
|
func Warn(format string, v ...interface{}) {
|
|
if logger != nil {
|
|
logger.Warn(format, v...)
|
|
}
|
|
}
|
|
|
|
func Debug(format string, v ...interface{}) {
|
|
if logger != nil {
|
|
logger.Debug(format, v...)
|
|
}
|
|
}
|
|
|
|
func Panic(format string) {
|
|
if logger != nil {
|
|
logger.Panic(format)
|
|
}
|
|
}
|
|
|
|
// RecoverAndLog recovers from panics and logs them
|
|
func RecoverAndLog() {
|
|
if logger != nil {
|
|
logger.Info("Recovering from panic")
|
|
if r := recover(); r != nil {
|
|
// Get stack trace
|
|
buf := make([]byte, 4096)
|
|
n := runtime.Stack(buf, false)
|
|
stackTrace := string(buf[:n])
|
|
|
|
logger.log("PANIC", "Recovered from panic: %v\nStack Trace:\n%s", r, stackTrace)
|
|
}
|
|
}
|
|
} |