Доработка. Защита от переполнения диска

This commit is contained in:
Maxim
2026-06-09 11:29:02 +03:00
parent 676a674794
commit d84856ba3a
2 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,204 @@
package logger
import (
"log"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
type Retention struct {
dataDir string
maxAgeHours int // максимальный возраст файлов в часах
maxSizeBytes int64 // максимальный размер всех файлов в байтах
eventLogger *EventLogger
}
func NewRetention(dataDir string, maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
return &Retention{
dataDir: dataDir,
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() {
if r.eventLogger != nil {
r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКАОГОВ")
}
// 1. Удаляем старые файлы
deletedByAge := r.cleanByAge()
// 2. Проверяем общий размер и удаляем самые старые если превышен лимит
deletedBySize := r.cleanBySize()
if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) {
r.eventLogger.Event("УДАЛЕНОАЙЛОВ")
log.Printf("🧹 Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)",
deletedByAge+deletedBySize, deletedByAge, deletedBySize)
}
}
func (r *Retention) cleanByAge() int {
cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
deleted := 0
// Получаем все .bin файлы
files, err := filepath.Glob(filepath.Join(r.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() int {
// Получаем все файлы
files, err := filepath.Glob(filepath.Join(r.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)
}
// GetAvailableRange возвращает диапазон доступных данных
func (r *Retention) GetAvailableRange() (oldest, newest time.Time, count int) {
files, err := filepath.Glob(filepath.Join(r.dataDir, "gpio-*.bin"))
if err != nil {
return
}
count = len(files)
if count == 0 {
return
}
for _, file := range files {
fileTime := r.parseFilenameTime(file)
if fileTime.IsZero() {
continue
}
if oldest.IsZero() || fileTime.Before(oldest) {
oldest = fileTime
}
if newest.IsZero() || fileTime.After(newest) {
newest = fileTime
}
}
return
}