От сложности к простоте: как я переписал систему логирования Discord-серверов и сделал её в 10 раз проще

от автора

Всем привет! В апреле я создавал сервис для логирования серверов в Discord. Моя идея была максимально простой: бот отвечает за отслеживание событий и запись логов в базу данных, а веб-интерфейс отображает их.

Первоначально я задумывал делать это через связку MySQL c ботом на Python и сайтом на PHP. Такая задумка работала и вышла в свет 11 февраля 2025 года. Но все оказалось не так гладко как я думал. 4 апреля проект получил обновление 2.0 где были мелкие исправления и редизайн веб-интерфейса.

Однако поддержка такой задумки оказалось слишком сложной. Связка MySQL + PHP + Python породила несколько конкретных проблем. Во-первых, схему базы приходилось держать синхронной сразу в двух местах: бот на Python писал данные, сайт на PHP их читал. Любое изменение структуры таблицы означало правки в двух кодовых базах на двух языках, и рассинхрон ловился только в рантайме. Во-вторых, развёртывание превращалось в квест. Пользователю нужно было поднять MySQL, настроить веб-сервер под PHP, прописать доступы и связать всё между собой. Для инструмента, который люди разворачивают у себя на сервере, это слишком высокий порог входа. В-третьих, дублировалась логика: форматирование одних и тех же данных писалось отдельно на Python (для записи) и на PHP (для вывода). В итоге все это привело к тому, что проект закрылся и был помещен в архив.

Однако идея не давала мне покоя — уж слишком полезный инструмент может получиться. И спустя 5 месяцев проект переродился: 27 сентября 2025 года свет увидел Discord Log Hub — бот или же целая система для логирования Discord сервера.

Новая архитектура

Стек проекта был радикально упрощен и вместо MySQL + PHP + Python используются связка Python + SQLite3. Почему SQLite3 вместо MySQL? Основная причина — один писатель и короткие чтения. Логгер пишет данные в базу и даже на активном сервере, объем будет небольшим. Веб-интерфейс в свою же очередь, читает данные из базы и получает их только тогда, когда пользователь заходит на сайт. Для такого сценария отдельный сервер под базу данных показался мне избыточным. Почему Python вместо PHP? Потому что бот уже изначально был написан на Python и унификация веб-части устраняет главную проблему — рассинхрон схемы.

Первая версия проекта имела установщики (setup.exe и setup), которые создавали: файл конфига, файл зависимостей и файл конфигурации для деплоя на один из хостингов. Однако в обновлении 1.1.0, которое вышло 3 июня 2026 года, я отказался от такой задумки в пользу docker compose, дабы упростить деплой с установкой и окончательно привести все к единой точке входа.

services:  discord-log-hub:    build: .    image: discord-log-hub:latest    container_name: discord-log-hub    restart: unless-stopped    env_file:      - .env    ports:      - "5000:5000"    volumes:      - ./data:/app/data

Отныне проект работает по гораздо более простой схеме:

[Discord] ↔ [Бот] ↔ [База данных] ↔ [Веб-интерфейс] ↔ [Пользователь]

А весь сервис может целиком запуститься одной командой:

python main.py

или

docker compose up -d

Что под капотом

  • Бот слушает события и обрабатывает их записывая в базу данных

  • Веб-интерфейс считывает записи с базы данных и отображает их в виде таблицы

Бот и веб-интерфейс — это два отдельных процесса, а не один. web.py на flask крутит свой event loop, bot.py на disnake — свой асинхронный, и держать их в одном процессе означало бы вручную сводить два цикла событий. Запуск двумя Popen разводит их по разным процессам: каждый живёт в своём цикле, а общаются они только через общий SQLite-файл:

if __name__ == "__main__":    try:        flask_proc = subprocess.Popen(["python", "web.py"])        bot_proc = subprocess.Popen(["python", "bot.py"])        flask_proc.wait()        bot_proc.wait()    except KeyboardInterrupt:        pass

Функционал логирования (каналы)

Имеется 5 направлений (или категорий) логирования:

  • Сообщения, редактирование и удаление

  • Участники, входы и выходы, баны и разбаны и смены никнеймов

  • Сервер, изменение ролей, редактирование каналов, голосовая активность и приглашения

  • Прочее, использование команд (бота), изменение эмодзи и стикеров.

Пример записи события с редактированием сообщения:

@commands.Cog.listener()async def on_message_edit(self, before: disnake.Message, after: disnake.Message):    if not self.is_enabled(before.guild.id) or (before.author.bot and self.should_ignore_bots(before.guild.id)):        return    cursor.execute("SELECT log_message_edit FROM settings WHERE guild_id = ?", (before.guild.id,))    if cursor.fetchone() is None or False:        return    if before.content == after.content:        return    cursor.execute("INSERT INTO logs_messages (message_id, channel_id, guild_id, user_id, username, action_type, old_content, new_content) VALUES (?, ?, ?, ?, ?, 'EDIT', ?, ?)", (before.id, before.channel.id, before.guild.id, before.author.id, f"{before.author.name}#{before.author.discriminator}", before.content, after.content))    connection.commit()

Остальные записи сделаны аналогичным образом.

Каждая категория имеет свои каналы, всего их 13.

Также, каждый канал логирования можно включить или отключить, изменить режим отображения сообщений: embed или обычные и настроить цвет embed сообщений.

Многопользовательский режим

Новая система также получила функцию мультиаккаунтности. Через бота можно создать или удалить пользователей, а также получить их список. При создании пользователя, ему в личное сообщение отправляется пароль, хеш которого хранится в базе данных.

Управление пользователями в боте

Управление пользователями в боте

У пользователей есть 2 уровня прав доступа:

  • 1 уровень — обычный пользователь, который может просматривать веб-интерфейс

  • 2 уровень — администратор, может создавать пользователей (через бота), удалять их (через бота и веб) и сбрасывать им пароль (через веб)

Внешний вид

Главная страница

Главная страница
Основная страница логов

Основная страница логов
Отображение настроек

Отображение настроек
Логи сервера

Логи сервера

Заключение

Упрощение стека полностью оправдало ожидания. Проект из сложного в поддержке монстра превратился в элегантное и при этом довольно простое решение. Вместо двух кодовых баз, я смог прийти к одной кодовой базе. Деплой стал возможен через одну команду, вместо настройки целых MySQL и веб серверов.

Проект полностью открыт: GitHub

Если проект получит популярность, я буду готов обновлять и улучшать его.


P.S. Если тема интересна — пишите в комментариях, о каких технических деталях хотели бы узнать подробнее

ссылка на оригинал статьи https://habr.com/ru/articles/1058506/