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

210 lines
4.9 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 {
maxAgeHours int
maxSizeBytes int64
eventLogger *EventLogger
stopCh chan struct{}
}
func NewRetention(maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
return &Retention{
maxAgeHours: maxAgeHours,
maxSizeBytes: int64(maxSizeMB) * 1024 * 1024,
eventLogger: eventLogger,
stopCh: make(chan struct{}),
}
}
func (r *Retention) Start() {
// Запускаем проверку каждые 15 минут
r.StartWithInterval(15 * time.Minute)
}
func (r *Retention) StartWithInterval(interval time.Duration) {
// Запускаем проверку с указанным интервалом
ticker := time.NewTicker(interval)
go func() {
for {
select {
case <-ticker.C:
r.Cleanup()
case <-r.stopCh:
ticker.Stop()
return
}
}
}()
// Первая очистка через 1 минуту после запуска
time.AfterFunc(1*time.Minute, func() {
r.Cleanup()
})
}
func (r *Retention) Stop() {
close(r.stopCh)
}
func (r *Retention) Cleanup() {
// Если оба лимита отключены, ничего не делаем
if r.maxAgeHours <= 0 && r.maxSizeBytes <= 0 {
return
}
// Получаем директорию с данными
dataDir, err := GetDataLogsDir()
if err != nil {
if r.eventLogger != nil {
r.eventLogger.Event("ОШИБКАОЛУЧЕНИЯ_ДИРЕКТОРИИ")
}
return
}
if r.eventLogger != nil {
r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКАОГОВ")
}
// 1. Удаляем старые файлы (если включено)
deletedByAge := 0
if r.maxAgeHours > 0 {
deletedByAge = r.cleanByAge(dataDir)
}
// 2. Проверяем общий размер и удаляем самые старые если превышен лимит (если включено)
deletedBySize := 0
if r.maxSizeBytes > 0 {
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)
}