90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
|
|
// Файл: internal/log/logger.go
|
|||
|
|
// Назначение: Асинхронная, wait-free запись логов в файл с меткой времени
|
|||
|
|
// в миллисекундах. Поддержка уровней логирования и ротации.
|
|||
|
|
|
|||
|
|
package log
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"sync/atomic"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type LogLevel int32
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
DebugLevel LogLevel = iota
|
|||
|
|
InfoLevel
|
|||
|
|
WarnLevel
|
|||
|
|
ErrorLevel
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type Logger struct {
|
|||
|
|
file *os.File
|
|||
|
|
level atomic.Int32
|
|||
|
|
writeChan chan string
|
|||
|
|
done chan struct{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewLogger(filename string, levelStr string) (*Logger, error) {
|
|||
|
|
file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
level := InfoLevel
|
|||
|
|
switch levelStr {
|
|||
|
|
case "debug":
|
|||
|
|
level = DebugLevel
|
|||
|
|
case "warn":
|
|||
|
|
level = WarnLevel
|
|||
|
|
case "error":
|
|||
|
|
level = ErrorLevel
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
l := &Logger{
|
|||
|
|
file: file,
|
|||
|
|
writeChan: make(chan string, 10000),
|
|||
|
|
done: make(chan struct{}),
|
|||
|
|
}
|
|||
|
|
l.level.Store(int32(level))
|
|||
|
|
|
|||
|
|
// Запуск wait-free writer
|
|||
|
|
go l.writerLoop()
|
|||
|
|
|
|||
|
|
return l, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (l *Logger) writerLoop() {
|
|||
|
|
for msg := range l.writeChan {
|
|||
|
|
l.file.WriteString(msg + "\n")
|
|||
|
|
}
|
|||
|
|
close(l.done)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (l *Logger) log(level LogLevel, levelStr, msg string) {
|
|||
|
|
if level < LogLevel(l.level.Load()) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
now := time.Now()
|
|||
|
|
timestamp := now.Format("2006-01-02 15:04:05") + fmt.Sprintf(".%03d", now.Nanosecond()/1e6)
|
|||
|
|
logMsg := fmt.Sprintf("[%s] %s: %s", timestamp, levelStr, msg)
|
|||
|
|
select {
|
|||
|
|
case l.writeChan <- logMsg:
|
|||
|
|
default:
|
|||
|
|
// Неблокирующая запись, старый лог теряется - wait-free
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (l *Logger) Debug(msg string) { l.log(DebugLevel, "DEBUG", msg) }
|
|||
|
|
func (l *Logger) Info(msg string) { l.log(InfoLevel, "INFO", msg) }
|
|||
|
|
func (l *Logger) Warn(msg string) { l.log(WarnLevel, "WARN", msg) }
|
|||
|
|
func (l *Logger) Error(msg string) { l.log(ErrorLevel, "ERROR", msg) }
|
|||
|
|
|
|||
|
|
func (l *Logger) Close() {
|
|||
|
|
close(l.writeChan)
|
|||
|
|
<-l.done
|
|||
|
|
l.file.Close()
|
|||
|
|
}
|