Доработка. Использование стандартных путей Linux
This commit is contained in:
@@ -165,15 +165,20 @@ func main() {
|
|||||||
// ===== ИНИЦИАЛИЗАЦИЯ ЛОГГЕРОВ =====
|
// ===== ИНИЦИАЛИЗАЦИЯ ЛОГГЕРОВ =====
|
||||||
|
|
||||||
// 1. EventLogger (события системы)
|
// 1. EventLogger (события системы)
|
||||||
eventLogger, err := logger.NewEventLogger("logs/events.log")
|
eventLogger, err := logger.NewEventLogger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Ошибка инициализации EventLogger:", err)
|
log.Fatal("Ошибка инициализации EventLogger:", err)
|
||||||
}
|
}
|
||||||
defer eventLogger.Close()
|
defer eventLogger.Close()
|
||||||
eventLogger.Event("СЕРВЕР_ЗАПУЩЕН")
|
eventLogger.Event("СЕРВЕР_ЗАПУЩЕН")
|
||||||
|
|
||||||
|
// Выводим пути для информации
|
||||||
|
if logsDir, err := logger.GetLogsDir(); err == nil {
|
||||||
|
log.Printf("Логи сохраняются в: %s", logsDir)
|
||||||
|
}
|
||||||
|
|
||||||
// 1.5 Human-readable логгер
|
// 1.5 Human-readable логгер
|
||||||
humanLogger, err := logger.NewHumanLogger("logs/gpio_human.log")
|
humanLogger, err := logger.NewHumanLogger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Ошибка инициализации HumanLogger:", err)
|
log.Fatal("Ошибка инициализации HumanLogger:", err)
|
||||||
}
|
}
|
||||||
@@ -184,7 +189,7 @@ func main() {
|
|||||||
monitor := logger.NewMonitor(eventLogger)
|
monitor := logger.NewMonitor(eventLogger)
|
||||||
|
|
||||||
// 3. DataLogger с ротацией
|
// 3. DataLogger с ротацией
|
||||||
dataLogger, err := logger.NewRotatingLogger("logs/data", eventLogger)
|
dataLogger, err := logger.NewRotatingLogger(eventLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
eventLogger.Event("ОШИБКА_ИНИЦИАЛИЗАЦИИ_DATA_ЛОГЕРА")
|
eventLogger.Event("ОШИБКА_ИНИЦИАЛИЗАЦИИ_DATA_ЛОГЕРА")
|
||||||
log.Fatal("Ошибка инициализации DataLogger:", err)
|
log.Fatal("Ошибка инициализации DataLogger:", err)
|
||||||
@@ -192,19 +197,11 @@ func main() {
|
|||||||
defer dataLogger.Close()
|
defer dataLogger.Close()
|
||||||
eventLogger.Event("DATA_ЛОГЕР_ГОТОВ")
|
eventLogger.Event("DATA_ЛОГЕР_ГОТОВ")
|
||||||
|
|
||||||
// 3.5 Retention (автоматическая очистка)
|
// 3.5 Retention (автоматическая очистка)
|
||||||
retention := logger.NewRetention("logs/data", 48, 5000, eventLogger) // 48 часов, 5 GB
|
retention := logger.NewRetention(48, 5000, eventLogger)
|
||||||
retention.Start()
|
retention.Start()
|
||||||
eventLogger.Event("RETENTION_ГОТОВ")
|
eventLogger.Event("RETENTION_ГОТОВ")
|
||||||
|
|
||||||
// Получаем информацию о доступных данных
|
|
||||||
if oldest, newest, count := retention.GetAvailableRange(); count > 0 {
|
|
||||||
log.Printf("Доступны данные с %s по %s (%d файлов)",
|
|
||||||
oldest.Format("2006-01-02 15:04"),
|
|
||||||
newest.Format("2006-01-02 15:04"),
|
|
||||||
count)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. RingBuffer
|
// 4. RingBuffer
|
||||||
buf := adapter.NewRingBuffer(1024 * 10)
|
buf := adapter.NewRingBuffer(1024 * 10)
|
||||||
|
|
||||||
|
|||||||
@@ -4,38 +4,41 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EventLogger struct {
|
type EventLogger struct {
|
||||||
file *os.File
|
file *os.File
|
||||||
humanFile *os.File
|
humanFile *os.File
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEventLogger(path string) (*EventLogger, error) {
|
func NewEventLogger() (*EventLogger, error) {
|
||||||
// Создаём директорию для логов
|
// Получаем пути к файлам
|
||||||
dir := filepath.Dir(path)
|
jsonPath, err := GetEventLogPath()
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON файл для событий
|
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("ошибка получения пути для JSON логов: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Human-readable файл для событий
|
humanPath, err := GetEventHumanLogPath()
|
||||||
humanPath := filepath.Join(dir, "events_human.log")
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ошибка получения пути для Human логов: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открываем JSON файл
|
||||||
|
f, err := os.OpenFile(jsonPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ошибка открытия JSON лога: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открываем Human-readable файл
|
||||||
humanF, err := os.OpenFile(humanPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
humanF, err := os.OpenFile(humanPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return nil, err
|
return nil, fmt.Errorf("ошибка открытия Human лога: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &EventLogger{
|
return &EventLogger{
|
||||||
file: f,
|
file: f,
|
||||||
humanFile: humanF,
|
humanFile: humanF,
|
||||||
@@ -45,9 +48,9 @@ func NewEventLogger(path string) (*EventLogger, error) {
|
|||||||
func (l *EventLogger) Event(name string) {
|
func (l *EventLogger) Event(name string) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// JSON формат (для машин)
|
// JSON формат (для машин)
|
||||||
ev := struct {
|
ev := struct {
|
||||||
Timestamp int64 `json:"ts"`
|
Timestamp int64 `json:"ts"`
|
||||||
@@ -58,15 +61,15 @@ func (l *EventLogger) Event(name string) {
|
|||||||
Time: now.Format("2006-01-02 15:04:05"),
|
Time: now.Format("2006-01-02 15:04:05"),
|
||||||
Event: name,
|
Event: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, _ := json.Marshal(ev)
|
data, _ := json.Marshal(ev)
|
||||||
data = append(data, '\n')
|
data = append(data, '\n')
|
||||||
l.file.Write(data)
|
l.file.Write(data)
|
||||||
|
|
||||||
// Human-readable формат
|
// Human-readable формат
|
||||||
humanLine := fmt.Sprintf("[%s] EVENT: %s\n", now.Format("2006-01-02 15:04:05.000"), name)
|
humanLine := fmt.Sprintf("[%s] EVENT: %s\n", now.Format("2006-01-02 15:04:05.000"), name)
|
||||||
l.humanFile.WriteString(humanLine)
|
l.humanFile.WriteString(humanLine)
|
||||||
|
|
||||||
l.file.Sync()
|
l.file.Sync()
|
||||||
l.humanFile.Sync()
|
l.humanFile.Sync()
|
||||||
}
|
}
|
||||||
@@ -75,4 +78,4 @@ func (l *EventLogger) Close() error {
|
|||||||
l.file.Close()
|
l.file.Close()
|
||||||
l.humanFile.Close()
|
l.humanFile.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -10,27 +10,28 @@ type HumanLogger struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanLogger(path string) (*HumanLogger, error) {
|
func NewHumanLogger() (*HumanLogger, error) {
|
||||||
if err := os.MkdirAll("logs", 0755); err != nil {
|
path, err := GetGPIOLogPath()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HumanLogger{file: f}, nil
|
return &HumanLogger{file: f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *HumanLogger) Write(data string) {
|
func (l *HumanLogger) Write(data string) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.file.WriteString(data)
|
l.file.WriteString(data)
|
||||||
l.file.Sync()
|
l.file.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *HumanLogger) Close() error {
|
func (l *HumanLogger) Close() error {
|
||||||
return l.file.Close()
|
return l.file.Close()
|
||||||
}
|
}
|
||||||
85
internal/logger/paths.go
Normal file
85
internal/logger/paths.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDataDir возвращает стандартную директорию для данных приложения
|
||||||
|
func GetDataDir() (string, error) {
|
||||||
|
// Используем XDG Base Directory Specification
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~/.local/share/gpio-monitoring
|
||||||
|
dataDir := filepath.Join(homeDir, ".local", "share", "gpio-monitoring")
|
||||||
|
|
||||||
|
// Создаём директорию если её нет
|
||||||
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogsDir возвращает директорию для логов
|
||||||
|
func GetLogsDir() (string, error) {
|
||||||
|
dataDir, err := GetDataDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
logsDir := filepath.Join(dataDir, "logs")
|
||||||
|
if err := os.MkdirAll(logsDir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return logsDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDataLogsDir возвращает директорию для бинарных данных
|
||||||
|
func GetDataLogsDir() (string, error) {
|
||||||
|
logsDir, err := GetLogsDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataDir := filepath.Join(logsDir, "data")
|
||||||
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEventLogPath возвращает путь к файлу событий
|
||||||
|
func GetEventLogPath() (string, error) {
|
||||||
|
logsDir, err := GetLogsDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(logsDir, "events.log"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEventHumanLogPath возвращает путь к человеко-читаемому файлу событий
|
||||||
|
func GetEventHumanLogPath() (string, error) {
|
||||||
|
logsDir, err := GetLogsDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(logsDir, "events_human.log"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGPIOLogPath возвращает путь к человеко-читаемому GPIO логу
|
||||||
|
func GetGPIOLogPath() (string, error) {
|
||||||
|
logsDir, err := GetLogsDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(logsDir, "gpio_human.log"), nil
|
||||||
|
}
|
||||||
@@ -10,15 +10,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Retention struct {
|
type Retention struct {
|
||||||
dataDir string
|
maxAgeHours int
|
||||||
maxAgeHours int // максимальный возраст файлов в часах
|
maxSizeBytes int64
|
||||||
maxSizeBytes int64 // максимальный размер всех файлов в байтах
|
|
||||||
eventLogger *EventLogger
|
eventLogger *EventLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRetention(dataDir string, maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
|
func NewRetention(maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
|
||||||
return &Retention{
|
return &Retention{
|
||||||
dataDir: dataDir,
|
|
||||||
maxAgeHours: maxAgeHours,
|
maxAgeHours: maxAgeHours,
|
||||||
maxSizeBytes: int64(maxSizeMB) * 1024 * 1024,
|
maxSizeBytes: int64(maxSizeMB) * 1024 * 1024,
|
||||||
eventLogger: eventLogger,
|
eventLogger: eventLogger,
|
||||||
@@ -41,29 +39,38 @@ func (r *Retention) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Retention) Cleanup() {
|
func (r *Retention) Cleanup() {
|
||||||
|
// Получаем директорию с данными
|
||||||
|
dataDir, err := GetDataLogsDir()
|
||||||
|
if err != nil {
|
||||||
|
if r.eventLogger != nil {
|
||||||
|
r.eventLogger.Event("ОШИБКА_ПОЛУЧЕНИЯ_ДИРЕКТОРИИ")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if r.eventLogger != nil {
|
if r.eventLogger != nil {
|
||||||
r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКА_ЛОГОВ")
|
r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКА_ЛОГОВ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Удаляем старые файлы
|
// 1. Удаляем старые файлы
|
||||||
deletedByAge := r.cleanByAge()
|
deletedByAge := r.cleanByAge(dataDir)
|
||||||
|
|
||||||
// 2. Проверяем общий размер и удаляем самые старые если превышен лимит
|
// 2. Проверяем общий размер и удаляем самые старые если превышен лимит
|
||||||
deletedBySize := r.cleanBySize()
|
deletedBySize := r.cleanBySize(dataDir)
|
||||||
|
|
||||||
if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) {
|
if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) {
|
||||||
r.eventLogger.Event("УДАЛЕНО_ФАЙЛОВ")
|
r.eventLogger.Event("УДАЛЕНО_ФАЙЛОВ")
|
||||||
log.Printf("🧹 Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)",
|
log.Printf("Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)",
|
||||||
deletedByAge+deletedBySize, deletedByAge, deletedBySize)
|
deletedByAge+deletedBySize, deletedByAge, deletedBySize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Retention) cleanByAge() int {
|
func (r *Retention) cleanByAge(dataDir string) int {
|
||||||
cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
|
cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
|
||||||
deleted := 0
|
deleted := 0
|
||||||
|
|
||||||
// Получаем все .bin файлы
|
// Получаем все .bin файлы
|
||||||
files, err := filepath.Glob(filepath.Join(r.dataDir, "gpio-*.bin"))
|
files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -93,9 +100,9 @@ func (r *Retention) cleanByAge() int {
|
|||||||
return deleted
|
return deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Retention) cleanBySize() int {
|
func (r *Retention) cleanBySize(dataDir string) int {
|
||||||
// Получаем все файлы
|
// Получаем все файлы
|
||||||
files, err := filepath.Glob(filepath.Join(r.dataDir, "gpio-*.bin"))
|
files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -104,7 +111,7 @@ func (r *Retention) cleanBySize() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сортируем по времени (старые первыми)
|
// Сортируем по времени (старыми первыми)
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
path string
|
path string
|
||||||
time time.Time
|
time time.Time
|
||||||
@@ -172,33 +179,4 @@ func (r *Retention) parseFilenameTime(filename string) time.Time {
|
|||||||
hour, _ := time.Parse("15", parts[4])
|
hour, _ := time.Parse("15", parts[4])
|
||||||
|
|
||||||
return time.Date(year.Year(), month.Month(), day.Day(), hour.Hour(), 0, 0, 0, time.Local)
|
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
|
|
||||||
}
|
}
|
||||||
@@ -2,27 +2,28 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RotatingLogger struct {
|
type RotatingLogger struct {
|
||||||
dataLogger *DataLogger
|
dataLogger *DataLogger
|
||||||
currentHour int
|
currentHour int
|
||||||
baseDir string
|
baseDir string
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
eventLogger *EventLogger
|
eventLogger *EventLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRotatingLogger(baseDir string, eventLogger *EventLogger) (*RotatingLogger, error) {
|
func NewRotatingLogger(eventLogger *EventLogger) (*RotatingLogger, error) {
|
||||||
if err := os.MkdirAll(baseDir, 0755); err != nil {
|
// Получаем директорию для бинарных данных
|
||||||
|
dataDir, err := GetDataLogsDir()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &RotatingLogger{
|
r := &RotatingLogger{
|
||||||
baseDir: baseDir,
|
baseDir: dataDir,
|
||||||
eventLogger: eventLogger,
|
eventLogger: eventLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,10 +35,10 @@ func NewRotatingLogger(baseDir string, eventLogger *EventLogger) (*RotatingLogge
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RotatingLogger) getFilename(hour int) string {
|
func (r *RotatingLogger) getFilename() string {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
return filepath.Join(r.baseDir, fmt.Sprintf("gpio-%04d-%02d-%02d-%02d.bin",
|
return filepath.Join(r.baseDir, fmt.Sprintf("gpio-%04d-%02d-%02d-%02d.bin",
|
||||||
now.Year(), now.Month(), now.Day(), hour))
|
now.Year(), now.Month(), now.Day(), now.Hour()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RotatingLogger) rotate() error {
|
func (r *RotatingLogger) rotate() error {
|
||||||
@@ -58,16 +59,20 @@ func (r *RotatingLogger) rotate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Открываем новый
|
// Открываем новый
|
||||||
filename := r.getFilename(newHour)
|
filename := r.getFilename()
|
||||||
dataLogger, err := NewDataLogger(filename)
|
dataLogger, err := NewDataLogger(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.eventLogger.Event("ROTATION_FAILED")
|
if r.eventLogger != nil {
|
||||||
|
r.eventLogger.Event("ROTATION_FAILED")
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
r.dataLogger = dataLogger
|
r.dataLogger = dataLogger
|
||||||
r.currentHour = newHour
|
r.currentHour = newHour
|
||||||
r.eventLogger.Event("ROTATION_COMPLETE")
|
if r.eventLogger != nil {
|
||||||
|
r.eventLogger.Event("ROTATION_COMPLETE")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,36 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd /home/user/temp/golang
|
LOG_DIR="$HOME/.local/share/gpio-monitoring/logs"
|
||||||
|
|
||||||
echo "=== СТАТИСТИКА ЛОГОВ ==="
|
echo "=== СТАТИСТИКА ЛОГОВ ==="
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ -d logs ]; then
|
if [ -d "$LOG_DIR" ]; then
|
||||||
|
echo "Логи в: $LOG_DIR"
|
||||||
|
echo ""
|
||||||
echo "Общая статистика:"
|
echo "Общая статистика:"
|
||||||
echo " Событий (JSON): $(wc -l < logs/events.log 2>/dev/null || echo 0)"
|
echo " Событий (JSON): $(wc -l < $LOG_DIR/events.log 2>/dev/null || echo 0)"
|
||||||
echo " Событий (Human): $(wc -l < logs/events_human.log 2>/dev/null || echo 0)"
|
echo " Событий (Human): $(wc -l < $LOG_DIR/events_human.log 2>/dev/null || echo 0)"
|
||||||
echo " Записей GPIO: $(wc -l < logs/gpio_human.log 2>/dev/null || echo 0)"
|
echo " Записей GPIO: $(wc -l < $LOG_DIR/gpio_human.log 2>/dev/null || echo 0)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "Последние 5 предупреждений:"
|
echo "оследние 5 предупреждений:"
|
||||||
grep "ВНИМАНИЕ" logs/gpio_human.log 2>/dev/null | tail -5 || echo " Нет предупреждений"
|
grep "ВНИМАНИЕ" $LOG_DIR/gpio_human.log 2>/dev/null | tail -5 || echo " Нет предупреждений"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "Топ-10 по количеству обнаруженных объектов:"
|
echo "Статистика по значениям:"
|
||||||
grep "Обнаружение:" logs/gpio_human.log 2>/dev/null | \
|
grep "Значение GPIO:" $LOG_DIR/gpio_human.log 2>/dev/null | awk -F': ' '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
|
||||||
sed -n 's/.* \([0-9]\+\) объектов.*/\1/p' | \
|
|
||||||
sort | uniq -c | sort -rn | head -10 | \
|
|
||||||
awk '{print " " $2 " объектов: " $1 " раз"}'
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "Распределение по силе сигнала:"
|
|
||||||
grep "Обнаружение:" logs/gpio_human.log 2>/dev/null | \
|
|
||||||
grep -o "Сила: [А-Яа-я]*" | \
|
|
||||||
sort | uniq -c | \
|
|
||||||
awk '{print " " $2 " " $3 ": " $1 " раз"}'
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "💾 Использование диска:"
|
echo "💾 Использование диска:"
|
||||||
du -sh logs/ 2>/dev/null
|
du -sh $LOG_DIR 2>/dev/null
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if ls logs/data/*.bin 1> /dev/null 2>&1; then
|
if ls $LOG_DIR/data/*.bin 1> /dev/null 2>&1; then
|
||||||
echo "Файлы данных:"
|
echo "📁 Файлы данных:"
|
||||||
ls -lh logs/data/*.bin 2>/dev/null | awk '{print " " $9 ": " $5}'
|
ls -lh $LOG_DIR/data/*.bin 2>/dev/null | awk '{print " " $9 ": " $5}'
|
||||||
else
|
|
||||||
echo "Нет файлов данных"
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Директория logs/ не найдена"
|
echo "❌ Директория логов не найдена: $LOG_DIR"
|
||||||
|
echo "Запустите сервер для создания логов"
|
||||||
fi
|
fi
|
||||||
Reference in New Issue
Block a user