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()
|
||
}
|