PatrickRedStar commited on
Commit
d6f4b44
·
1 Parent(s): 30e257a
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,12 +1,231 @@
1
- ---
2
- title: MultiAgentLogsAnalyze
3
- emoji: 🔥
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 6.2.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: MultiAgentLogsAnalyze
3
+ emoji: 🔥
4
+ colorFrom: gray
5
+ colorTo: indigo
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # 🔍 Мультиагентная система анализа логов
13
+
14
+ Интерактивное веб-приложение для автоматизированного анализа лог-файлов с использованием трёх логически разделённых агентов.
15
+
16
+ ## 📋 Описание
17
+
18
+ Система обеспечивает автоматизированный анализ логов с:
19
+ - Структурированием данных
20
+ - Выявлением аномалий
21
+ - Интерпретацией причин и выдачей рекомендаций
22
+
23
+ ## 🏗️ Архитектура
24
+
25
+ Проект реализован как мультиагентная система с **строго 3 независимыми агентами**, каждый из которых выполняет определённую функцию в цепочке анализа.
26
+
27
+ ### Агент 1: Log Parser Agent (`agents/parser_agent.py`)
28
+
29
+ **Назначение:** Преобразование сырых логов в структурированное представление.
30
+
31
+ **Функциональность:**
32
+ - Разбор строк логов
33
+ - Извлечение временных меток
34
+ - Определение уровней логирования (INFO, WARNING, ERROR, CRITICAL, DEBUG)
35
+ - Извлечение сообщений
36
+ - Группировка по типам событий (CONNECTION, HTTP_REQUEST, DATABASE, AUTHENTICATION, EXCEPTION, SYSTEM, GENERAL)
37
+
38
+ **Выход:** Структурированный JSON-объект с полями:
39
+ - `events` - список всех распарсенных событий
40
+ - `errors` - список ошибок
41
+ - `warnings` - список предупреждений
42
+ - `statistics` - статистика по логам
43
+
44
+ ### Агент 2: Anomaly Detection Agent (`agents/anomaly_agent.py`)
45
+
46
+ **Назначение:** Выявление аномалий и подозрительных паттернов в структурированных логах.
47
+
48
+ **Функциональность:**
49
+ - Подсчёт частоты событий
50
+ - Поиск повторяющихся ошибок
51
+ - Обнаружение временных всплесков
52
+ - Эвристический анализ последовательностей событий
53
+
54
+ **Типы обнаруживаемых аномалий:**
55
+ - **BURST_ERRORS** - всплески ошибок (более 5 ошибок в короткий промежуток времени)
56
+ - **REPEATED_ERRORS** - повторяющиеся ошибки (одна и та же ошибка более 3 раз)
57
+ - **ERROR_BEFORE_CRASH** - паттерны "ошибка перед крашем"
58
+ - **TEMPORAL_SPIKE** - временные всплески событий (превышение среднего в 2 раза)
59
+ - **REPEATED_STACK_TRACES** - повторяющиеся stack traces
60
+
61
+ **Выход:** JSON-отчёт об аномалиях с описанием, метаданными и статистикой.
62
+
63
+ ### Агент 3: Root Cause & Recommendation Agent (`agents/rca_agent.py`)
64
+
65
+ **Назначение:** Интерпретация аномалий и формирование рекомендаций.
66
+
67
+ **Функциональность:**
68
+ - Определение возможных первопричин на основе типа аномалий
69
+ - Формирование человеко-читаемого отчёта в формате Markdown
70
+ - Генерация рекомендаций по устранению проблем с приоритетами
71
+ - Предоставление конкретных действий для решения проблем
72
+
73
+ **Выход:** Markdown-текст с анализом первопричин и рекомендациями.
74
+
75
+ ## 🔄 Процесс анализа
76
+
77
+ Анализ выполняется последовательно:
78
+
79
+ 1. **Пользователь** загружает или вставляет логи в интерфейс
80
+ 2. **Agent 1** (Log Parser) обрабатывает сырые логи → структурированный JSON
81
+ 3. **Agent 2** (Anomaly Detection) анализирует структурированные данные → отчёт об аномалиях
82
+ 4. **Agent 3** (Root Cause) интерпретирует аномалии → Markdown с рекомендациями
83
+ 5. **Результаты** отображаются в интерфейсе в трёх вкладках
84
+
85
+ ## 🚀 Использование
86
+
87
+ ### Локальный запуск
88
+
89
+ 1. Установите зависимости:
90
+ ```bash
91
+ pip install -r requirements.txt
92
+ ```
93
+
94
+ 2. Запустите приложение:
95
+ ```bash
96
+ python app.py
97
+ ```
98
+
99
+ 3. Откройте браузер по адресу `http://localhost:7860`
100
+
101
+ ### Использование в Hugging Face Spaces
102
+
103
+ Приложение автоматически развернётся при загрузке на Hugging Face Spaces.
104
+
105
+ ## 📁 Структура проекта
106
+
107
+ ```
108
+ MultiAgentLogsAnalyze/
109
+ ├── agents/
110
+ │ ├── __init__.py # Экспорт агентов
111
+ │ ├── parser_agent.py # Agent 1: Log Parser Agent
112
+ │ ├── anomaly_agent.py # Agent 2: Anomaly Detection Agent
113
+ │ └── rca_agent.py # Agent 3: Root Cause Agent
114
+ ├── app.py # Gradio приложение и оркестрация
115
+ ├── requirements.txt # Зависимости Python
116
+ ├── README.md # Документация проекта
117
+ └── space_config.yaml # Конфигурация для Hugging Face Spaces
118
+ ```
119
+
120
+ ## 🔧 Технические детали
121
+
122
+ ### Зависимости
123
+
124
+ - `gradio>=4.0.0,<5.0.0` - веб-интерфейс
125
+
126
+ Все агенты реализованы на чистом Python 3.10+ без использования LLM или трансформеров.
127
+
128
+ ### Производительность
129
+
130
+ - Поддержка анализа до 10,000 строк логов
131
+ - Время обработки ≤ 10 секунд для типичных логов
132
+
133
+ ### Обработка ошибок
134
+
135
+ - Валидация входных данных
136
+ - Обработка некорректных логов без падения приложения
137
+ - Информативные сообщения об ошибках
138
+
139
+ ## 🎯 Пример использования
140
+
141
+ ### Пример входных логов:
142
+
143
+ ```
144
+ 2024-01-15 10:00:00 INFO Application started
145
+ 2024-01-15 10:00:05 INFO Database connection established
146
+ 2024-01-15 10:01:00 ERROR Connection timeout to external API
147
+ 2024-01-15 10:01:05 ERROR Connection timeout to external API
148
+ 2024-01-15 10:01:10 ERROR Connection timeout to external API
149
+ 2024-01-15 10:01:15 WARNING High memory usage detected: 85%
150
+ 2024-01-15 10:02:00 CRITICAL System crash detected
151
+ 2024-01-15 10:02:01 INFO Application shutdown
152
+ ```
153
+
154
+ ### Пример выхода Agent 1 (структурированные данные):
155
+
156
+ ```json
157
+ {
158
+ "events": [
159
+ {
160
+ "line_number": 1,
161
+ "timestamp": "2024-01-15 10:00:00",
162
+ "level": "INFO",
163
+ "message": "Application started",
164
+ "type": "SYSTEM"
165
+ },
166
+ ...
167
+ ],
168
+ "errors": [...],
169
+ "warnings": [...],
170
+ "statistics": {
171
+ "total_lines": 8,
172
+ "parsed_events": 8,
173
+ "errors": 3,
174
+ "warnings": 1,
175
+ ...
176
+ }
177
+ }
178
+ ```
179
+
180
+ ### Пример выхода Agent 2 (аномалии):
181
+
182
+ ```json
183
+ {
184
+ "anomalies": [
185
+ {
186
+ "type": "BURST_ERRORS",
187
+ "severity": "HIGH",
188
+ "description": "Обнаружен всплеск из 3 последовательных ошибок",
189
+ "count": 3,
190
+ ...
191
+ },
192
+ {
193
+ "type": "ERROR_BEFORE_CRASH",
194
+ "severity": "CRITICAL",
195
+ "description": "Обнаружен паттерн: ошибка перед возможным крашем системы",
196
+ ...
197
+ }
198
+ ],
199
+ ...
200
+ }
201
+ ```
202
+
203
+ ### Пример выхода Agent 3 (рекомендации):
204
+
205
+ Markdown-отчёт с:
206
+ - Анализом первопричин
207
+ - Детальным описанием аномалий
208
+ - Приоритизированными рекомендациями
209
+ - Конкретными действиями для решения проблем
210
+
211
+ ## 🔌 Расширяемость
212
+
213
+ Система разработана с учётом расширяемости:
214
+
215
+ - **Независимые агенты:** Каждый агент реализован как отдельный класс и может быть заменён без изменения остальных
216
+ - **Чёткий интерфейс:** Агенты взаимодействуют через стандартизированные форматы данных (JSON)
217
+ - **Добавление новых правил:** Легко добавить новые типы аномалий в `AnomalyDetectionAgent`
218
+ - **Кастомные парсеры:** Можно расширить `LogParserAgent` для поддержки новых форматов логов
219
+
220
+ ## 📝 Лицензия
221
+
222
+ Этот проект создан в рамках технического задания для демонстрации мультиагентной архитектуры.
223
+
224
+ ## 🤝 Вклад
225
+
226
+ Проект готов к расширению и улучшению. Возможные направления:
227
+ - Поддержка дополнительных форматов логов
228
+ - Интеграция с LLM для более глубокого анализа
229
+ - Поддержка потоковой обработки больших файлов
230
+ - Экспорт результатов в различные форматы
231
+ - Интеграция с системами мониторинга
__pycache__/app.cpython-314.pyc ADDED
Binary file (12.6 kB). View file
 
agents/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (428 Bytes). View file
 
agents/__pycache__/anomaly_agent.cpython-314.pyc ADDED
Binary file (22.6 kB). View file
 
agents/__pycache__/parser_agent.cpython-314.pyc ADDED
Binary file (12.1 kB). View file
 
agents/__pycache__/rca_agent.cpython-314.pyc ADDED
Binary file (20.5 kB). View file
 
