Доработка. Парсинг в логах
This commit is contained in:
40
internal/logger/parser.go
Normal file
40
internal/logger/parser.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
type GPIOData struct {
|
||||||
|
RawValue byte // сырое значение
|
||||||
|
Count byte // количество обнаружений (биты 0-5)
|
||||||
|
Strength byte // сила сигнала (биты 6-7)
|
||||||
|
IsHighCount bool // превышает ли количество 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGPIO парсит сырой байт на составляющие
|
||||||
|
func ParseGPIO(raw byte) GPIOData {
|
||||||
|
// Биты 0-5: количество (0-63)
|
||||||
|
count := raw & 0x3F // маска 00111111
|
||||||
|
|
||||||
|
// Биты 6-7: сила (0-3)
|
||||||
|
strength := (raw >> 6) & 0x03 // сдвиг на 6 бит, маска 00000011
|
||||||
|
|
||||||
|
return GPIOData{
|
||||||
|
RawValue: raw,
|
||||||
|
Count: count,
|
||||||
|
Strength: strength,
|
||||||
|
IsHighCount: count > 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStrengthName возвращает текстовое описание силы
|
||||||
|
func GetStrengthName(strength byte) string {
|
||||||
|
switch strength {
|
||||||
|
case 0:
|
||||||
|
return "Слабый"
|
||||||
|
case 1:
|
||||||
|
return "Средний"
|
||||||
|
case 2:
|
||||||
|
return "Сильный"
|
||||||
|
case 3:
|
||||||
|
return "Максимальный"
|
||||||
|
default:
|
||||||
|
return "Неизвестно"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,11 @@ type PipeReader struct {
|
|||||||
humanLogger *logger.HumanLogger
|
humanLogger *logger.HumanLogger
|
||||||
monitor *logger.Monitor
|
monitor *logger.Monitor
|
||||||
eventLog *logger.EventLogger
|
eventLog *logger.EventLogger
|
||||||
lastAlert map[byte]time.Time // для предотвращения спама алертов
|
|
||||||
|
// Для дедупликации логов
|
||||||
|
lastData logger.GPIOData
|
||||||
|
lastLogTime time.Time
|
||||||
|
lastAlertTime map[byte]time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPipeReader(
|
func NewPipeReader(
|
||||||
@@ -27,12 +31,12 @@ func NewPipeReader(
|
|||||||
eventLog *logger.EventLogger,
|
eventLog *logger.EventLogger,
|
||||||
) *PipeReader {
|
) *PipeReader {
|
||||||
return &PipeReader{
|
return &PipeReader{
|
||||||
buf: buf,
|
buf: buf,
|
||||||
dataLogger: dataLogger,
|
dataLogger: dataLogger,
|
||||||
humanLogger: humanLogger,
|
humanLogger: humanLogger,
|
||||||
monitor: monitor,
|
monitor: monitor,
|
||||||
eventLog: eventLog,
|
eventLog: eventLog,
|
||||||
lastAlert: make(map[byte]time.Time),
|
lastAlertTime: make(map[byte]time.Time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +51,6 @@ func (pr *PipeReader) Start(ctx context.Context, pipePath string) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем существует ли pipe
|
|
||||||
if _, err := os.Stat(pipePath); os.IsNotExist(err) {
|
if _, err := os.Stat(pipePath); os.IsNotExist(err) {
|
||||||
if pr.eventLog != nil {
|
if pr.eventLog != nil {
|
||||||
pr.eventLog.Event("PIPE_НЕ_НАЙДЕН")
|
pr.eventLog.Event("PIPE_НЕ_НАЙДЕН")
|
||||||
@@ -79,33 +82,58 @@ func (pr *PipeReader) Start(ctx context.Context, pipePath string) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обрабатываем каждый байт
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
b := buffer[i]
|
rawByte := buffer[i]
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// 1. В RAM для UI
|
// Парсим данные
|
||||||
pr.buf.Write(b)
|
data := logger.ParseGPIO(rawByte)
|
||||||
|
|
||||||
|
// 1. В RAM для UI (сырое значение)
|
||||||
|
pr.buf.Write(rawByte)
|
||||||
|
|
||||||
// 2. На диск для архива (бинарный)
|
// 2. На диск для архива (бинарный)
|
||||||
if pr.dataLogger != nil {
|
if pr.dataLogger != nil {
|
||||||
pr.dataLogger.Write(logger.Sample{
|
pr.dataLogger.Write(logger.Sample{
|
||||||
Timestamp: now.UnixMicro(),
|
Timestamp: now.UnixMicro(),
|
||||||
Value: b,
|
Value: rawByte,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Human-readable лог (только когда есть данные)
|
// 3. Human-readable лог (при изменении состояния или раз в 5 секунд)
|
||||||
if pr.humanLogger != nil {
|
if pr.humanLogger != nil {
|
||||||
humanLine := fmt.Sprintf("[%s] Значение GPIO: %d (0x%02X)\n",
|
shouldLog := false
|
||||||
now.Format("2006-01-02 15:04:05.000"),
|
|
||||||
b, b)
|
// Логируем если изменилось количество или сила
|
||||||
pr.humanLogger.Write(humanLine)
|
if data.Count != pr.lastData.Count || data.Strength != pr.lastData.Strength {
|
||||||
|
shouldLog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Или если прошло больше 5 секунд с последнего лога
|
||||||
|
if now.Sub(pr.lastLogTime) >= 5*time.Second {
|
||||||
|
shouldLog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldLog {
|
||||||
|
strengthName := logger.GetStrengthName(data.Strength)
|
||||||
|
humanLine := fmt.Sprintf(
|
||||||
|
"[%s] Обнаружение: %d объектов | Сила: %s (уровень %d) | Сырое: 0x%02X (%d)\n",
|
||||||
|
now.Format("2006-01-02 15:04:05.000"),
|
||||||
|
data.Count,
|
||||||
|
strengthName,
|
||||||
|
data.Strength,
|
||||||
|
rawByte,
|
||||||
|
rawByte,
|
||||||
|
)
|
||||||
|
pr.humanLogger.Write(humanLine)
|
||||||
|
pr.lastData = data
|
||||||
|
pr.lastLogTime = now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Алерт если значение > 10
|
// 4. Алерт если количество обнаружений > 10
|
||||||
if b > 10 {
|
if data.IsHighCount {
|
||||||
pr.handleAlert(b, now)
|
pr.handleAlert(data, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Обновляем watchdog
|
// 5. Обновляем watchdog
|
||||||
@@ -115,32 +143,37 @@ func (pr *PipeReader) Start(ctx context.Context, pipePath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пауза перед переподключением
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *PipeReader) handleAlert(value byte, timestamp time.Time) {
|
func (pr *PipeReader) handleAlert(data logger.GPIOData, timestamp time.Time) {
|
||||||
// Anti-spam: не чаще 1 алерта в секунду для одного значения
|
// Anti-spam: не чаще 1 алерта в 10 секунд для одинакового количества
|
||||||
if last, exists := pr.lastAlert[value]; exists {
|
key := data.Count
|
||||||
if timestamp.Sub(last) < 1*time.Second {
|
if last, exists := pr.lastAlertTime[key]; exists {
|
||||||
|
if timestamp.Sub(last) < 10*time.Second {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pr.lastAlert[value] = timestamp
|
pr.lastAlertTime[key] = timestamp
|
||||||
|
|
||||||
|
strengthName := logger.GetStrengthName(data.Strength)
|
||||||
|
|
||||||
// 1. В event log (JSON)
|
// 1. В event log (JSON)
|
||||||
if pr.eventLog != nil {
|
if pr.eventLog != nil {
|
||||||
pr.eventLog.Event(fmt.Sprintf("ВЫСОКОЕ_ЗНАЧЕНИЕ_%d", value))
|
pr.eventLog.Event(fmt.Sprintf("ОБНАРУЖЕНО_%d_ОБЪЕКТОВ_СИЛА_%d", data.Count, data.Strength))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. В human-readable лог с предупреждением (только на русском)
|
// 2. В human-readable лог с предупреждением
|
||||||
if pr.humanLogger != nil {
|
if pr.humanLogger != nil {
|
||||||
alertLine := fmt.Sprintf("[%s] ВНИМАНИЕ: Обнаружено высокое значение! GPIO = %d (>10)\n",
|
alertLine := fmt.Sprintf(
|
||||||
timestamp.Format("2006-01-02 15:04:05.000"), value)
|
"[%s] ВНИМАНИЕ: Обнаружено превышение! %d объектов | Сила: %s (уровень %d)\n",
|
||||||
|
timestamp.Format("2006-01-02 15:04:05.000"),
|
||||||
|
data.Count,
|
||||||
|
strengthName,
|
||||||
|
data.Strength,
|
||||||
|
)
|
||||||
pr.humanLogger.Write(alertLine)
|
pr.humanLogger.Write(alertLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. В консоль больше НЕ пишем (всё уже в логах)
|
|
||||||
}
|
}
|
||||||
45
scripts/view_logs.sh
Normal file
45
scripts/view_logs.sh
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd /home/user/temp/golang
|
||||||
|
|
||||||
|
echo "=== СТАТИСТИКА ЛОГОВ ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -d logs ]; then
|
||||||
|
echo "Общая статистика:"
|
||||||
|
echo " Событий (JSON): $(wc -l < logs/events.log 2>/dev/null || echo 0)"
|
||||||
|
echo " Событий (Human): $(wc -l < logs/events_human.log 2>/dev/null || echo 0)"
|
||||||
|
echo " Записей GPIO: $(wc -l < logs/gpio_human.log 2>/dev/null || echo 0)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "Последние 5 предупреждений:"
|
||||||
|
grep "ВНИМАНИЕ" logs/gpio_human.log 2>/dev/null | tail -5 || echo " Нет предупреждений"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "Топ-10 по количеству обнаруженных объектов:"
|
||||||
|
grep "Обнаружение:" logs/gpio_human.log 2>/dev/null | \
|
||||||
|
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 "💾 Использование диска:"
|
||||||
|
du -sh logs/ 2>/dev/null
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if ls logs/data/*.bin 1> /dev/null 2>&1; then
|
||||||
|
echo "Файлы данных:"
|
||||||
|
ls -lh logs/data/*.bin 2>/dev/null | awk '{print " " $9 ": " $5}'
|
||||||
|
else
|
||||||
|
echo "Нет файлов данных"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Директория logs/ не найдена"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user