Files
go-service/internal/logger/retention.go

204 lines
4.8 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.

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
}