agents/anomaly_agent.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Agent 2: Anomaly Detection Agent
3
+ Выявляет аномалии и подозрительные паттерны в структурированных логах.
4
+ """
5
+
6
+ import re
7
+ import json
8
+ from typing import Dict, List, Any
9
+ from collections import defaultdict, Counter
10
+ from datetime import datetime
11
+
12
+
13
+ class AnomalyDetectionAgent:
14
+ """Обнаруживает аномалии в структурированных логах."""
15
+
16
+ def __init__(self):
17
+ """Инициализация агента."""
18
+ self.burst_threshold = 5 # Минимальное количество ошибок для burst
19
+ self.burst_time_window = 60 # Окно времени в секундах для burst
20
+ self.repeat_threshold = 3 # Минимальное количество повторений
21
+
22
+ def detect(self, structured_data: Dict[str, Any]) -> Dict[str, Any]:
23
+ """
24
+ Выявляет аномалии в структурированных данных.
25
+
26
+ Args:
27
+ structured_data: Структурированные данные от LogParserAgent
28
+
29
+ Returns:
30
+ JSON-отчёт об аномалиях с описанием и метаданными
31
+ """
32
+ if not structured_data or not structured_data.get('events'):
33
+ return self._empty_report()
34
+
35
+ events = structured_data.get('events', [])
36
+ errors = structured_data.get('errors', [])
37
+
38
+ anomalies = []
39
+
40
+ # 1. Обнаружение burst errors
41
+ burst_anomalies = self._detect_burst_errors(events, errors)
42
+ anomalies.extend(burst_anomalies)
43
+
44
+ # 2. Обнаружение повторяющихся ошибок
45
+ repeat_anomalies = self._detect_repeated_errors(errors)
46
+ anomalies.extend(repeat_anomalies)
47
+
48
+ # 3. Обнаружение паттернов "ошибка перед крашем"
49
+ crash_patterns = self._detect_error_before_crash(errors, events)
50
+ anomalies.extend(crash_patterns)
51
+
52
+ # 4. Обнаружение временных всплесков
53
+ spike_anomalies = self._detect_temporal_spikes(events)
54
+ anomalies.extend(spike_anomalies)
55
+
56
+ # 5. Обнаружение повторяющихся stack traces
57
+ stack_trace_anomalies = self._detect_repeated_stack_traces(events)
58
+ anomalies.extend(stack_trace_anomalies)
59
+
60
+ # Подсчёт статистики
61
+ anomaly_stats = self._calculate_anomaly_statistics(anomalies)
62
+
63
+ return {
64
+ 'anomalies': anomalies,
65
+ 'statistics': anomaly_stats,
66
+ 'severity_summary': self._calculate_severity_summary(anomalies)
67
+ }
68
+
69
+ def _detect_burst_errors(self, events: List[Dict], errors: List[Dict]) -> List[Dict[str, Any]]:
70
+ """Обнаруживает всплески ошибок (burst errors)."""
71
+ anomalies = []
72
+
73
+ if len(errors) < self.burst_threshold:
74
+ return anomalies
75
+
76
+ # Группировка ошибок по времени (если доступны временные метки)
77
+ error_times = []
78
+ for error in errors:
79
+ timestamp_str = error.get('timestamp')
80
+ if timestamp_str:
81
+ try:
82
+ timestamp = self._parse_timestamp_simple(timestamp_str)
83
+ if timestamp:
84
+ error_times.append((timestamp, error))
85
+ except:
86
+ pass
87
+
88
+ # Если временные метки доступны, анализируем временные окна
89
+ if error_times:
90
+ error_times.sort(key=lambda x: x[0] if x[0] else datetime.min)
91
+
92
+ # Поиск кластеров ошибок во временных окнах
93
+ i = 0
94
+ while i < len(error_times):
95
+ cluster_start = error_times[i][0]
96
+ cluster_errors = [error_times[i][1]]
97
+
98
+ j = i + 1
99
+ while j < len(error_times) and error_times[j][0]:
100
+ time_diff = (error_times[j][0] - cluster_start).total_seconds()
101
+ if time_diff <= self.burst_time_window:
102
+ cluster_errors.append(error_times[j][1])
103
+ j += 1
104
+ else:
105
+ break
106
+
107
+ if len(cluster_errors) >= self.burst_threshold:
108
+ messages = [e.get('message', '')[:100] for e in cluster_errors[:3]]
109
+ anomalies.append({
110
+ 'type': 'BURST_ERRORS',
111
+ 'severity': 'HIGH',
112
+ 'description': f'Обнаружен всплеск из {len(cluster_errors)} ошибок в течение {self.burst_time_window} секунд',
113
+ 'count': len(cluster_errors),
114
+ 'time_window_seconds': self.burst_time_window,
115
+ 'sample_messages': messages,
116
+ 'first_occurrence': cluster_start.isoformat() if cluster_start else None,
117
+ 'metadata': {
118
+ 'threshold': self.burst_threshold,
119
+ 'affected_lines': [e.get('line_number') for e in cluster_errors[:10]]
120
+ }
121
+ })
122
+
123
+ i = j
124
+ else:
125
+ # Если временных меток нет, проверяем последовательные ошибки
126
+ consecutive_count = 0
127
+ start_idx = 0
128
+
129
+ for i, error in enumerate(errors):
130
+ if i > 0:
131
+ prev_error = errors[i - 1]
132
+ # Проверяем, являются ли ошибки последовательными (по номерам строк)
133
+ if error.get('line_number', 0) - prev_error.get('line_number', 0) <= 5:
134
+ consecutive_count += 1
135
+ else:
136
+ if consecutive_count >= self.burst_threshold:
137
+ anomalies.append(self._create_burst_anomaly(
138
+ errors[start_idx:i], consecutive_count + 1
139
+ ))
140
+ consecutive_count = 0
141
+ start_idx = i
142
+ else:
143
+ consecutive_count = 1
144
+
145
+ # Проверка последнего кластера
146
+ if consecutive_count >= self.burst_threshold:
147
+ anomalies.append(self._create_burst_anomaly(
148
+ errors[start_idx:], consecutive_count
149
+ ))
150
+
151
+ return anomalies
152
+
153
+ def _create_burst_anomaly(self, errors: List[Dict], count: int) -> Dict[str, Any]:
154
+ """Создаёт запись об аномалии burst errors."""
155
+ messages = [e.get('message', '')[:100] for e in errors[:3]]
156
+ return {
157
+ 'type': 'BURST_ERRORS',
158
+ 'severity': 'HIGH',
159
+ 'description': f'Обнаружен всплеск из {count} последовательных ошибок',
160
+ 'count': count,
161
+ 'sample_messages': messages,
162
+ 'metadata': {
163
+ 'threshold': self.burst_threshold,
164
+ 'affected_lines': [e.get('line_number') for e in errors[:10]]
165
+ }
166
+ }
167
+
168
+ def _detect_repeated_errors(self, errors: List[Dict]) -> List[Dict[str, Any]]:
169
+ """Обнаруживает повторяющиеся ошибки."""
170
+ anomalies = []
171
+
172
+ if not errors:
173
+ return anomalies
174
+
175
+ # Группировка ошибок по сообщениям (нормализованным)
176
+ error_groups = defaultdict(list)
177
+ for error in errors:
178
+ message = self._normalize_message(error.get('message', ''))
179
+ error_groups[message].append(error)
180
+
181
+ # Поиск повторяющихся ошибок
182
+ for message, error_list in error_groups.items():
183
+ if len(error_list) >= self.repeat_threshold:
184
+ line_numbers = [e.get('line_number') for e in error_list]
185
+ timestamps = [e.get('timestamp') for e in error_list if e.get('timestamp')]
186
+
187
+ anomalies.append({
188
+ 'type': 'REPEATED_ERRORS',
189
+ 'severity': 'MEDIUM',
190
+ 'description': f'Одна и та же ошибка повторяется {len(error_list)} раз(а)',
191
+ 'count': len(error_list),
192
+ 'error_message': message[:200],
193
+ 'first_occurrence': timestamps[0] if timestamps else None,
194
+ 'last_occurrence': timestamps[-1] if timestamps else None,
195
+ 'metadata': {
196
+ 'threshold': self.repeat_threshold,
197
+ 'affected_lines': line_numbers[:20]
198
+ }
199
+ })
200
+
201
+ return anomalies
202
+
203
+ def _detect_error_before_crash(self, errors: List[Dict], events: List[Dict]) -> List[Dict[str, Any]]:
204
+ """Обнаруживает паттерны "ошибка перед крашем"."""
205
+ anomalies = []
206
+
207
+ if not errors:
208
+ return anomalies
209
+
210
+ # Ищем последовательности критических ошибок в конце логов
211
+ # Или ошибки, за которыми следует остановка системы
212
+ crash_keywords = ['crash', 'shutdown', 'fatal', 'terminate', 'abort', 'exit']
213
+
214
+ # Проверяем последние события на наличие паттернов краша
215
+ last_events = events[-50:] if len(events) > 50 else events
216
+ last_errors = errors[-20:] if len(errors) > 20 else errors
217
+
218
+ for i, error in enumerate(last_errors):
219
+ error_msg_lower = error.get('message', '').lower()
220
+ error_level = error.get('level', '').upper()
221
+
222
+ # Проверяем, является ли это критической ошибкой
223
+ if error_level in ['CRITICAL', 'ERROR']:
224
+ # Проверяем последующие события на признаки краша
225
+ error_line = error.get('line_number', 0)
226
+ subsequent_events = [e for e in last_events if e.get('line_number', 0) > error_line][:10]
227
+
228
+ crash_indicators = []
229
+ for event in subsequent_events:
230
+ event_msg_lower = event.get('message', '').lower()
231
+ if any(keyword in event_msg_lower for keyword in crash_keywords):
232
+ crash_indicators.append(event.get('message', '')[:100])
233
+
234
+ if crash_indicators or i == len(last_errors) - 1:
235
+ anomalies.append({
236
+ 'type': 'ERROR_BEFORE_CRASH',
237
+ 'severity': 'CRITICAL',
238
+ 'description': 'Обнаружен паттерн: ошибка перед возможным крашем системы',
239
+ 'error_message': error.get('message', '')[:200],
240
+ 'error_level': error_level,
241
+ 'crash_indicators': crash_indicators[:3],
242
+ 'metadata': {
243
+ 'error_line': error_line,
244
+ 'is_last_error': i == len(last_errors) - 1
245
+ }
246
+ })
247
+
248
+ return anomalies
249
+
250
+ def _detect_temporal_spikes(self, events: List[Dict]) -> List[Dict[str, Any]]:
251
+ """Обнаруживает временные всплески событий."""
252
+ anomalies = []
253
+
254
+ # Группировка событий по времени (если доступны временные метки)
255
+ events_with_time = [(e.get('timestamp'), e) for e in events if e.get('timestamp')]
256
+
257
+ if len(events_with_time) < 10:
258
+ return anomalies
259
+
260
+ # Группировка по минутам (или другим временным окнам)
261
+ time_groups = defaultdict(list)
262
+ for timestamp_str, event in events_with_time:
263
+ try:
264
+ timestamp = self._parse_timestamp_simple(timestamp_str)
265
+ if timestamp:
266
+ # Группируем по минутам
267
+ time_key = timestamp.strftime('%Y-%m-%d %H:%M')
268
+ time_groups[time_key].append(event)
269
+ except:
270
+ pass
271
+
272
+ if not time_groups:
273
+ return anomalies
274
+
275
+ # Вычисляем среднее количество событий на временное окно
276
+ event_counts = [len(events) for events in time_groups.values()]
277
+ if not event_counts:
278
+ return anomalies
279
+
280
+ avg_count = sum(event_counts) / len(event_counts)
281
+ threshold = avg_count * 2 # Всплеск - это превышение среднего в 2 раза
282
+
283
+ # Поиск всплесков
284
+ for time_key, events_in_window in time_groups.items():
285
+ if len(events_in_window) > threshold:
286
+ error_count = len([e for e in events_in_window if e.get('level', '').upper() in ['ERROR', 'CRITICAL']])
287
+
288
+ anomalies.append({
289
+ 'type': 'TEMPORAL_SPIKE',
290
+ 'severity': 'MEDIUM',
291
+ 'description': f'Обнаружен временной всплеск: {len(events_in_window)} событий за период {time_key} (среднее: {avg_count:.1f})',
292
+ 'time_window': time_key,
293
+ 'event_count': len(events_in_window),
294
+ 'average_count': round(avg_count, 1),
295
+ 'error_count': error_count,
296
+ 'metadata': {
297
+ 'threshold_multiplier': 2.0
298
+ }
299
+ })
300
+
301
+ return anomalies
302
+
303
+ def _detect_repeated_stack_traces(self, events: List[Dict]) -> List[Dict[str, Any]]:
304
+ """Обнаруживает повторяющиеся stack traces."""
305
+ anomalies = []
306
+
307
+ # Ищем строки, похожие на stack traces
308
+ stack_trace_keywords = ['traceback', 'stack trace', 'at ', 'exception', 'file "', 'line ', 'in ']
309
+ potential_stacks = []
310
+
311
+ for event in events:
312
+ message = event.get('message', '').lower()
313
+ if any(keyword in message for keyword in stack_trace_keywords):
314
+ # Проверяем длину сообщения (stack traces обычно длинные)
315
+ if len(event.get('message', '')) > 100:
316
+ potential_stacks.append(event)
317
+
318
+ if len(potential_stacks) < self.repeat_threshold:
319
+ return anomalies
320
+
321
+ # Группировка по нормализованным сообщениям
322
+ stack_groups = defaultdict(list)
323
+ for stack in potential_stacks:
324
+ normalized = self._normalize_stack_trace(stack.get('message', ''))
325
+ stack_groups[normalized].append(stack)
326
+
327
+ # Поиск повторяющихся
328
+ for normalized_stack, stack_list in stack_groups.items():
329
+ if len(stack_list) >= self.repeat_threshold:
330
+ anomalies.append({
331
+ 'type': 'REPEATED_STACK_TRACES',
332
+ 'severity': 'HIGH',
333
+ 'description': f'Один и тот же stack trace повторяется {len(stack_list)} раз(а)',
334
+ 'count': len(stack_list),
335
+ 'stack_trace_preview': normalized_stack[:300],
336
+ 'metadata': {
337
+ 'threshold': self.repeat_threshold,
338
+ 'affected_lines': [s.get('line_number') for s in stack_list[:10]]
339
+ }
340
+ })
341
+
342
+ return anomalies
343
+
344
+ def _normalize_message(self, message: str) -> str:
345
+ """Нормализует сообщение для группировки (удаляет переменные части)."""
346
+ # Удаляем числа и даты
347
+ normalized = re.sub(r'\d+', 'N', message)
348
+ # Удаляем пути к файлам
349
+ normalized = re.sub(r'[A-Z]:\\[^\s]+|/[^\s]+', 'PATH', normalized)
350
+ # Удаляем URL
351
+ normalized = re.sub(r'https?://[^\s]+', 'URL', normalized)
352
+ return normalized.strip()
353
+
354
+ def _normalize_stack_trace(self, stack: str) -> str:
355
+ """Нормализует stack trace для сравнения."""
356
+ # Оставляем только ключевые части stack trace
357
+ lines = stack.split('\n')[:5] # Первые 5 строк обычно достаточны
358
+ normalized = '\n'.join([line.strip() for line in lines])
359
+ # Удаляем пути и номера строк
360
+ normalized = re.sub(r'File "[^"]+", line \d+', 'File "FILE", line N', normalized)
361
+ return normalized
362
+
363
+ def _parse_timestamp_simple(self, timestamp_str: str) -> datetime | None:
364
+ """Простой парсер временных меток."""
365
+ timestamp_str = timestamp_str.strip('[]')
366
+ formats = [
367
+ '%Y-%m-%d %H:%M:%S',
368
+ '%Y-%m-%dT%H:%M:%S',
369
+ '%Y-%m-%d %H:%M:%S.%f',
370
+ '%Y-%m-%dT%H:%M:%S.%f',
371
+ '%d/%m/%Y %H:%M:%S',
372
+ ]
373
+
374
+ for fmt in formats:
375
+ try:
376
+ return datetime.strptime(timestamp_str, fmt)
377
+ except ValueError:
378
+ continue
379
+
380
+ return None
381
+
382
+ def _calculate_anomaly_statistics(self, anomalies: List[Dict]) -> Dict[str, Any]:
383
+ """Вычисляет статистику аномалий."""
384
+ if not anomalies:
385
+ return {
386
+ 'total': 0,
387
+ 'by_type': {},
388
+ 'by_severity': {}
389
+ }
390
+
391
+ by_type = Counter(a.get('type') for a in anomalies)
392
+ by_severity = Counter(a.get('severity') for a in anomalies)
393
+
394
+ return {
395
+ 'total': len(anomalies),
396
+ 'by_type': dict(by_type),
397
+ 'by_severity': dict(by_severity)
398
+ }
399
+
400
+ def _calculate_severity_summary(self, anomalies: List[Dict]) -> Dict[str, int]:
401
+ """Вычисляет сводку по уровням серьёзности."""
402
+ severity_counts = Counter(a.get('severity', 'UNKNOWN') for a in anomalies)
403
+ return dict(severity_counts)
404
+
405
+ def _empty_report(self) -> Dict[str, Any]:
406
+ """Возвращает пустой отчёт при отсутствии данных."""
407
+ return {
408
+ 'anomalies': [],
409
+ 'statistics': {
410
+ 'total': 0,
411
+ 'by_type': {},
412
+ 'by_severity': {}
413
+ },
414
+ 'severity_summary': {}
415
+ }
agents/parser_agent.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Agent 1: Log Parser Agent
3
+ Преобразует сырые логи в структурированное представление.
4
+ """
5
+
6
+ import re
7
+ import json
8
+ from datetime import datetime
9
+ from typing import Dict, List, Any
10
+ from collections import defaultdict
11
+
12
+
13
+ class LogParserAgent:
14
+ """Парсит сырые логи и преобразует их в структурированный JSON."""
15
+
16
+ # Паттерны для распознавания уровней логирования
17
+ LOG_LEVELS = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE']
18
+
19
+ # Паттерны для временных меток (поддержка различных форматов)
20
+ TIMESTAMP_PATTERNS = [
21
+ r'\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}(?:\.\d+)?', # ISO 8601
22
+ r'\d{2}/\d{2}/\d{4}[\s]\d{2}:\d{2}:\d{2}', # DD/MM/YYYY HH:MM:SS
23
+ r'\[(?:[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\]', # [Mon Jan 1 12:00:00]
24
+ ]
25
+
26
+ def __init__(self):
27
+ """Инициализация агента."""
28
+ self.compiled_patterns = [re.compile(pattern) for pattern in self.TIMESTAMP_PATTERNS]
29
+
30
+ def parse(self, raw_logs: str) -> Dict[str, Any]:
31
+ """
32
+ Парсит сырые логи и возвращает структурированный JSON.
33
+
34
+ Args:
35
+ raw_logs: Строка с сырыми логами
36
+
37
+ Returns:
38
+ Структурированный JSON-объект с событиями, ошибками, предупреждениями и статистикой
39
+ """
40
+ if not raw_logs or not raw_logs.strip():
41
+ return self._empty_result()
42
+
43
+ lines = raw_logs.strip().split('\n')
44
+ events = []
45
+ errors = []
46
+ warnings = []
47
+
48
+ for line_num, line in enumerate(lines, start=1):
49
+ if not line.strip():
50
+ continue
51
+
52
+ parsed_event = self._parse_line(line, line_num)
53
+ if parsed_event:
54
+ events.append(parsed_event)
55
+
56
+ level = parsed_event.get('level', '').upper()
57
+ if level == 'ERROR' or level == 'CRITICAL':
58
+ errors.append(parsed_event)
59
+ elif level == 'WARNING':
60
+ warnings.append(parsed_event)
61
+
62
+ # Группировка по типам событий
63
+ event_types = defaultdict(int)
64
+ for event in events:
65
+ event_type = event.get('type', 'UNKNOWN')
66
+ event_types[event_type] += 1
67
+
68
+ # Статистика
69
+ statistics = {
70
+ 'total_lines': len(lines),
71
+ 'parsed_events': len(events),
72
+ 'errors': len(errors),
73
+ 'warnings': len(warnings),
74
+ 'info_messages': len([e for e in events if e.get('level', '').upper() == 'INFO']),
75
+ 'event_types': dict(event_types),
76
+ 'time_range': self._calculate_time_range(events),
77
+ }
78
+
79
+ return {
80
+ 'events': events,
81
+ 'errors': errors,
82
+ 'warnings': warnings,
83
+ 'statistics': statistics
84
+ }
85
+
86
+ def _parse_line(self, line: str, line_num: int) -> Dict[str, Any] | None:
87
+ """
88
+ Парсит одну строку лога.
89
+
90
+ Args:
91
+ line: Строка лога
92
+ line_num: Номер строки
93
+
94
+ Returns:
95
+ Словарь с распарсенными данными или None
96
+ """
97
+ # Поиск временной метки
98
+ timestamp = None
99
+ timestamp_str = None
100
+ for pattern in self.compiled_patterns:
101
+ match = pattern.search(line)
102
+ if match:
103
+ timestamp_str = match.group(0)
104
+ try:
105
+ # Попытка парсинга различных форматов
106
+ timestamp = self._parse_timestamp(timestamp_str)
107
+ except:
108
+ pass
109
+ break
110
+
111
+ # Поиск уровня логирования
112
+ level = None
113
+ for log_level in self.LOG_LEVELS:
114
+ if log_level in line.upper():
115
+ level = log_level
116
+ break
117
+
118
+ # Если уровень не найден, определяем по ключевым словам
119
+ if not level:
120
+ line_upper = line.upper()
121
+ if any(word in line_upper for word in ['ERROR', 'EXCEPTION', 'FAILED', 'FAILURE']):
122
+ level = 'ERROR'
123
+ elif any(word in line_upper for word in ['WARN', 'WARNING']):
124
+ level = 'WARNING'
125
+ elif any(word in line_upper for word in ['INFO', 'INFORMATION']):
126
+ level = 'INFO'
127
+ elif any(word in line_upper for word in ['DEBUG']):
128
+ level = 'DEBUG'
129
+ else:
130
+ level = 'INFO' # По умолчанию
131
+
132
+ # Извлечение сообщения (часть после временной метки и уровня)
133
+ message = line
134
+ if timestamp_str:
135
+ message = message.replace(timestamp_str, '', 1).strip()
136
+
137
+ # Определение типа события
138
+ event_type = self._detect_event_type(line)
139
+
140
+ return {
141
+ 'line_number': line_num,
142
+ 'timestamp': timestamp_str if timestamp_str else None,
143
+ 'level': level,
144
+ 'message': message.strip(),
145
+ 'type': event_type,
146
+ 'raw': line
147
+ }
148
+
149
+ def _parse_timestamp(self, timestamp_str: str) -> datetime | None:
150
+ """Парсит строку временной метки в объект datetime."""
151
+ # Удаление скобок если есть
152
+ timestamp_str = timestamp_str.strip('[]')
153
+
154
+ # Попытка различных форматов
155
+ formats = [
156
+ '%Y-%m-%d %H:%M:%S',
157
+ '%Y-%m-%dT%H:%M:%S',
158
+ '%Y-%m-%d %H:%M:%S.%f',
159
+ '%Y-%m-%dT%H:%M:%S.%f',
160
+ '%d/%m/%Y %H:%M:%S',
161
+ '%a %b %d %H:%M:%S %Y', # [Mon Jan 1 12:00:00 2024]
162
+ ]
163
+
164
+ for fmt in formats:
165
+ try:
166
+ return datetime.strptime(timestamp_str, fmt)
167
+ except ValueError:
168
+ continue
169
+
170
+ return None
171
+
172
+ def _detect_event_type(self, line: str) -> str:
173
+ """Определяет тип события по содержимому строки."""
174
+ line_lower = line.lower()
175
+
176
+ if any(keyword in line_lower for keyword in ['connection', 'connect', 'disconnect']):
177
+ return 'CONNECTION'
178
+ elif any(keyword in line_lower for keyword in ['request', 'response', 'http', 'api']):
179
+ return 'HTTP_REQUEST'
180
+ elif any(keyword in line_lower for keyword in ['database', 'db', 'query', 'sql']):
181
+ return 'DATABASE'
182
+ elif any(keyword in line_lower for keyword in ['authentication', 'auth', 'login', 'logout']):
183
+ return 'AUTHENTICATION'
184
+ elif any(keyword in line_lower for keyword in ['exception', 'error', 'failure']):
185
+ return 'EXCEPTION'
186
+ elif any(keyword in line_lower for keyword in ['start', 'stop', 'shutdown', 'initialized']):
187
+ return 'SYSTEM'
188
+ else:
189
+ return 'GENERAL'
190
+
191
+ def _calculate_time_range(self, events: List[Dict[str, Any]]) -> Dict[str, str] | None:
192
+ """Вычисляет временной диапазон событий."""
193
+ timestamps = [e.get('timestamp') for e in events if e.get('timestamp')]
194
+ if not timestamps:
195
+ return None
196
+
197
+ return {
198
+ 'start': timestamps[0],
199
+ 'end': timestamps[-1]
200
+ }
201
+
202
+ def _empty_result(self) -> Dict[str, Any]:
203
+ """Возвращает пустой результат при отсутствии логов."""
204
+ return {
205
+ 'events': [],
206
+ 'errors': [],
207
+ 'warnings': [],
208
+ 'statistics': {
209
+ 'total_lines': 0,
210
+ 'parsed_events': 0,
211
+ 'errors': 0,
212
+ 'warnings': 0,
213
+ 'info_messages': 0,
214
+ 'event_types': {},
215
+ 'time_range': None
216
+ }
217
+ }
agents/rca_agent.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Agent 3: Root Cause & Recommendation Agent
3
+ Интерпретирует аномалии и формирует рекомендации.
4
+ """
5
+
6
+ from typing import Dict, List, Any
7
+ import json
8
+
9
+
10
+ class RootCauseAgent:
11
+ """Анализирует аномалии и генерирует рекомендации."""
12
+
13
+ def __init__(self):
14
+ """Инициализация агента."""
15
+ self.root_cause_templates = self._init_root_cause_templates()
16
+ self.recommendation_templates = self._init_recommendation_templates()
17
+
18
+ def analyze(self, anomaly_report: Dict[str, Any]) -> str:
19
+ """
20
+ Анализирует отчёт об аномалиях и генерирует рекомендации.
21
+
22
+ Args:
23
+ anomaly_report: Отчёт об аномалиях от AnomalyDetectionAgent
24
+
25
+ Returns:
26
+ Markdown-текст с анализом и рекомендациями
27
+ """
28
+ if not anomaly_report or not anomaly_report.get('anomalies'):
29
+ return self._generate_no_anomalies_report()
30
+
31
+ anomalies = anomaly_report.get('anomalies', [])
32
+ statistics = anomaly_report.get('statistics', {})
33
+ severity_summary = anomaly_report.get('severity_summary', {})
34
+
35
+ # Генерация отчёта
36
+ report_parts = []
37
+
38
+ # Заголовок
39
+ report_parts.append("# Анализ первопричин и рекомендации\n")
40
+ report_parts.append(f"**Обнаружено аномалий:** {statistics.get('total', 0)}\n")
41
+
42
+ # Сводка по серьёзности
43
+ if severity_summary:
44
+ report_parts.append("\n## Сводка по уровням серьёзности\n")
45
+ severity_order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
46
+ for severity in severity_order:
47
+ count = severity_summary.get(severity, 0)
48
+ if count > 0:
49
+ emoji = self._get_severity_emoji(severity)
50
+ report_parts.append(f"- {emoji} **{severity}:** {count}\n")
51
+
52
+ # Группировка аномалий по типам
53
+ anomalies_by_type = {}
54
+ for anomaly in anomalies:
55
+ anomaly_type = anomaly.get('type', 'UNKNOWN')
56
+ if anomaly_type not in anomalies_by_type:
57
+ anomalies_by_type[anomaly_type] = []
58
+ anomalies_by_type[anomaly_type].append(anomaly)
59
+
60
+ # Анализ каждого типа аномалий
61
+ report_parts.append("\n## Детальный анализ аномалий\n")
62
+
63
+ for anomaly_type, type_anomalies in anomalies_by_type.items():
64
+ report_parts.append(f"\n### {self._get_anomaly_type_name(anomaly_type)}\n")
65
+
66
+ # Анализ первопричин
67
+ root_causes = self._identify_root_causes(anomaly_type, type_anomalies)
68
+ if root_causes:
69
+ report_parts.append("#### Возможные первопричины:\n")
70
+ for i, cause in enumerate(root_causes, 1):
71
+ report_parts.append(f"{i}. {cause}\n")
72
+
73
+ # Детали аномалий
74
+ report_parts.append("\n#### Детали:\n")
75
+ for i, anomaly in enumerate(type_anomalies[:5], 1): # Показываем до 5 примеров
76
+ severity = anomaly.get('severity', 'UNKNOWN')
77
+ description = anomaly.get('description', 'Без описания')
78
+ report_parts.append(f"**Аномалия {i}** ({severity}):\n")
79
+ report_parts.append(f"- {description}\n")
80
+
81
+ # Дополнительная информация
82
+ if anomaly.get('count'):
83
+ report_parts.append(f"- Количество: {anomaly.get('count')}\n")
84
+ if anomaly.get('error_message'):
85
+ error_msg = anomaly.get('error_message', '')[:150]
86
+ report_parts.append(f"- Сообщение: `{error_msg}`\n")
87
+ if anomaly.get('metadata'):
88
+ metadata = anomaly.get('metadata', {})
89
+ if metadata.get('affected_lines'):
90
+ lines = metadata.get('affected_lines', [])[:5]
91
+ report_parts.append(f"- Затронутые строки: {', '.join(map(str, lines))}\n")
92
+
93
+ if len(type_anomalies) > 5:
94
+ report_parts.append(f"\n*... и ещё {len(type_anomalies) - 5} аномалий этого типа*\n")
95
+
96
+ # Рекомендации
97
+ report_parts.append("\n## Рекомендации по устранению\n")
98
+ recommendations = self._generate_recommendations(anomalies)
99
+
100
+ for i, recommendation in enumerate(recommendations, 1):
101
+ priority = recommendation.get('priority', 'MEDIUM')
102
+ emoji = self._get_priority_emoji(priority)
103
+ report_parts.append(f"\n### {emoji} Рекомендация {i} (Приоритет: {priority})\n")
104
+ report_parts.append(f"{recommendation.get('text', '')}\n")
105
+
106
+ if recommendation.get('actions'):
107
+ report_parts.append("**Конкретные действия:**\n")
108
+ for action in recommendation.get('actions', []):
109
+ report_parts.append(f"- {action}\n")
110
+
111
+ # Общие рекомендации
112
+ report_parts.append("\n## Общие рекомендации\n")
113
+ general_recommendations = self._generate_general_recommendations(anomalies, statistics)
114
+ for rec in general_recommendations:
115
+ report_parts.append(f"- {rec}\n")
116
+
117
+ return ''.join(report_parts)
118
+
119
+ def _identify_root_causes(self, anomaly_type: str, anomalies: List[Dict]) -> List[str]:
120
+ """Определяет возможные первопричины для типа аномалий."""
121
+ causes = []
122
+
123
+ if anomaly_type == 'BURST_ERRORS':
124
+ causes.extend([
125
+ "Внезапная перегрузка системы или внешнего сервиса",
126
+ "Сбой в инфраструктуре (сеть, база данных, диск)",
127
+ "Проблемы с зависимыми сервисами или API",
128
+ "Некорректное обновление или развертывание кода"
129
+ ])
130
+ elif anomaly_type == 'REPEATED_ERRORS':
131
+ causes.extend([
132
+ "Проблема в коде, которая воспроизводится при определённых условиях",
133
+ "Недостаточная обработка ошибок в цикле или повторяющемся процессе",
134
+ "Проблема конфигурации, влияющая на конкретную функциональность",
135
+ "Ресурсные ограничения (память, диск, соединения)"
136
+ ])
137
+ elif anomaly_type == 'ERROR_BEFORE_CRASH':
138
+ causes.extend([
139
+ "Критическая ошибка, приводящая к падению процесса",
140
+ "Исчерпание ресурсов (память, дескрипторы файлов)",
141
+ "Некорректное состояние приложения после длительной работы",
142
+ "Проблемы с внешними зависимостями"
143
+ ])
144
+ elif anomaly_type == 'TEMPORAL_SPIKE':
145
+ causes.extend([
146
+ "Плановые задачи (cron jobs, scheduled tasks)",
147
+ "Резкое увеличение нагрузки от пользователей",
148
+ "Внешние события, вызывающие массовые запросы",
149
+ "Проблемы с кэшированием или сессиями"
150
+ ])
151
+ elif anomaly_type == 'REPEATED_STACK_TRACES':
152
+ causes.extend([
153
+ "Необработанное исключение в часто вызываемом коде",
154
+ "Проблема в библиотеке или зависимостях",
155
+ "Некорректные входные данные, вызывающие исключение",
156
+ "Race condition или проблема конкурентности"
157
+ ])
158
+ else:
159
+ causes.append("Требуется дополнительный анализ для определения первопричины")
160
+
161
+ return causes
162
+
163
+ def _generate_recommendations(self, anomalies: List[Dict]) -> List[Dict[str, Any]]:
164
+ """Генерирует рекомендации на основе обнаруженных аномалий."""
165
+ recommendations = []
166
+
167
+ # Группировка по типам для приоритизации
168
+ anomaly_types = [a.get('type') for a in anomalies]
169
+ severities = [a.get('severity') for a in anomalies]
170
+
171
+ has_critical = any(s == 'CRITICAL' for s in severities)
172
+ has_high = any(s == 'HIGH' for s in severities)
173
+ has_burst = 'BURST_ERRORS' in anomaly_types
174
+ has_crash = 'ERROR_BEFORE_CRASH' in anomaly_types
175
+
176
+ # Критические рекомендации
177
+ if has_crash:
178
+ recommendations.append({
179
+ 'priority': 'CRITICAL',
180
+ 'text': 'Обнаружены признаки возможн��го краша системы. Требуется немедленное внимание.',
181
+ 'actions': [
182
+ 'Проверить состояние системы и процессов',
183
+ 'Проанализировать последние ошибки перед крашем',
184
+ 'Убедиться, что мониторинг и алертинг настроены корректно',
185
+ 'Рассмотреть возможность отката последних изменений'
186
+ ]
187
+ })
188
+
189
+ if has_burst:
190
+ recommendations.append({
191
+ 'priority': 'HIGH',
192
+ 'text': 'Обнаружены всплески ошибок. Необходимо определить источник нагрузки.',
193
+ 'actions': [
194
+ 'Проверить метрики нагрузки (CPU, память, сеть)',
195
+ 'Изучить логи зависимых сервисов',
196
+ 'Проверить состояние базы данных и внешних API',
197
+ 'Рассмотреть возможность масштабирования или rate limiting'
198
+ ]
199
+ })
200
+
201
+ # Рекомендации по повторяющимся ошибкам
202
+ if 'REPEATED_ERRORS' in anomaly_types:
203
+ recommendations.append({
204
+ 'priority': 'HIGH',
205
+ 'text': 'Обнаружены повторяющиеся ошибки. Требуется исправление в коде или конфигурации.',
206
+ 'actions': [
207
+ 'Идентифицировать конкретный участок кода, вызывающий ошибку',
208
+ 'Добавить более детальное логирование для отладки',
209
+ 'Улучшить обработку ошибок с логированием контекста',
210
+ 'Провести code review проблемного участка'
211
+ ]
212
+ })
213
+
214
+ # Рекомендации по stack traces
215
+ if 'REPEATED_STACK_TRACES' in anomaly_types:
216
+ recommendations.append({
217
+ 'priority': 'MEDIUM',
218
+ 'text': 'Обнаружены повторяющиеся stack traces. Необходимо исправить необработанные исключения.',
219
+ 'actions': [
220
+ 'Найти и исправить источник исключения',
221
+ 'Добавить обработку исключений (try-except блоки)',
222
+ 'Улучшить валидацию входных данных',
223
+ 'Обновить проблемные библиотеки или зависимости'
224
+ ]
225
+ })
226
+
227
+ # Общие рекомендации по мониторингу
228
+ if has_high or has_critical:
229
+ recommendations.append({
230
+ 'priority': 'MEDIUM',
231
+ 'text': 'Улучшить систему мониторинга и алертинга для раннего обнаружения проблем.',
232
+ 'actions': [
233
+ 'Настроить алерты на критические ошибки',
234
+ 'Внедрить мониторинг метрик производительности',
235
+ 'Настроить дашборды для визуализации состояния системы',
236
+ 'Реализовать автоматические проверки здоровья (health checks)'
237
+ ]
238
+ })
239
+
240
+ return recommendations
241
+
242
+ def _generate_general_recommendations(self, anomalies: List[Dict], statistics: Dict) -> List[str]:
243
+ """Генерирует общие рекомендации."""
244
+ recommendations = []
245
+
246
+ total = statistics.get('total', 0)
247
+ if total == 0:
248
+ return ["Логи не содержат аномалий. Система работает стабильно."]
249
+
250
+ recommendations.append("Регулярно проверяйте логи на наличие паттернов и трендов")
251
+ recommendations.append("Настройте автоматическое уведомление о критических ошибках")
252
+
253
+ if total > 10:
254
+ recommendations.append("Обнаружено значительное количество аномалий - рекомендуется провести комплексный анализ системы")
255
+
256
+ recommendations.append("Ведите документацию по известным проблемам и их решениям")
257
+ recommendations.append("Рассмотрите возможность внедрения централизованного логирования (ELK, Splunk и т.д.)")
258
+
259
+ return recommendations
260
+
261
+ def _get_anomaly_type_name(self, anomaly_type: str) -> str:
262
+ """Возвращает читаемое название типа аномалии."""
263
+ names = {
264
+ 'BURST_ERRORS': 'Всплески ошибок',
265
+ 'REPEATED_ERRORS': 'Повторяющиеся ошибки',
266
+ 'ERROR_BEFORE_CRASH': 'Ошибки перед крашем',
267
+ 'TEMPORAL_SPIKE': 'Временные всплески',
268
+ 'REPEATED_STACK_TRACES': 'Повторяющиеся stack traces'
269
+ }
270
+ return names.get(anomaly_type, anomaly_type)
271
+
272
+ def _get_severity_emoji(self, severity: str) -> str:
273
+ """Возвращает emoji для уровня серьёзности."""
274
+ emoji_map = {
275
+ 'CRITICAL': '🔴',
276
+ 'HIGH': '🟠',
277
+ 'MEDIUM': '🟡',
278
+ 'LOW': '🟢'
279
+ }
280
+ return emoji_map.get(severity, '⚪')
281
+
282
+ def _get_priority_emoji(self, priority: str) -> str:
283
+ """Возвращает emoji для приоритета."""
284
+ emoji_map = {
285
+ 'CRITICAL': '🔴',
286
+ 'HIGH': '🟠',
287
+ 'MEDIUM': '🟡',
288
+ 'LOW': '🟢'
289
+ }
290
+ return emoji_map.get(priority, '⚪')
291
+
292
+ def _init_root_cause_templates(self) -> Dict[str, List[str]]:
293
+ """Инициализирует шаблоны первопричин."""
294
+ return {}
295
+
296
+ def _init_recommendation_templates(self) -> Dict[str, List[str]]:
297
+ """Инициализирует шаблоны рекомендаций."""
298
+ return {}
299
+
300
+ def _generate_no_anomalies_report(self) -> str:
301
+ """Генерирует отчёт, когда аномалий не обнаружено."""
302
+ return """# Анализ первопричин и рекомендации
303
+
304
+ ## Результаты анализа
305
+
306
+ **Обнаружено аномалий:** 0
307
+
308
+ ✅ **Система работает стабильно.** В логах не обнаружено значительных аномалий или паттернов, указывающих на проблемы.
309
+
310
+ ### Общие рекомендации
311
+
312
+ - Продолжайте регулярный мониторинг логов
313
+ - Поддерживайте текущий уровень логирования
314
+ - Настройте автоматические проверки для раннего обнаружения проблем
315
+ - Регулярно просматривайте метрики производительности
316
+ """
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio приложение для мультиагентной системы анализа логов.
3
+ Оркестрирует работу трёх агентов и предоставляет веб-интерфейс.
4
+ """
5
+
6
+ import gradio as gr
7
+ import json
8
+ from typing import Tuple, Dict, Any
9
+
10
+ from agents import LogParserAgent, AnomalyDetectionAgent, RootCauseAgent
11
+
12
+
13
+ class LogAnalysisOrchestrator:
14
+ """Оркестратор для последовательного выполнения агентов."""
15
+
16
+ def __init__(self):
17
+ """Инициализация оркестратора и агентов."""
18
+ self.parser_agent = LogParserAgent()
19
+ self.anomaly_agent = AnomalyDetectionAgent()
20
+ self.rca_agent = RootCauseAgent()
21
+
22
+ def analyze(self, raw_logs: str) -> Tuple[str, str, str]:
23
+ """
24
+ Выполняет полный цикл анализа логов через всех агентов.
25
+
26
+ Args:
27
+ raw_logs: Сырые логи в виде строки
28
+
29
+ Returns:
30
+ Кортеж из трёх строк:
31
+ - JSON с распарсенными логами
32
+ - JSON с обнаруженными аномалиями
33
+ - Markdown с рекомендациями
34
+ """
35
+ try:
36
+ # Валидация входных данных
37
+ if not raw_logs or not raw_logs.strip():
38
+ empty_json = json.dumps({"error": "Логи не предоставлены"}, ensure_ascii=False, indent=2)
39
+ return empty_json, empty_json, "# Ошибка\n\nЛоги не предоставлены для анализа."
40
+
41
+ # Agent 1: Парсинг логов
42
+ try:
43
+ structured_data = self.parser_agent.parse(raw_logs)
44
+ parsed_json = json.dumps(structured_data, ensure_ascii=False, indent=2)
45
+ except Exception as e:
46
+ error_msg = {"error": f"Ошибка парсинга логов: {str(e)}"}
47
+ parsed_json = json.dumps(error_msg, ensure_ascii=False, indent=2)
48
+ return parsed_json, parsed_json, f"# Ошибка\n\nОшибка на этапе парсинга: {str(e)}"
49
+
50
+ # Agent 2: Обнаружение аномалий
51
+ try:
52
+ anomaly_report = self.anomaly_agent.detect(structured_data)
53
+ anomalies_json = json.dumps(anomaly_report, ensure_ascii=False, indent=2)
54
+ except Exception as e:
55
+ error_msg = {"error": f"Ошибка обнаружения аномалий: {str(e)}"}
56
+ anomalies_json = json.dumps(error_msg, ensure_ascii=False, indent=2)
57
+ return parsed_json, anomalies_json, f"# Ошибка\n\nОшибка на этапе обнаружения аномалий: {str(e)}"
58
+
59
+ # Agent 3: Анализ первопричин и рекомендации
60
+ try:
61
+ recommendations_md = self.rca_agent.analyze(anomaly_report)
62
+ except Exception as e:
63
+ recommendations_md = f"# Ошибка\n\nОшибка на этапе анализа первопричин: {str(e)}"
64
+
65
+ return parsed_json, anomalies_json, recommendations_md
66
+
67
+ except Exception as e:
68
+ error_json = json.dumps({"error": f"Критическая ошибка: {str(e)}"}, ensure_ascii=False, indent=2)
69
+ return error_json, error_json, f"# Критическая ошибка\n\n{str(e)}"
70
+
71
+
72
+ # Глобальный экземпляр оркестратора
73
+ orchestrator = LogAnalysisOrchestrator()
74
+
75
+
76
+ def analyze_logs(raw_logs: str) -> Tuple[str, str, str]:
77
+ """
78
+ Обёртка для Gradio интерфейса.
79
+
80
+ Args:
81
+ raw_logs: Сырые логи из интерфейса
82
+
83
+ Returns:
84
+ Кортеж результатов для отображения
85
+ """
86
+ return orchestrator.analyze(raw_logs)
87
+
88
+
89
+ def create_interface():
90
+ """Создаёт и настраивает Gradio интерфейс."""
91
+
92
+ # Описание интерфейса
93
+ description = """
94
+ # 🔍 Мультиагентная система анализа логов
95
+
96
+ Система использует трёх независимых агентов для анализа логов:
97
+ 1. **Log Parser Agent** - парсит и структурирует логи
98
+ 2. **Anomaly Detection Agent** - обнаруживает аномалии и паттерны
99
+ 3. **Root Cause Agent** - анализирует первопричины и генерирует рекомендации
100
+
101
+ Вставьте логи в поле ниже или загрузите лог-файл, затем нажм��те "Анализировать".
102
+ """
103
+
104
+ # Создание интерфейса
105
+ with gr.Blocks(title="Multi-Agent Log Analysis") as app:
106
+ gr.Markdown(description)
107
+
108
+ with gr.Row():
109
+ with gr.Column(scale=1):
110
+ log_input = gr.Textbox(
111
+ label="Логи для анализа",
112
+ placeholder="Вставьте логи здесь или используйте кнопку загрузки ниже...",
113
+ lines=15,
114
+ max_lines=30
115
+ )
116
+
117
+ upload_btn = gr.UploadButton(
118
+ "📁 Загрузить лог-файл",
119
+ file_types=[".log", ".txt"],
120
+ file_count="single"
121
+ )
122
+
123
+ analyze_btn = gr.Button("🔍 Анализировать", variant="primary", size="lg")
124
+
125
+ # Обработчик загрузки файла
126
+ def load_file(file):
127
+ if file is None:
128
+ return ""
129
+ try:
130
+ with open(file.name, 'r', encoding='utf-8') as f:
131
+ content = f.read()
132
+ return content
133
+ except Exception as e:
134
+ return f"Ошибка чтения файла: {str(e)}"
135
+
136
+ upload_btn.upload(load_file, inputs=upload_btn, outputs=log_input)
137
+
138
+ with gr.Row():
139
+ with gr.Tabs():
140
+ with gr.Tab("📊 Распарсенные логи (JSON)"):
141
+ parsed_output = gr.JSON(
142
+ label="Структурированные данные"
143
+ )
144
+
145
+ with gr.Tab("⚠️ Обнаруженные аномалии (JSON)"):
146
+ anomalies_output = gr.JSON(
147
+ label="Отчёт об аномалиях"
148
+ )
149
+
150
+ with gr.Tab("💡 Анализ и рекомендации (Markdown)"):
151
+ recommendations_output = gr.Markdown(
152
+ label="Рекомендации по устранению проблем"
153
+ )
154
+
155
+ # Примеры логов для тестирования
156
+ gr.Markdown("### 📝 Примеры логов для тестирования")
157
+ with gr.Row():
158
+ example_logs = [
159
+ """2024-01-15 10:00:00 INFO Application started
160
+ 2024-01-15 10:00:05 INFO Database connection established
161
+ 2024-01-15 10:01:00 ERROR Connection timeout to external API
162
+ 2024-01-15 10:01:05 ERROR Connection timeout to external API
163
+ 2024-01-15 10:01:10 ERROR Connection timeout to external API
164
+ 2024-01-15 10:01:15 WARNING High memory usage detected: 85%
165
+ 2024-01-15 10:02:00 CRITICAL System crash detected
166
+ 2024-01-15 10:02:01 INFO Application shutdown""",
167
+
168
+ """[2024-01-15 14:30:00] INFO User authentication successful
169
+ [2024-01-15 14:30:01] DEBUG Request received: GET /api/users
170
+ [2024-01-15 14:30:02] ERROR Database query failed: connection lost
171
+ [2024-01-15 14:30:03] ERROR Database query failed: connection lost
172
+ [2024-01-15 14:30:04] ERROR Database query failed: connection lost
173
+ [2024-01-15 14:30:05] ERROR Database query failed: connection lost
174
+ [2024-01-15 14:30:06] WARNING Retrying database connection
175
+ [2024-01-15 14:30:10] INFO Database connection restored"""
176
+ ]
177
+
178
+ example_btn1 = gr.Button("Загрузить пример 1", size="sm")
179
+ example_btn2 = gr.Button("Загрузить пример 2", size="sm")
180
+
181
+ example_btn1.click(
182
+ lambda: example_logs[0],
183
+ outputs=log_input
184
+ )
185
+
186
+ example_btn2.click(
187
+ lambda: example_logs[1],
188
+ outputs=log_input
189
+ )
190
+
191
+ # Связывание кнопки анализа с обработчиком
192
+ analyze_btn.click(
193
+ fn=analyze_logs,
194
+ inputs=log_input,
195
+ outputs=[parsed_output, anomalies_output, recommendations_output]
196
+ )
197
+
198
+ # Информация о системе
199
+ gr.Markdown("""
200
+ ---
201
+ ### ℹ️ Информация о системе
202
+
203
+ - **Архитектура:** Мультиагентная система (3 независимых агента)
204
+ - **Платформа:** Hugging Face Spaces
205
+ - **Интерфейс:** Gradio
206
+ - **Поддержка:** До 10,000 строк логов
207
+ """)
208
+
209
+ return app
210
+
211
+
212
+ # Создание и запуск приложения
213
+ if __name__ == "__main__":
214
+ app = create_interface()
215
+ app.launch(server_name="0.0.0.0", server_port=7860, theme=gr.themes.Soft())
216
+ else:
217
+ # Для Hugging Face Spaces
218
+ app = create_interface()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0.0,<5.0.0
2
+ pillow>=11.0
run.bat ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ @echo off
2
+ echo Starting Multi-Agent Log Analysis System...
3
+ echo.
4
+ python app.py
5
+ pause
space_config.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Конфигурация для Hugging Face Spaces
2
+ # Эта конфигурация также может быть указана в README.md через фронт-матер
3
+
4
+ sdk: gradio
5
+ sdk_version: 4.44.0
6
+ app_file: app.py
7
+ title: Multi-Agent Log Analysis System
8
+ emoji: 🔥
9
+ colorFrom: gray
10
+ colorTo: indigo
11
+ pinned: false
test_large_logs.py ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Генератор больших тестовых лог-файлов и скрипт для тестирования системы.
3
+ """
4
+
5
+ import random
6
+ import os
7
+ from datetime import datetime, timedelta
8
+ from agents import LogParserAgent, AnomalyDetectionAgent, RootCauseAgent
9
+ import time
10
+
11
+ def generate_log_entry(timestamp, level, message_template, **kwargs):
12
+ """Генерирует одну запись лога."""
13
+ message = message_template.format(**kwargs)
14
+ return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n"
15
+
16
+ def generate_log_file_1():
17
+ """Лог-файл 1: Обычные логи с редкими ошибками (3000 строк)"""
18
+ lines = []
19
+ base_time = datetime(2024, 1, 15, 10, 0, 0)
20
+
21
+ messages = [
22
+ "User {user_id} logged in from IP {ip}",
23
+ "Request GET /api/users/{user_id} processed successfully",
24
+ "Database query executed in {time}ms",
25
+ "Cache hit for key: {key}",
26
+ "Request POST /api/data processed in {time}ms",
27
+ "Session {session_id} created",
28
+ "File {filename} uploaded successfully",
29
+ "Processing job {job_id} started",
30
+ "Background task {task_id} completed",
31
+ ]
32
+
33
+ error_messages = [
34
+ "Connection timeout to external API: {api_url}",
35
+ "Database connection lost, retrying...",
36
+ "Invalid token received from user {user_id}",
37
+ ]
38
+
39
+ for i in range(3000):
40
+ timestamp = base_time + timedelta(seconds=i * 2)
41
+
42
+ if i % 100 == 0: # Каждая 100-я строка - ошибка
43
+ level = random.choice(["ERROR", "WARNING"])
44
+ template = random.choice(error_messages)
45
+ message = template.format(
46
+ api_url=f"api-{random.randint(1,5)}.example.com",
47
+ user_id=random.randint(1000, 9999),
48
+ )
49
+ else:
50
+ level = "INFO"
51
+ template = random.choice(messages)
52
+ message = template.format(
53
+ user_id=random.randint(1000, 9999),
54
+ ip=f"192.168.{random.randint(1,255)}.{random.randint(1,255)}",
55
+ time=random.randint(10, 500),
56
+ key=f"cache_key_{random.randint(1,100)}",
57
+ session_id=f"session_{random.randint(10000,99999)}",
58
+ filename=f"file_{random.randint(1,1000)}.txt",
59
+ job_id=random.randint(1000, 9999),
60
+ task_id=random.randint(10000, 99999),
61
+ )
62
+
63
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
64
+
65
+ return ''.join(lines)
66
+
67
+ def generate_log_file_2():
68
+ """Лог-файл 2: Burst errors (5000 строк с всплеском ошибок)"""
69
+ lines = []
70
+ base_time = datetime(2024, 1, 15, 14, 0, 0)
71
+
72
+ for i in range(5000):
73
+ timestamp = base_time + timedelta(seconds=i)
74
+
75
+ # Всплеск ошибок между 2000-2050 строками
76
+ if 2000 <= i < 2050:
77
+ level = random.choice(["ERROR", "ERROR", "ERROR", "CRITICAL"])
78
+ message = f"Database connection failed: unable to connect to host db-{random.randint(1,3)}.internal"
79
+ elif 2050 <= i < 2060:
80
+ level = "WARNING"
81
+ message = f"High latency detected: {random.randint(5000, 15000)}ms response time"
82
+ else:
83
+ level = "INFO"
84
+ message = f"Request processed: {random.choice(['GET', 'POST', 'PUT'])} /api/v1/{random.choice(['users', 'data', 'files'])}"
85
+
86
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
87
+
88
+ return ''.join(lines)
89
+
90
+ def generate_log_file_3():
91
+ """Лог-файл 3: Повторяющиеся ошибки (4000 строк)"""
92
+ lines = []
93
+ base_time = datetime(2024, 1, 15, 16, 0, 0)
94
+
95
+ repeated_error = "Authentication failed for user admin@example.com: invalid credentials"
96
+
97
+ for i in range(4000):
98
+ timestamp = base_time + timedelta(seconds=i * 3)
99
+
100
+ # Одна и та же ошибка повторяется каждые 50 строк
101
+ if i % 50 == 0:
102
+ level = "ERROR"
103
+ message = repeated_error
104
+ elif i % 75 == 0:
105
+ level = "WARNING"
106
+ message = f"Rate limit approaching: {random.randint(80, 95)}% of limit used"
107
+ else:
108
+ level = "INFO"
109
+ message = f"HTTP {random.choice([200, 200, 200, 201, 304])} {random.choice(['GET', 'POST'])} /api/{random.choice(['users', 'orders', 'products'])}"
110
+
111
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
112
+
113
+ return ''.join(lines)
114
+
115
+ def generate_log_file_4():
116
+ """Лог-файл 4: Stack traces (3500 строк)"""
117
+ lines = []
118
+ base_time = datetime(2024, 1, 15, 18, 0, 0)
119
+
120
+ stack_trace = """Traceback (most recent call last):
121
+ File "/app/services/api.py", line {line}, in process_request
122
+ result = external_api.call(data)
123
+ File "/app/lib/external_api.py", line {line2}, in call
124
+ raise ConnectionError("Service unavailable")
125
+ ConnectionError: Service unavailable"""
126
+
127
+ for i in range(3500):
128
+ timestamp = base_time + timedelta(seconds=i * 2)
129
+
130
+ if i % 200 == 0:
131
+ level = "ERROR"
132
+ message = stack_trace.format(
133
+ line=random.randint(100, 500),
134
+ line2=random.randint(50, 300)
135
+ )
136
+ else:
137
+ level = random.choice(["INFO", "DEBUG"])
138
+ message = f"Processing request {random.randint(10000, 99999)}"
139
+
140
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
141
+
142
+ return ''.join(lines)
143
+
144
+ def generate_log_file_5():
145
+ """Лог-файл 5: Временные всплески (4500 строк)"""
146
+ lines = []
147
+ base_time = datetime(2024, 1, 16, 8, 0, 0)
148
+
149
+ for i in range(4500):
150
+ # Группируем по минутам для создания всплесков
151
+ timestamp = base_time + timedelta(minutes=i // 60, seconds=i % 60)
152
+
153
+ # Всплески в определённые минуты
154
+ minute = (i // 60) % 60
155
+ if minute in [5, 15, 25, 35, 45]:
156
+ # Много событий в эти минуты
157
+ level = random.choice(["INFO", "INFO", "INFO", "WARNING", "ERROR"])
158
+ message = f"High traffic: {random.randint(100, 1000)} requests/min"
159
+ else:
160
+ level = "INFO"
161
+ message = f"Normal traffic: {random.randint(10, 50)} requests/min"
162
+
163
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
164
+
165
+ return ''.join(lines)
166
+
167
+ def generate_log_file_6():
168
+ """Лог-файл 6: Ошибка перед крашем (3000 строк)"""
169
+ lines = []
170
+ base_time = datetime(2024, 1, 16, 12, 0, 0)
171
+
172
+ for i in range(3000):
173
+ timestamp = base_time + timedelta(seconds=i)
174
+
175
+ # Последние 50 строк - критические ошибки
176
+ if i >= 2950:
177
+ level = random.choice(["CRITICAL", "ERROR"])
178
+ messages = [
179
+ "Out of memory: cannot allocate additional resources",
180
+ "Fatal error: database connection pool exhausted",
181
+ "Critical: unable to process requests, system overloaded",
182
+ "ERROR: Service unavailable, shutting down",
183
+ ]
184
+ message = random.choice(messages)
185
+ elif i >= 2900:
186
+ level = "ERROR"
187
+ message = f"System resource exhaustion detected: memory usage {random.randint(95, 99)}%"
188
+ else:
189
+ level = random.choice(["INFO", "DEBUG"])
190
+ message = f"System operation: {random.choice(['cache_update', 'db_query', 'api_call'])}"
191
+
192
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
193
+
194
+ return ''.join(lines)
195
+
196
+ def generate_log_file_7():
197
+ """Лог-файл 7: Разнообразные форматы логов (4000 строк)"""
198
+ lines = []
199
+ base_time = datetime(2024, 1, 16, 14, 30, 0)
200
+
201
+ formats = [
202
+ "{timestamp} [{level}] {message}",
203
+ "[{timestamp}] {level}: {message}",
204
+ "{timestamp} {level} - {message}",
205
+ ]
206
+
207
+ for i in range(4000):
208
+ timestamp = base_time + timedelta(seconds=i * 2)
209
+ level = random.choice(["INFO", "WARNING", "ERROR", "DEBUG"])
210
+
211
+ if level == "ERROR" and i % 100 == 0:
212
+ message = f"Error processing transaction {random.randint(100000, 999999)}"
213
+ else:
214
+ message = f"Event {i}: {random.choice(['user_action', 'system_check', 'data_sync'])}"
215
+
216
+ fmt = random.choice(formats)
217
+ if fmt.startswith("["):
218
+ lines.append(fmt.format(
219
+ timestamp=timestamp.strftime('%Y-%m-%d %H:%M:%S'),
220
+ level=level,
221
+ message=message
222
+ ) + "\n")
223
+ else:
224
+ lines.append(fmt.format(
225
+ timestamp=timestamp.strftime('%Y-%m-%d %H:%M:%S'),
226
+ level=level,
227
+ message=message
228
+ ) + "\n")
229
+
230
+ return ''.join(lines)
231
+
232
+ def generate_log_file_8():
233
+ """Лог-файл 8: Смешанные паттерны (5000 строк)"""
234
+ lines = []
235
+ base_time = datetime(2024, 1, 17, 9, 0, 0)
236
+
237
+ for i in range(5000):
238
+ timestamp = base_time + timedelta(seconds=i)
239
+
240
+ # Разные паттерны в разных секциях
241
+ if 1000 <= i < 1100:
242
+ # Burst errors
243
+ level = "ERROR"
244
+ message = f"API endpoint /api/data failed: {random.choice(['timeout', '500', 'connection refused'])}"
245
+ elif 2000 <= i < 2100 and i % 10 == 0:
246
+ # Repeated errors
247
+ level = "ERROR"
248
+ message = "Validation error: email format is invalid"
249
+ elif 3000 <= i < 3050:
250
+ # Stack traces
251
+ level = "ERROR"
252
+ message = f"Exception in handler: ValueError at line {random.randint(1, 500)}"
253
+ elif i >= 4900:
254
+ # Error before crash
255
+ level = random.choice(["CRITICAL", "ERROR"])
256
+ message = "System failure: critical service unavailable"
257
+ else:
258
+ level = "INFO"
259
+ message = f"Normal operation: {random.choice(['request', 'response', 'cache', 'db'])} processed"
260
+
261
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
262
+
263
+ return ''.join(lines)
264
+
265
+ def generate_log_file_9():
266
+ """Лог-файл 9: Web server logs format (4500 строк)"""
267
+ lines = []
268
+ base_time = datetime(2024, 1, 17, 15, 0, 0)
269
+
270
+ ips = [f"192.168.{x}.{y}" for x in range(1, 10) for y in range(1, 50)]
271
+
272
+ for i in range(4500):
273
+ timestamp = base_time + timedelta(seconds=i)
274
+ ip = random.choice(ips)
275
+ method = random.choice(["GET", "POST", "PUT", "DELETE"])
276
+ endpoint = random.choice(["/api/users", "/api/orders", "/api/products", "/static/css", "/static/js"])
277
+ status = random.choice([200, 200, 200, 201, 404, 500, 503])
278
+
279
+ if status >= 500:
280
+ level = "ERROR"
281
+ elif status >= 400:
282
+ level = "WARNING"
283
+ else:
284
+ level = "INFO"
285
+
286
+ message = f'{ip} - - [{timestamp.strftime("%d/%b/%Y:%H:%M:%S")}] "{method} {endpoint} HTTP/1.1" {status} {random.randint(100, 5000)}'
287
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
288
+
289
+ return ''.join(lines)
290
+
291
+ def generate_log_file_10():
292
+ """Лог-файл 10: Application logs с метаданными (4000 строк)"""
293
+ lines = []
294
+ base_time = datetime(2024, 1, 18, 10, 0, 0)
295
+
296
+ for i in range(4000):
297
+ timestamp = base_time + timedelta(seconds=i * 2)
298
+
299
+ # Периодические проблемы
300
+ if i % 300 == 0:
301
+ level = "ERROR"
302
+ message = f"Service health check failed: service-{random.randint(1, 5)}.internal is down"
303
+ elif i % 150 == 0:
304
+ level = "WARNING"
305
+ message = f"Performance degradation: p95 latency increased to {random.randint(1000, 5000)}ms"
306
+ elif 3500 <= i < 3600:
307
+ # Проблемы перед концом
308
+ level = random.choice(["ERROR", "WARNING"])
309
+ message = f"Resource constraint: {random.choice(['CPU', 'Memory', 'Disk'])} usage critical"
310
+ else:
311
+ level = "INFO"
312
+ message = f"[thread-{random.randint(1, 20)}] Processing job {random.randint(10000, 99999)}: status={random.choice(['completed', 'in_progress'])}"
313
+
314
+ lines.append(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} {level} {message}\n")
315
+
316
+ return ''.join(lines)
317
+
318
+ def test_log_file(content, file_num):
319
+ """Тестирует обработку одного лог-файла."""
320
+ print(f"\n{'='*60}")
321
+ print(f"Testing log file {file_num}")
322
+ print(f"{'='*60}")
323
+
324
+ # Подсчёт строк
325
+ line_count = len(content.split('\n'))
326
+ print(f"Lines in file: {line_count}")
327
+
328
+ # Замер времени
329
+ start_time = time.time()
330
+
331
+ # Agent 1: Парсинг
332
+ parser = LogParserAgent()
333
+ parsed_start = time.time()
334
+ structured_data = parser.parse(content)
335
+ parsed_time = time.time() - parsed_start
336
+
337
+ events_count = len(structured_data.get('events', []))
338
+ errors_count = len(structured_data.get('errors', []))
339
+ warnings_count = len(structured_data.get('warnings', []))
340
+
341
+ print(f"\n[OK] Agent 1 (Parser): {parsed_time:.2f} sec")
342
+ print(f" - Events: {events_count}")
343
+ print(f" - Errors: {errors_count}")
344
+ print(f" - Warnings: {warnings_count}")
345
+
346
+ # Agent 2: Обнаружение аномалий
347
+ anomaly_start = time.time()
348
+ anomaly_agent = AnomalyDetectionAgent()
349
+ anomaly_report = anomaly_agent.detect(structured_data)
350
+ anomaly_time = time.time() - anomaly_start
351
+
352
+ anomalies_count = len(anomaly_report.get('anomalies', []))
353
+ print(f"\n[OK] Agent 2 (Anomaly Detection): {anomaly_time:.2f} sec")
354
+ print(f" - Anomalies detected: {anomalies_count}")
355
+
356
+ if anomalies_count > 0:
357
+ by_type = anomaly_report.get('statistics', {}).get('by_type', {})
358
+ for anomaly_type, count in by_type.items():
359
+ print(f" - {anomaly_type}: {count}")
360
+
361
+ # Agent 3: Анализ первопричин
362
+ rca_start = time.time()
363
+ rca_agent = RootCauseAgent()
364
+ recommendations = rca_agent.analyze(anomaly_report)
365
+ rca_time = time.time() - rca_start
366
+
367
+ print(f"\n[OK] Agent 3 (Root Cause Analysis): {rca_time:.2f} sec")
368
+ print(f" - Report size: {len(recommendations)} characters")
369
+
370
+ total_time = time.time() - start_time
371
+ print(f"\n[TIME] Total processing time: {total_time:.2f} sec")
372
+ print(f" Speed: {line_count / total_time:.0f} lines/sec")
373
+
374
+ return {
375
+ 'file_num': file_num,
376
+ 'lines': line_count,
377
+ 'events': events_count,
378
+ 'errors': errors_count,
379
+ 'warnings': warnings_count,
380
+ 'anomalies': anomalies_count,
381
+ 'parsed_time': parsed_time,
382
+ 'anomaly_time': anomaly_time,
383
+ 'rca_time': rca_time,
384
+ 'total_time': total_time
385
+ }
386
+
387
+ def main():
388
+ """Главная функция для генерации и тестирования."""
389
+ print("=" * 60)
390
+ print("ГЕНЕРАЦИЯ И ТЕСТИРОВАНИЕ БОЛЬШИХ ЛОГ-ФАЙЛОВ")
391
+ print("=" * 60)
392
+
393
+ # Создаём папку для тестовых файлов
394
+ test_dir = "test_logs"
395
+ os.makedirs(test_dir, exist_ok=True)
396
+
397
+ # Генераторы лог-файлов
398
+ generators = [
399
+ ("normal_logs.log", generate_log_file_1),
400
+ ("burst_errors.log", generate_log_file_2),
401
+ ("repeated_errors.log", generate_log_file_3),
402
+ ("stack_traces.log", generate_log_file_4),
403
+ ("temporal_spikes.log", generate_log_file_5),
404
+ ("error_before_crash.log", generate_log_file_6),
405
+ ("mixed_formats.log", generate_log_file_7),
406
+ ("mixed_patterns.log", generate_log_file_8),
407
+ ("web_server.log", generate_log_file_9),
408
+ ("application_metadata.log", generate_log_file_10),
409
+ ]
410
+
411
+ # Генерируем файлы
412
+ print(f"\n[GENERATING] Generating {len(generators)} test files...")
413
+ files_data = []
414
+
415
+ for filename, generator in generators:
416
+ filepath = os.path.join(test_dir, filename)
417
+ print(f" Generating: {filename}...", end=" ")
418
+ content = generator()
419
+ with open(filepath, 'w', encoding='utf-8') as f:
420
+ f.write(content)
421
+
422
+ line_count = len(content.split('\n'))
423
+ file_size = len(content.encode('utf-8')) / 1024 # KB
424
+ print(f"OK ({line_count} lines, {file_size:.1f} KB)")
425
+ files_data.append((filepath, content))
426
+
427
+ print(f"\n[SUCCESS] All files created in '{test_dir}' folder")
428
+
429
+ # Тестируем каждый файл
430
+ print(f"\n[TESTING] Starting tests...")
431
+ results = []
432
+
433
+ for i, (filepath, content) in enumerate(files_data, 1):
434
+ result = test_log_file(content, i)
435
+ results.append(result)
436
+
437
+ # Итоговая статистика
438
+ print(f"\n\n{'='*60}")
439
+ print("SUMMARY STATISTICS")
440
+ print(f"{'='*60}")
441
+ print(f"\n{'#':<3} {'Lines':<8} {'Time (sec)':<12} {'Lines/sec':<12} {'Anomalies':<10}")
442
+ print("-" * 60)
443
+
444
+ total_lines = 0
445
+ total_time = 0
446
+
447
+ for result in results:
448
+ speed = result['lines'] / result['total_time'] if result['total_time'] > 0 else 0
449
+ print(f"{result['file_num']:<3} {result['lines']:<8} {result['total_time']:<12.2f} {speed:<12.0f} {result['anomalies']:<10}")
450
+ total_lines += result['lines']
451
+ total_time += result['total_time']
452
+
453
+ print("-" * 60)
454
+ avg_speed = total_lines / total_time if total_time > 0 else 0
455
+ print(f"{'TOTAL':<3} {total_lines:<8} {total_time:<12.2f} {avg_speed:<12.0f}")
456
+
457
+ print(f"\n[SUCCESS] Testing completed!")
458
+ print(f" Total processed: {total_lines} lines in {total_time:.2f} seconds")
459
+ print(f" Average speed: {avg_speed:.0f} lines/sec")
460
+
461
+ # Проверка производительности
462
+ if total_time > 100: # Если больше 100 секунд для всех файлов
463
+ print(f"\n[WARNING] Total processing time exceeds 100 seconds")
464
+ else:
465
+ print(f"\n[OK] Performance is within normal range (<100 sec for all files)")
466
+
467
+ if __name__ == "__main__":
468
+ main()
test_logs/application_metadata.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/burst_errors.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/error_before_crash.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/mixed_formats.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/mixed_patterns.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/normal_logs.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/repeated_errors.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/stack_traces.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/temporal_spikes.log ADDED
The diff for this file is too large to render. See raw diff
 
test_logs/web_server.log ADDED
The diff for this file is too large to render. See raw diff