291 lines
11 KiB
Python
291 lines
11 KiB
Python
from collections import Counter
|
|
import re
|
|
from datetime import datetime
|
|
import statistics
|
|
import tempfile
|
|
import json
|
|
|
|
|
|
def analyze_bot_logs(log_file_path="bot.log"):
|
|
"""
|
|
Анализирует логи бота и создает детальную статистику
|
|
"""
|
|
try:
|
|
with open(log_file_path, "r", encoding="utf-8") as log:
|
|
lines = log.readlines()
|
|
except FileNotFoundError:
|
|
return {"error": "Лог файл не найден"}
|
|
except Exception as e:
|
|
return {"error": f"Ошибка чтения файла: {str(e)}"}
|
|
|
|
if not lines:
|
|
return {"error": "Лог файл пуст"}
|
|
|
|
# Основные счетчики
|
|
stats = {
|
|
"total_lines": len(lines),
|
|
"time_period": {},
|
|
"log_levels": Counter(),
|
|
"activities": Counter(),
|
|
"errors": Counter(),
|
|
"warnings": Counter(),
|
|
"user_commands": Counter(),
|
|
"groups": Counter(),
|
|
"restarts": 0,
|
|
"schedule_checks": 0,
|
|
"schedule_changes": 0,
|
|
"schedule_failures": 0,
|
|
"network_errors": 0,
|
|
"browser_errors": 0,
|
|
"telegram_errors": Counter(),
|
|
"performance": {
|
|
"avg_handling_time": 0,
|
|
"fastest_handling": float("inf"),
|
|
"slowest_handling": 0,
|
|
"handling_count": 0,
|
|
},
|
|
}
|
|
|
|
# Временные метрики
|
|
start_time = None
|
|
end_time = None
|
|
handling_times = []
|
|
|
|
# Регулярные выражения для парсинга
|
|
timestamp_pattern = r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})"
|
|
log_level_pattern = r"\[(INFO|WARNING|ERROR)\]"
|
|
handling_time_pattern = r"Duration (\d+) ms"
|
|
command_pattern = r"Команда /rasp от ([\d-]+), группа=([^,]+), дата=(\d+)"
|
|
schedule_pattern = r"Проверяем расписание для ([^ ]+) на (\d{2}\.\d{2}\.\d{4})"
|
|
|
|
for line in lines:
|
|
# Извлекаем временную метку
|
|
timestamp_match = re.search(timestamp_pattern, line)
|
|
if timestamp_match:
|
|
timestamp = timestamp_match.group(1)
|
|
if not start_time:
|
|
start_time = timestamp
|
|
end_time = timestamp
|
|
|
|
# Уровень логирования
|
|
level_match = re.search(log_level_pattern, line)
|
|
if level_match:
|
|
level = level_match.group(1)
|
|
stats["log_levels"][level] += 1
|
|
|
|
# Время обработки сообщений
|
|
time_match = re.search(handling_time_pattern, line)
|
|
if time_match and "is handled" in line:
|
|
handling_time = int(time_match.group(1))
|
|
handling_times.append(handling_time)
|
|
stats["performance"]["handling_count"] += 1
|
|
stats["performance"]["slowest_handling"] = max(
|
|
stats["performance"]["slowest_handling"], handling_time
|
|
)
|
|
stats["performance"]["fastest_handling"] = min(
|
|
stats["performance"]["fastest_handling"], handling_time
|
|
)
|
|
|
|
# Команды пользователей
|
|
cmd_match = re.search(command_pattern, line)
|
|
if cmd_match:
|
|
user_id, group, date_offset = cmd_match.groups()
|
|
stats["user_commands"][group] += 1
|
|
stats["groups"][group] += 1
|
|
|
|
# Проверки расписания
|
|
if "Проверяем расписание" in line:
|
|
stats["schedule_checks"] += 1
|
|
sched_match = re.search(schedule_pattern, line)
|
|
if sched_match:
|
|
group, date = sched_match.groups()
|
|
stats["groups"][group] += 1
|
|
|
|
# Изменения расписания
|
|
if "Изменения найдены" in line:
|
|
stats["schedule_changes"] += 1
|
|
|
|
# Ошибки расписания
|
|
if "Не удалось получить расписание" in line:
|
|
stats["schedule_failures"] += 1
|
|
|
|
# Перезапуски бота
|
|
if "Бот запускается" in line:
|
|
stats["restarts"] += 1
|
|
|
|
# Сетевые ошибки
|
|
if "Failed to fetch updates" in line:
|
|
stats["network_errors"] += 1
|
|
|
|
# Ошибки браузера
|
|
if "TargetClosedError" in line or "BrowserContext.close" in line:
|
|
stats["browser_errors"] += 1
|
|
|
|
# Ошибки Telegram API
|
|
if "Telegram server says" in line:
|
|
error_msg = line.split("Telegram server says - ")[-1].split(":")[0]
|
|
stats["telegram_errors"][error_msg] += 1
|
|
|
|
# Сбор ошибок и предупреждений
|
|
if "[ERROR]" in line:
|
|
error_msg = line.split("[ERROR]")[-1].strip()
|
|
stats["errors"][error_msg[:100]] += 1
|
|
|
|
if "[WARNING]" in line:
|
|
warning_msg = line.split("[WARNING]")[-1].strip()
|
|
stats["warnings"][warning_msg[:100]] += 1
|
|
|
|
# Расчет средней скорости обработки
|
|
if handling_times:
|
|
stats["performance"]["avg_handling_time"] = sum(handling_times) / len(
|
|
handling_times
|
|
)
|
|
|
|
# Период работы
|
|
if start_time and end_time:
|
|
stats["time_period"] = {
|
|
"start": start_time,
|
|
"end": end_time,
|
|
"duration_hours": calculate_duration_hours(start_time, end_time),
|
|
}
|
|
|
|
# Дополнительные метрики
|
|
stats["success_rate"] = calculate_success_rate(stats)
|
|
stats["uptime_percentage"] = calculate_uptime_percentage(stats)
|
|
stats["schedule_success_rate"] = calculate_schedule_success_rate(stats)
|
|
|
|
return stats
|
|
|
|
|
|
def calculate_duration_hours(start_str, end_str):
|
|
"""Вычисляет продолжительность в часах"""
|
|
try:
|
|
fmt = "%Y-%m-%d %H:%M:%S"
|
|
start = datetime.strptime(start_str, fmt)
|
|
end = datetime.strptime(end_str, fmt)
|
|
return round((end - start).total_seconds() / 3600, 2)
|
|
except:
|
|
return 0
|
|
|
|
|
|
def calculate_success_rate(stats):
|
|
"""Рассчитывает процент успешных операций"""
|
|
total_operations = stats["performance"]["handling_count"] + sum(
|
|
stats["errors"].values()
|
|
)
|
|
if total_operations == 0:
|
|
return 0
|
|
success_rate = (stats["performance"]["handling_count"] / total_operations) * 100
|
|
return round(success_rate, 2)
|
|
|
|
|
|
def calculate_uptime_percentage(stats):
|
|
"""Рассчитывает процент времени работы"""
|
|
if stats["time_period"].get("duration_hours", 0) == 0:
|
|
return 0
|
|
# Предполагаем, что каждый перезапуск занимает ~10 секунд
|
|
restart_downtime = stats["restarts"] * 10 / 3600
|
|
total_hours = stats["time_period"]["duration_hours"]
|
|
uptime_hours = total_hours - restart_downtime
|
|
uptime_percentage = (uptime_hours / total_hours) * 100
|
|
return round(uptime_percentage, 2)
|
|
|
|
|
|
def calculate_schedule_success_rate(stats):
|
|
"""Рассчитывает процент успешных проверок расписания"""
|
|
total_checks = stats["schedule_checks"]
|
|
if total_checks == 0:
|
|
return 0
|
|
successful_checks = total_checks - stats["schedule_failures"]
|
|
success_rate = (successful_checks / total_checks) * 100
|
|
return round(success_rate, 2)
|
|
|
|
|
|
def create_statistics_text(stats):
|
|
"""Создает текстовый отчет статистики с расширенными метриками"""
|
|
if "error" in stats:
|
|
return f"❌ Ошибка анализа логов: {stats['error']}"
|
|
|
|
text = "📊 СТАТИСТИКА РАБОТЫ БОТА\n"
|
|
text += "=" * 40 + "\n\n"
|
|
|
|
# Основная информация
|
|
text += "📈 ОБЩАЯ ИНФОРМАЦИЯ:\n"
|
|
text += f"• Период: {stats['time_period'].get('start', 'N/A')} - {stats['time_period'].get('end', 'N/A')}\n"
|
|
text += f"• Длительность: {stats['time_period'].get('duration_hours', 0)} ч\n"
|
|
text += f"• Строк в логе: {stats['total_lines']:,}\n\n"
|
|
|
|
# Производительность
|
|
handling_times = stats.get(
|
|
"handling_times", []
|
|
) # сохрани список в analyze_bot_logs
|
|
median_time = statistics.median(handling_times) if handling_times else 0
|
|
|
|
text += "⚡ ПРОИЗВОДИТЕЛЬНОСТЬ:\n"
|
|
text += f"• Среднее время: {stats['performance']['avg_handling_time']:.0f} мс\n"
|
|
text += f"• Медиана времени: {median_time:.0f} мс\n"
|
|
text += f"• Сообщений обработано: {stats['performance']['handling_count']}\n"
|
|
text += f"• Успешных операций: {stats['success_rate']}%\n\n"
|
|
|
|
# Статус работы
|
|
duration = stats["time_period"].get("duration_hours", 0)
|
|
errors_total = sum(stats["errors"].values())
|
|
errors_per_hour = round(errors_total / duration, 2) if duration else 0
|
|
|
|
restarts = stats["restarts"]
|
|
mtbf = round(duration / restarts, 2) if restarts else duration
|
|
|
|
text += "🔄 СТАТУС РАБОТЫ:\n"
|
|
text += f"• Перезапусков: {restarts}\n"
|
|
text += f"• Uptime: {stats['uptime_percentage']}%\n"
|
|
text += f"• MTBF (среднее время между перезапусками): {mtbf} ч\n"
|
|
text += f"• Ошибок в час: {errors_per_hour}\n\n"
|
|
|
|
# Расписание
|
|
text += "📅 РАСПИСАНИЕ:\n"
|
|
text += f"• Проверок: {stats['schedule_checks']}\n"
|
|
text += f"• Изменений: {stats['schedule_changes']}\n"
|
|
text += f"• Ошибок: {stats['schedule_failures']}\n"
|
|
text += f"• Успешных проверок: {stats['schedule_success_rate']}%\n\n"
|
|
|
|
# Ошибки
|
|
text += "🚨 ОШИБКИ:\n"
|
|
text += f"• Всего ошибок: {errors_total}\n"
|
|
text += f"• Предупреждений: {sum(stats['warnings'].values())}\n"
|
|
text += f"• Сетевых: {stats['network_errors']}\n"
|
|
text += f"• Браузера: {stats['browser_errors']}\n"
|
|
|
|
# Топ-3 ошибок
|
|
if stats["errors"]:
|
|
text += "• Топ ошибок:\n"
|
|
for err, count in stats["errors"].most_common(3):
|
|
text += f" - {err} ({count})\n"
|
|
text += "\n"
|
|
|
|
# Пользователи
|
|
text += "👥 АКТИВНОСТЬ:\n"
|
|
text += f"• Команд: {sum(stats['user_commands'].values())}\n"
|
|
text += f"• Групп: {len(stats['groups'])}\n"
|
|
|
|
if stats["groups"]:
|
|
text += "• Топ групп:\n"
|
|
for group, count in stats["groups"].most_common(3):
|
|
text += f" - {group}: {count}\n"
|
|
|
|
return text
|
|
|
|
|
|
def create_statistics_file(stats):
|
|
"""Создает временный файл с полной статистикой"""
|
|
if "error" in stats:
|
|
return None
|
|
|
|
# Создаем временный файл
|
|
with tempfile.NamedTemporaryFile(
|
|
mode="w", encoding="utf-8", suffix=".json", delete=False
|
|
) as f:
|
|
json.dump(stats, f, ensure_ascii=False, indent=2, default=str)
|
|
temp_filename = f.name
|
|
|
|
return temp_filename
|