182 lines
4.3 KiB
Go
182 lines
4.3 KiB
Go
package logger
|
||
|
||
import (
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
type Retention struct {
|
||
maxAgeHours int
|
||
maxSizeBytes int64
|
||
eventLogger *EventLogger
|
||
}
|
||
|
||
func NewRetention(maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
|
||
return &Retention{
|
||
maxAgeHours: maxAgeHours,
|
||
maxSizeBytes: int64(maxSizeMB) * 1024 * 1024,
|
||
eventLogger: eventLogger,
|
||
}
|
||
}
|
||
|
||
func (r *Retention) Start() {
|
||
// Запускаем проверку каждые 15 минут
|
||
ticker := time.NewTicker(15 * time.Minute)
|
||
go func() {
|
||
for range ticker.C {
|
||
r.Cleanup()
|
||
}
|
||
}()
|
||
|
||
// Первая очистка через 1 минуту после запуска
|
||
time.AfterFunc(1*time.Minute, func() {
|
||
r.Cleanup()
|
||
})
|
||
}
|
||
|
||
func (r *Retention) Cleanup() {
|
||
// Получаем директорию с данными
|
||
dataDir, err := GetDataLogsDir()
|
||
if err != nil {
|
||
if r.eventLogger != nil {
|
||
r.eventLogger.Event("ОШИБКА_ПОЛУЧЕНИЯ_ДИРЕКТОРИИ")
|
||
}
|
||
return
|
||
}
|
||
|
||
if r.eventLogger != nil {
|
||
r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКА_ЛОГОВ")
|
||
}
|
||
|
||
// 1. Удаляем старые файлы
|
||
deletedByAge := r.cleanByAge(dataDir)
|
||
|
||
// 2. Проверяем общий размер и удаляем самые старые если превышен лимит
|
||
deletedBySize := r.cleanBySize(dataDir)
|
||
|
||
if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) {
|
||
r.eventLogger.Event("УДАЛЕНО_ФАЙЛОВ")
|
||
log.Printf("Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)",
|
||
deletedByAge+deletedBySize, deletedByAge, deletedBySize)
|
||
}
|
||
}
|
||
|
||
func (r *Retention) cleanByAge(dataDir string) int {
|
||
cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
|
||
deleted := 0
|
||
|
||
// Получаем все .bin файлы
|
||
files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
|
||
if err != nil {
|
||
return 0
|
||
}
|
||
|
||
for _, file := range files {
|
||
info, err := os.Stat(file)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// Извлекаем время из имени файла
|
||
fileTime := r.parseFilenameTime(file)
|
||
if fileTime.IsZero() {
|
||
fileTime = info.ModTime()
|
||
}
|
||
|
||
if fileTime.Before(cutoff) {
|
||
if err := os.Remove(file); err == nil {
|
||
deleted++
|
||
if r.eventLogger != nil {
|
||
r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_ВОЗРАСТУ")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return deleted
|
||
}
|
||
|
||
func (r *Retention) cleanBySize(dataDir string) int {
|
||
// Получаем все файлы
|
||
files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
|
||
if err != nil {
|
||
return 0
|
||
}
|
||
|
||
if len(files) == 0 {
|
||
return 0
|
||
}
|
||
|
||
// Сортируем по времени (старыми первыми)
|
||
type fileInfo struct {
|
||
path string
|
||
time time.Time
|
||
size int64
|
||
}
|
||
|
||
var fileList []fileInfo
|
||
var totalSize int64
|
||
|
||
for _, file := range files {
|
||
info, err := os.Stat(file)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
fileTime := r.parseFilenameTime(file)
|
||
if fileTime.IsZero() {
|
||
fileTime = info.ModTime()
|
||
}
|
||
|
||
fileList = append(fileList, fileInfo{
|
||
path: file,
|
||
time: fileTime,
|
||
size: info.Size(),
|
||
})
|
||
totalSize += info.Size()
|
||
}
|
||
|
||
// Сортируем по времени (старые первые)
|
||
sort.Slice(fileList, func(i, j int) bool {
|
||
return fileList[i].time.Before(fileList[j].time)
|
||
})
|
||
|
||
// Удаляем самые старые пока не уложимся в лимит
|
||
deleted := 0
|
||
for totalSize > r.maxSizeBytes && len(fileList) > 1 {
|
||
// Удаляем самый старый файл
|
||
oldest := fileList[0]
|
||
if err := os.Remove(oldest.path); err == nil {
|
||
totalSize -= oldest.size
|
||
deleted++
|
||
fileList = fileList[1:]
|
||
if r.eventLogger != nil {
|
||
r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_РАЗМЕРУ")
|
||
}
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
|
||
return deleted
|
||
}
|
||
|
||
func (r *Retention) parseFilenameTime(filename string) time.Time {
|
||
base := filepath.Base(filename)
|
||
// Формат: gpio-2026-06-09-11.bin
|
||
parts := strings.Split(strings.TrimSuffix(base, ".bin"), "-")
|
||
if len(parts) != 5 {
|
||
return time.Time{}
|
||
}
|
||
|
||
year, _ := time.Parse("2006", parts[1])
|
||
month, _ := time.Parse("01", parts[2])
|
||
day, _ := time.Parse("02", parts[3])
|
||
hour, _ := time.Parse("15", parts[4])
|
||
|
||
return time.Date(year.Year(), month.Month(), day.Day(), hour.Hour(), 0, 0, 0, time.Local)
|
||
} |