Files
futriis/internal/log/logger.go
2026-04-08 21:43:35 +03:00

90 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Файл: 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()
}