It's version 0.4
This commit is contained in:
+94
-88
@@ -6,13 +6,12 @@ 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:
|
||||
with open(log_file_path, "r", encoding="utf-8") as log:
|
||||
lines = log.readlines()
|
||||
except FileNotFoundError:
|
||||
return {"error": "Лог файл не найден"}
|
||||
@@ -24,27 +23,27 @@ def analyze_bot_logs(log_file_path="bot.log"):
|
||||
|
||||
# Основные счетчики
|
||||
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
|
||||
}
|
||||
"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,
|
||||
},
|
||||
}
|
||||
|
||||
# Временные метрики
|
||||
@@ -53,11 +52,11 @@ def analyze_bot_logs(log_file_path="bot.log"):
|
||||
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})'
|
||||
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:
|
||||
# Извлекаем временную метку
|
||||
@@ -72,86 +71,88 @@ def analyze_bot_logs(log_file_path="bot.log"):
|
||||
level_match = re.search(log_level_pattern, line)
|
||||
if level_match:
|
||||
level = level_match.group(1)
|
||||
stats['log_levels'][level] += 1
|
||||
stats["log_levels"][level] += 1
|
||||
|
||||
# Время обработки сообщений
|
||||
time_match = re.search(handling_time_pattern, line)
|
||||
if time_match and 'is handled' in 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"]["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
|
||||
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
|
||||
stats["user_commands"][group] += 1
|
||||
stats["groups"][group] += 1
|
||||
|
||||
# Проверки расписания
|
||||
if 'Проверяем расписание' in line:
|
||||
stats['schedule_checks'] += 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
|
||||
stats["groups"][group] += 1
|
||||
|
||||
# Изменения расписания
|
||||
if 'Изменения найдены' in line:
|
||||
stats['schedule_changes'] += 1
|
||||
if "Изменения найдены" in line:
|
||||
stats["schedule_changes"] += 1
|
||||
|
||||
# Ошибки расписания
|
||||
if 'Не удалось получить расписание' in line:
|
||||
stats['schedule_failures'] += 1
|
||||
if "Не удалось получить расписание" in line:
|
||||
stats["schedule_failures"] += 1
|
||||
|
||||
# Перезапуски бота
|
||||
if 'Бот запускается' in line:
|
||||
stats['restarts'] += 1
|
||||
if "Бот запускается" in line:
|
||||
stats["restarts"] += 1
|
||||
|
||||
# Сетевые ошибки
|
||||
if 'Failed to fetch updates' in line:
|
||||
stats['network_errors'] += 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
|
||||
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 "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 "[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 "[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)
|
||||
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["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)
|
||||
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
|
||||
|
||||
@@ -159,7 +160,7 @@ def analyze_bot_logs(log_file_path="bot.log"):
|
||||
def calculate_duration_hours(start_str, end_str):
|
||||
"""Вычисляет продолжительность в часах"""
|
||||
try:
|
||||
fmt = '%Y-%m-%d %H:%M:%S'
|
||||
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)
|
||||
@@ -169,20 +170,22 @@ def calculate_duration_hours(start_str, end_str):
|
||||
|
||||
def calculate_success_rate(stats):
|
||||
"""Рассчитывает процент успешных операций"""
|
||||
total_operations = stats['performance']['handling_count'] + sum(stats['errors'].values())
|
||||
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
|
||||
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:
|
||||
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']
|
||||
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)
|
||||
@@ -190,17 +193,17 @@ def calculate_uptime_percentage(stats):
|
||||
|
||||
def calculate_schedule_success_rate(stats):
|
||||
"""Рассчитывает процент успешных проверок расписания"""
|
||||
total_checks = stats['schedule_checks']
|
||||
total_checks = stats["schedule_checks"]
|
||||
if total_checks == 0:
|
||||
return 0
|
||||
successful_checks = total_checks - stats['schedule_failures']
|
||||
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:
|
||||
if "error" in stats:
|
||||
return f"❌ Ошибка анализа логов: {stats['error']}"
|
||||
|
||||
text = "📊 СТАТИСТИКА РАБОТЫ БОТА\n"
|
||||
@@ -213,7 +216,9 @@ def create_statistics_text(stats):
|
||||
text += f"• Строк в логе: {stats['total_lines']:,}\n\n"
|
||||
|
||||
# Производительность
|
||||
handling_times = stats.get("handling_times", []) # сохрани список в analyze_bot_logs
|
||||
handling_times = stats.get(
|
||||
"handling_times", []
|
||||
) # сохрани список в analyze_bot_logs
|
||||
median_time = statistics.median(handling_times) if handling_times else 0
|
||||
|
||||
text += "⚡ ПРОИЗВОДИТЕЛЬНОСТЬ:\n"
|
||||
@@ -223,11 +228,11 @@ def create_statistics_text(stats):
|
||||
text += f"• Успешных операций: {stats['success_rate']}%\n\n"
|
||||
|
||||
# Статус работы
|
||||
duration = stats['time_period'].get('duration_hours', 0)
|
||||
errors_total = sum(stats['errors'].values())
|
||||
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']
|
||||
restarts = stats["restarts"]
|
||||
mtbf = round(duration / restarts, 2) if restarts else duration
|
||||
|
||||
text += "🔄 СТАТУС РАБОТЫ:\n"
|
||||
@@ -251,9 +256,9 @@ def create_statistics_text(stats):
|
||||
text += f"• Браузера: {stats['browser_errors']}\n"
|
||||
|
||||
# Топ-3 ошибок
|
||||
if stats['errors']:
|
||||
if stats["errors"]:
|
||||
text += "• Топ ошибок:\n"
|
||||
for err, count in stats['errors'].most_common(3):
|
||||
for err, count in stats["errors"].most_common(3):
|
||||
text += f" - {err} ({count})\n"
|
||||
text += "\n"
|
||||
|
||||
@@ -262,9 +267,9 @@ def create_statistics_text(stats):
|
||||
text += f"• Команд: {sum(stats['user_commands'].values())}\n"
|
||||
text += f"• Групп: {len(stats['groups'])}\n"
|
||||
|
||||
if stats['groups']:
|
||||
if stats["groups"]:
|
||||
text += "• Топ групп:\n"
|
||||
for group, count in stats['groups'].most_common(3):
|
||||
for group, count in stats["groups"].most_common(3):
|
||||
text += f" - {group}: {count}\n"
|
||||
|
||||
return text
|
||||
@@ -272,13 +277,14 @@ def create_statistics_text(stats):
|
||||
|
||||
def create_statistics_file(stats):
|
||||
"""Создает временный файл с полной статистикой"""
|
||||
if 'error' in stats:
|
||||
if "error" in stats:
|
||||
return None
|
||||
|
||||
# Создаем временный файл
|
||||
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8',
|
||||
suffix='.json', delete=False) as f:
|
||||
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
|
||||
return temp_filename
|
||||
|
||||
+5
-4
@@ -21,9 +21,6 @@ def is_chat_spam(chat_id: int, state: BotState) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
from functools import wraps
|
||||
from aiogram import types
|
||||
|
||||
def admin_required(need_level: int):
|
||||
"""Декоратор для проверки прав администратора (0 = высший уровень)"""
|
||||
|
||||
@@ -41,12 +38,16 @@ def admin_required(need_level: int):
|
||||
return await func(message, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def saving(func):
|
||||
"""Декоратор для сохранения входящего сообщения"""
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(message: types.Message, *args, **kwargs):
|
||||
save_message(message.chat.id, message.message_id)
|
||||
return await func(message, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
return wrapper
|
||||
|
||||
+15
-9
@@ -1,11 +1,14 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
|
||||
async def get_macbook_battery_level():
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"pmset", "-g", "batt",
|
||||
"pmset",
|
||||
"-g",
|
||||
"batt",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
if process.returncode != 0:
|
||||
@@ -28,9 +31,13 @@ async def get_process_usage(pid=None):
|
||||
pid = os.getpid()
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"ps", "-p", str(pid), "-o", "%cpu,%mem,rss,comm",
|
||||
"ps",
|
||||
"-p",
|
||||
str(pid),
|
||||
"-o",
|
||||
"%cpu,%mem,rss,comm",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
if process.returncode != 0:
|
||||
@@ -47,19 +54,18 @@ async def get_process_usage(pid=None):
|
||||
"command": comm,
|
||||
"cpu_percent": float(cpu),
|
||||
"mem_percent": float(mem_percent),
|
||||
"rss_mb": int(rss_kb) / 1024 # переводим КБ → МБ
|
||||
"rss_mb": int(rss_kb) / 1024, # переводим КБ → МБ
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async def main():
|
||||
battery = await get_macbook_battery_level()
|
||||
usage = await get_process_usage()
|
||||
|
||||
print(f"🔋 Батарея: {battery}%")
|
||||
print(f"🖥 CPU: {usage['cpu_percent']}% | MEM: {usage['mem_percent']}% | RSS: {usage['rss_mb']:.2f} MB")
|
||||
print(
|
||||
f"🖥 CPU: {usage['cpu_percent']}% | MEM: {usage['mem_percent']}% | RSS: {usage['rss_mb']:.2f} MB"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user