From 193099fdcce4ab2fe0bc9c82ef44e3fb79202c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A0=D0=B5=D0=BF?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Fri, 3 Apr 2026 20:58:27 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?/=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PythonApplication1.py | 228 ++++++++++++++++++++++++++++++++++++++ PythonApplication1.pyproj | 35 ++++++ PythonApplication1.sln | 23 ++++ server_logs.txt | 12 ++ summary.txt | 19 ++++ 5 files changed, 317 insertions(+) create mode 100644 PythonApplication1.py create mode 100644 PythonApplication1.pyproj create mode 100644 PythonApplication1.sln create mode 100644 server_logs.txt create mode 100644 summary.txt diff --git a/PythonApplication1.py b/PythonApplication1.py new file mode 100644 index 0000000..2894e46 --- /dev/null +++ b/PythonApplication1.py @@ -0,0 +1,228 @@ +import os +from datetime import datetime +from collections import defaultdict + +# === 10 функций === + +def load_logs(file_path: str) -> list[dict]: + """Читает файл с логами, преобразует каждую строку в словарь""" + logs = [] + try: + with open(file_path, 'r', encoding='utf-8') as file: + for line in file: + line = line.strip() + if not line: + continue + + # Парсинг строки лога (формат Nginx/Common Log Format) + parts = line.split(' ') + if len(parts) < 10: + continue + + ip = parts[0] + timestamp = parts[3][1:] # убираем '[' + method = parts[5][1:] # убираем '"' + url = parts[6] + status = int(parts[8]) + size = int(parts[9]) + + log_entry = { + 'ip': ip, + 'timestamp': timestamp, + 'method': method, + 'url': url, + 'status': status, + 'size': size + } + logs.append(log_entry) + except FileNotFoundError: + print(f"Ошибка: Файл {file_path} не найден") + return [] + + return logs + + +def filter_by_status(logs: list[dict], status_code: int) -> list[dict]: + """Возвращает записи с заданным статус-кодом""" + return [log for log in logs if log['status'] == status_code] + + +def filter_by_ip_prefix(logs: list[dict], prefix: str) -> list[dict]: + """Фильтрует записи по началу IP-адреса""" + return [log for log in logs if log['ip'].startswith(prefix)] + + +def get_unique_ips(logs: list[dict]) -> list[str]: + """Возвращает отсортированный список уникальных IP-адресов""" + unique_ips = set(log['ip'] for log in logs) + return sorted(unique_ips) + + +def count_requests_by_method(logs: list[dict]) -> dict[str, int]: + """Подсчитывает количество запросов по каждому HTTP-методу""" + method_count = defaultdict(int) + for log in logs: + method_count[log['method']] += 1 + return dict(method_count) + + +def find_top_urls(logs: list[dict], n: int) -> list[tuple[str, int]]: + """Возвращает топ-N самых часто запрашиваемых URL""" + url_count = defaultdict(int) + for log in logs: + url_count[log['url']] += 1 + + sorted_urls = sorted(url_count.items(), key=lambda x: x[1], reverse=True) + return sorted_urls[:n] + + +def get_avg_response_size(logs: list[dict]) -> float: + """Вычисляет средний размер ответа""" + if not logs: + return 0.0 + + total_size = sum(log['size'] for log in logs) + return total_size / len(logs) + + +def filter_by_date_range(logs: list[dict], start: str, end: str) -> list[dict]: + """Фильтрует логи по временному диапазону""" + def parse_date(date_str: str) -> datetime: + # Формат: 02/Jan/2025:13:45:12 + return datetime.strptime(date_str, "%d/%b/%Y:%H:%M:%S") + + start_date = parse_date(start) + end_date = parse_date(end) + + filtered = [] + for log in logs: + try: + log_date = parse_date(log['timestamp']) + if start_date <= log_date <= end_date: + filtered.append(log) + except: + continue + + return filtered + + +def group_by_hour(logs: list[dict]) -> dict[int, int]: + """Группирует количество запросов по часам суток""" + hour_count = defaultdict(int) + + for log in logs: + try: + # Извлекаем час из временной метки + timestamp = log['timestamp'] + # Формат: 02/Jan/2025:13:45:12 + hour = int(timestamp.split(':')[0].split('/')[-1].split(':')[0]) + hour_count[hour] += 1 + except: + continue + + return dict(hour_count) + + +def generate_report(logs: list[dict]) -> str: + """Формирует текстовый отчёт""" + if not logs: + return "Нет данных для отчёта" + + # Используем другие функции + method_stats = count_requests_by_method(logs) + top_urls = find_top_urls(logs, 3) + avg_size = get_avg_response_size(logs) + hourly_stats = group_by_hour(logs) + + # Формируем отчёт + report = "=" * 50 + "\n" + report += "ОТЧЁТ ПО ЛОГАМ ВЕБ-СЕРВЕРА\n" + report += "=" * 50 + "\n\n" + + report += "1. СТАТИСТИКА ПО HTTP-МЕТОДАМ:\n" + for method, count in sorted(method_stats.items()): + report += f" {method}: {count} запросов\n" + + report += "\n2. ТОП-3 САМЫХ ЧАСТЫХ URL:\n" + for i, (url, count) in enumerate(top_urls, 1): + report += f" {i}. {url} - {count} раз(а)\n" + + report += f"\n3. СРЕДНИЙ РАЗМЕР ОТВЕТА: {avg_size:.2f} байт\n" + + report += "\n4. РАСПРЕДЕЛЕНИЕ ПО ЧАСАМ:\n" + for hour in sorted(hourly_stats.keys()): + report += f" {hour:02d}:00 - {hourly_stats[hour]} запросов\n" + + report += "\n" + "=" * 50 + "\n" + + return report + + +# === Функция main для демонстрации === + +def main(): + # 1. Загружаем логи + logs = load_logs("data/server_logs.txt") + if not logs: + print("Не удалось загрузить логи. Проверьте наличие файла data/server_logs.txt") + return + + print("=" * 60) + print("АНАЛИЗ ЛОГОВ ВЕБ-СЕРВЕРА") + print("=" * 60) + + # 2. Общее количество записей + print(f"\n📊 Общее количество записей: {len(logs)}") + + # 3. Уникальные IP + unique_ips = get_unique_ips(logs) + print(f"🌐 Количество уникальных IP: {len(unique_ips)}") + print(f" Примеры: {unique_ips[:3]}") + + # 4. Фильтрация по статусу 404 + errors_404 = filter_by_status(logs, 404) + print(f"\n❌ Количество ошибок 404: {len(errors_404)}") + + # 5. Топ-2 URL с ошибками 500 + errors_500 = filter_by_status(logs, 500) + top_500_urls = find_top_urls(errors_500, 2) + print(f"⚠️ Топ-2 URL с ошибками 500:") + for url, count in top_500_urls: + print(f" - {url}: {count} раз(а)") + + # 6. Фильтрация по префиксу IP (пример) + local_ips = filter_by_ip_prefix(logs, "192.168.") + print(f"\n🏠 Запросы с локальных IP (192.168.*): {len(local_ips)}") + + # 7. Средний размер ответа + avg_size = get_avg_response_size(logs) + print(f"📏 Средний размер ответа: {avg_size:.2f} байт") + + # 8. Фильтрация по дате (пример) + date_filtered = filter_by_date_range(logs, "01/Jan/2025:00:00:00", "31/Jan/2025:23:59:59") + print(f"📅 Запросы за январь 2025: {len(date_filtered)}") + + # 9. Группировка по часам + hourly = group_by_hour(logs) + peak_hour = max(hourly, key=hourly.get) + print(f"⏰ Пиковый час: {peak_hour}:00 ({hourly[peak_hour]} запросов)") + + # 10. Генерация отчёта + report = generate_report(logs) + + # Создаём директорию для отчётов + os.makedirs("reports", exist_ok=True) + + # Сохраняем отчёт в файл + with open("reports/summary.txt", "w", encoding="utf-8") as f: + f.write(report) + + print(f"\n✅ Полный отчёт сохранён в файл: reports/summary.txt") + print("\n" + "=" * 60) + + +# === Запуск === + +if __name__ == "__main__": + # Запускаем анализ + main() diff --git a/PythonApplication1.pyproj b/PythonApplication1.pyproj new file mode 100644 index 0000000..6f92fcd --- /dev/null +++ b/PythonApplication1.pyproj @@ -0,0 +1,35 @@ + + + Debug + 2.0 + 57327018-5847-4f0c-a393-7543eefa27d5 + . + PythonApplication1.py + + + . + . + PythonApplication1 + PythonApplication1 + + + true + false + + + true + false + + + + + + + + + + + + \ No newline at end of file diff --git a/PythonApplication1.sln b/PythonApplication1.sln new file mode 100644 index 0000000..0744e66 --- /dev/null +++ b/PythonApplication1.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36908.2 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "PythonApplication1", "PythonApplication1.pyproj", "{57327018-5847-4F0C-A393-7543EEFA27D5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57327018-5847-4F0C-A393-7543EEFA27D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57327018-5847-4F0C-A393-7543EEFA27D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6F538869-9B3B-4C48-A68A-E7622181080C} + EndGlobalSection +EndGlobal diff --git a/server_logs.txt b/server_logs.txt new file mode 100644 index 0000000..1614d36 --- /dev/null +++ b/server_logs.txt @@ -0,0 +1,12 @@ +192.168.1.10 - - [02/Jan/2025:13:45:12 +0000] "GET /index.html HTTP/1.1" 200 1024 +192.168.1.11 - - [02/Jan/2025:13:46:23 +0000] "POST /api/login HTTP/1.1" 200 512 +192.168.1.10 - - [02/Jan/2025:13:47:01 +0000] "GET /about.html HTTP/1.1" 404 256 +10.0.0.1 - - [02/Jan/2025:14:10:34 +0000] "GET /index.html HTTP/1.1" 200 1024 +192.168.1.12 - - [02/Jan/2025:14:15:45 +0000] "GET /products.html HTTP/1.1" 500 128 +192.168.1.10 - - [02/Jan/2025:14:20:56 +0000] "GET /index.html HTTP/1.1" 200 1024 +10.0.0.2 - - [02/Jan/2025:15:05:12 +0000] "POST /api/data HTTP/1.1" 201 2048 +192.168.1.11 - - [02/Jan/2025:15:30:22 +0000] "GET /index.html HTTP/1.1" 200 1024 +192.168.1.10 - - [02/Jan/2025:16:00:00 +0000] "GET /admin.html HTTP/1.1" 403 512 +10.0.0.1 - - [02/Jan/2025:16:15:33 +0000] "GET /products.html HTTP/1.1" 500 128 +192.168.1.13 - - [03/Jan/2025:09:00:00 +0000] "GET /index.html HTTP/1.1" 200 1024 +192.168.1.10 - - [03/Jan/2025:10:30:45 +0000] "GET /api/users HTTP/1.1" 200 2048 diff --git a/summary.txt b/summary.txt new file mode 100644 index 0000000..a5e249c --- /dev/null +++ b/summary.txt @@ -0,0 +1,19 @@ +================================================== +ОТЧЁТ ПО ЛОГАМ ВЕБ-СЕРВЕРА +================================================== + +1. СТАТИСТИКА ПО HTTP-МЕТОДАМ: + GET: 10 запросов + POST: 2 запросов + +2. ТОП-3 САМЫХ ЧАСТЫХ URL: + 1. /index.html - 5 раз(а) + 2. /products.html - 2 раз(а) + 3. /api/login - 1 раз(а) + +3. СРЕДНИЙ РАЗМЕР ОТВЕТА: 896.00 байт + +4. РАСПРЕДЕЛЕНИЕ ПО ЧАСАМ: + 2025:00 - 12 запросов + +==================================================