diff --git a/internal/logger/retention.go b/internal/logger/retention.go index badd7d3..e80d82e 100644 --- a/internal/logger/retention.go +++ b/internal/logger/retention.go @@ -1,6 +1,7 @@ package logger import ( + "fmt" "log" "os" "path/filepath" @@ -70,37 +71,48 @@ func (r *Retention) Cleanup() { return } - if r.eventLogger != nil { - r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКА_ЛОГОВ") - } - // 1. Удаляем старые файлы (если включено) deletedByAge := 0 + var deletedByAgeBytes int64 if r.maxAgeHours > 0 { - deletedByAge = r.cleanByAge(dataDir) + deletedByAge, deletedByAgeBytes = r.cleanByAge(dataDir) } // 2. Проверяем общий размер и удаляем самые старые если превышен лимит (если включено) deletedBySize := 0 + var deletedBySizeBytes int64 if r.maxSizeBytes > 0 { - deletedBySize = r.cleanBySize(dataDir) + deletedBySize, deletedBySizeBytes = r.cleanBySize(dataDir) } - if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) { - r.eventLogger.Event("УДАЛЕНО_ФАЙЛОВ") - log.Printf("Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)", - deletedByAge+deletedBySize, deletedByAge, deletedBySize) + totalDeleted := deletedByAge + deletedBySize + totalDeletedBytes := deletedByAgeBytes + deletedBySizeBytes + + // Логируем ТОЛЬКО если были удаления + if totalDeleted > 0 { + // Логируем суммарное событие + if r.eventLogger != nil { + eventMsg := fmt.Sprintf("ОЧИСТКА_ЛОГОВ_УДАЛЕНО_%d_ФАЙЛОВ_%d_MB", + totalDeleted, totalDeletedBytes/(1024*1024)) + r.eventLogger.Event(eventMsg) + } + + // Пишем в консоль для отладки + log.Printf("Очистка логов: удалено %d файлов (%.2f MB) [по возрасту: %d, по размеру: %d]", + totalDeleted, float64(totalDeletedBytes)/(1024*1024), deletedByAge, deletedBySize) } } -func (r *Retention) cleanByAge(dataDir string) int { +// cleanByAge возвращает количество удаленных файлов и их общий размер в байтах +func (r *Retention) cleanByAge(dataDir string) (int, int64) { cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour) deleted := 0 + var deletedBytes int64 // Получаем все .bin файлы files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin")) if err != nil { - return 0 + return 0, 0 } for _, file := range files { @@ -118,25 +130,24 @@ func (r *Retention) cleanByAge(dataDir string) int { if fileTime.Before(cutoff) { if err := os.Remove(file); err == nil { deleted++ - if r.eventLogger != nil { - r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_ВОЗРАСТУ") - } + deletedBytes += info.Size() } } } - return deleted + return deleted, deletedBytes } -func (r *Retention) cleanBySize(dataDir string) int { +// cleanBySize возвращает количество удаленных файлов и их общий размер в байтах +func (r *Retention) cleanBySize(dataDir string) (int, int64) { // Получаем все файлы files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin")) if err != nil { - return 0 + return 0, 0 } if len(files) == 0 { - return 0 + return 0, 0 } // Сортируем по времени (старыми первыми) @@ -175,22 +186,23 @@ func (r *Retention) cleanBySize(dataDir string) int { // Удаляем самые старые пока не уложимся в лимит deleted := 0 + var deletedBytes int64 + + // Сохраняем как минимум 1 файл for totalSize > r.maxSizeBytes && len(fileList) > 1 { // Удаляем самый старый файл oldest := fileList[0] if err := os.Remove(oldest.path); err == nil { totalSize -= oldest.size + deletedBytes += oldest.size deleted++ fileList = fileList[1:] - if r.eventLogger != nil { - r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_РАЗМЕРУ") - } } else { break } } - return deleted + return deleted, deletedBytes } func (r *Retention) parseFilenameTime(filename string) time.Time { diff --git a/internal/pipe/reader.go b/internal/pipe/reader.go index f9c9738..5d5581b 100644 --- a/internal/pipe/reader.go +++ b/internal/pipe/reader.go @@ -23,8 +23,12 @@ type PipeReader struct { lastAlertTime map[byte]time.Time // Настройки - alertCooldownSec time.Duration + alertCooldownSec time.Duration humanLogIntervalSec time.Duration + + // Флаги состояния + pipeState string // "unknown", "found", "not_found" + lastLoggedState string // для отслеживания изменений } func NewPipeReader( @@ -36,14 +40,26 @@ func NewPipeReader( config *logger.Config, ) *PipeReader { return &PipeReader{ - buf: buf, - dataLogger: dataLogger, - humanLogger: humanLogger, - monitor: monitor, - eventLog: eventLog, - lastAlertTime: make(map[byte]time.Time), - alertCooldownSec: config.AlertCooldownSec, + buf: buf, + dataLogger: dataLogger, + humanLogger: humanLogger, + monitor: monitor, + eventLog: eventLog, + lastAlertTime: make(map[byte]time.Time), + alertCooldownSec: config.AlertCooldownSec, humanLogIntervalSec: config.HumanLogIntervalSec, + pipeState: "unknown", + lastLoggedState: "unknown", + } +} + + +func (pr *PipeReader) logStateChange(newState string, eventName string) { + if newState != pr.lastLoggedState { + if pr.eventLog != nil { + pr.eventLog.Event(eventName) + } + pr.lastLoggedState = newState } } @@ -59,8 +75,10 @@ func (pr *PipeReader) Start(ctx context.Context, pipePath string) { } if _, err := os.Stat(pipePath); os.IsNotExist(err) { - if pr.eventLog != nil { - pr.eventLog.Event("PIPE_НЕ_НАЙДЕН") + // Логируем ТОЛЬКО при смене состояния + if pr.pipeState != "not_found" { + pr.pipeState = "not_found" + pr.logStateChange("not_found", "PIPE_НЕ_НАЙДЕН") } time.Sleep(2 * time.Second) continue @@ -68,27 +86,38 @@ func (pr *PipeReader) Start(ctx context.Context, pipePath string) { f, err := os.OpenFile(pipePath, os.O_RDONLY, 0) if err != nil { - if pr.eventLog != nil { - pr.eventLog.Event("ОШИБКА_ОТКРЫТИЯ_PIPE") + if pr.pipeState != "error" { + pr.pipeState = "error" + pr.logStateChange("error", "ОШИБКА_ОТКРЫТИЯ_PIPE") } time.Sleep(time.Second) continue } - if pr.eventLog != nil { - pr.eventLog.Event("PIPE_ПОДКЛЮЧЕН") + // Pipe успешно открыт + if pr.pipeState != "found" { + pr.pipeState = "found" + pr.logStateChange("found", "PIPE_ПОДКЛЮЧЕН") } for { n, err := f.Read(buffer) if err != nil { f.Close() - if pr.eventLog != nil { - pr.eventLog.Event("PIPE_ОТКЛЮЧЕН") + // Отключаемся только если были подключены + if pr.pipeState == "found" { + pr.pipeState = "disconnected" + pr.logStateChange("disconnected", "PIPE_ОТКЛЮЧЕН") } break } + // Если были в состоянии ошибки/отключения, восстанавливаемся + if pr.pipeState != "found" { + pr.pipeState = "found" + pr.logStateChange("found", "PIPE_ПОДКЛЮЧЕН") + } + for i := 0; i < n; i++ { rawByte := buffer[i] now := time.Now()