залил

This commit is contained in:
2026-04-15 08:00:15 +03:00
commit 5549b3545e
51 changed files with 8073 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
package logger
import (
"fmt"
"log"
"os"
"sync"
"time"
)
type LogLevel string
const (
LevelInfo LogLevel = "INFO"
LevelWarn LogLevel = "WARN"
LevelError LogLevel = "ERROR"
LevelFatal LogLevel = "FATAL"
LevelDebug LogLevel = "DEBUG"
)
type LogEntry struct {
Time time.Time `json:"time"`
Level LogLevel `json:"level"`
Message string `json:"message"`
}
var (
mu sync.Mutex
// Кольцевой буфер последних записей (максимум 1000)
buffer []LogEntry
bufferIdx int
bufferCap = 1000
// Канал для подписчиков (один на всех, но можно расширить)
subscribers []chan LogEntry
subMu sync.RWMutex
)
func Init(filename string) error {
buffer = make([]LogEntry, bufferCap)
bufferIdx = 0
return nil
}
// Добавляет запись в буфер и рассылает подписчикам
func addEntry(level LogLevel, format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
entry := LogEntry{
Time: time.Now(),
Level: level,
Message: msg,
}
// Сохраняем в буфер
mu.Lock()
buffer[bufferIdx] = entry
bufferIdx = (bufferIdx + 1) % bufferCap
mu.Unlock()
// Отправляем подписчикам (асинхронно, чтобы не блокировать)
subMu.RLock()
for _, ch := range subscribers {
select {
case ch <- entry:
default:
// Если канал заполнен, пропускаем (чтобы не тормозить)
}
}
subMu.RUnlock()
// Вывод в консоль
log.Printf("[%s] %s", level, msg)
if level == LevelFatal {
os.Exit(1)
}
}
func Info(format string, v ...interface{}) {
addEntry(LevelInfo, format, v...)
}
func Warn(format string, v ...interface{}) {
addEntry(LevelWarn, format, v...)
}
func Error(format string, v ...interface{}) {
addEntry(LevelError, format, v...)
}
func Fatal(format string, v ...interface{}) {
addEntry(LevelFatal, format, v...)
}
// GetRecent возвращает последние N записей (в хронологическом порядке)
func GetRecent(limit int) []LogEntry {
if limit > bufferCap {
limit = bufferCap
}
mu.Lock()
defer mu.Unlock()
result := make([]LogEntry, 0, limit)
// Буфер заполнен циклически, начинаем с bufferIdx-1 и идём назад
start := bufferIdx - 1
if start < 0 {
start = bufferCap - 1
}
for i := 0; i < limit; i++ {
idx := (start - i + bufferCap) % bufferCap
if buffer[idx].Time.IsZero() {
break
}
result = append([]LogEntry{buffer[idx]}, result...)
}
return result
}
// Subscribe возвращает канал для получения новых записей (буферизированный)
func Subscribe() <-chan LogEntry {
ch := make(chan LogEntry, 100)
subMu.Lock()
subscribers = append(subscribers, ch)
subMu.Unlock()
return ch
}
// Unsubscribe удаляет канал из списка подписчиков
func Unsubscribe(ch <-chan LogEntry) {
subMu.Lock()
defer subMu.Unlock()
for i, c := range subscribers {
if c == ch {
subscribers = append(subscribers[:i], subscribers[i+1:]...)
close(c)
break
}
}
}
func Debug(format string, v ...interface{}) {
addEntry(LevelDebug, format, v...)
}
// GetAll возвращает все имеющиеся записи в порядке от старых к новым.
func GetAll() []LogEntry {
mu.Lock()
defer mu.Unlock()
result := make([]LogEntry, 0, bufferCap)
// Начинаем с самого старого: индекс (bufferIdx) - это место, куда будет записана следующая запись.
// Самый старый элемент находится в bufferIdx, если буфер заполнен, иначе в 0.
start := 0
if buffer[bufferCap-1].Time.IsZero() {
// Буфер не полностью заполнен, начинаем с 0
start = 0
} else {
// Буфер заполнен, начинаем с bufferIdx (следующая позиция записи — это самое старое)
start = bufferIdx
}
for i := 0; i < bufferCap; i++ {
idx := (start + i) % bufferCap
if buffer[idx].Time.IsZero() {
continue
}
result = append(result, buffer[idx])
}
return result
}