add personal logging

This commit is contained in:
Oleg Oleg 2025-12-19 22:59:41 +04:00
parent 9bf50b3772
commit a1933c7b14
4 changed files with 61 additions and 30 deletions

2
.gitignore vendored
View File

@ -13,3 +13,5 @@ conversations.pkl
*.png *.png
*.jpeg *.jpeg
.vscode/ .vscode/
data/actors
examples.ini

View File

@ -28,7 +28,7 @@ RUN poetry config virtualenvs.create false \
COPY src/ ./src/ COPY src/ ./src/
# Create directories for bot persistence # Create directories for bot persistence
RUN mkdir -p /app/data RUN mkdir -p /app/data/actors
# Create non-root user # Create non-root user
RUN groupadd -r botuser && useradd -r -g botuser botuser RUN groupadd -r botuser && useradd -r -g botuser botuser

View File

@ -2,6 +2,7 @@ import logging
import sys import sys
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from typing import Sequence from typing import Sequence
from threading import Lock
from core.config import settings from core.config import settings
@ -24,10 +25,49 @@ class ExtendedLogger(logging.Logger):
formatted = ", ".join(items) formatted = ", ".join(items)
self.info(f"{message}: {formatted}") self.info(f"{message}: {formatted}")
class ActorLogger(logging.Logger):
def __init__(self):
self.__loggers: dict[str, logging.Logger] = {}
self.lock = Lock()
def log(self, actor_id: int, level: int, msg: str):
logger = self._get_or_create_logger(actor_id)
logger.log(level, msg)
def info(self, actor_id: int, msg: str):
self.log(actor_id, logging.INFO, msg)
def error(self, actor_id: int, msg: str):
self.log(actor_id, logging.ERROR, msg)
def warning(self, actor_id: int, msg: str):
self.log(actor_id, logging.WARNING, msg)
def _get_or_create_logger(self, actor_id: str):
with self.lock:
if actor_id not in self.__loggers:
logger = logging.getLogger(f"actor_{actor_id}")
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
filename=f"/app/data/actors/actor_{actor_id}.log",
maxBytes=5 * 1024 * 1024,
backupCount=5,
encoding='utf-8'
)
formatter = logging.Formatter(
fmt="%(asctime)s %(levelname)s [%(name)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
logger.addHandler(handler)
self.__loggers[actor_id] = logger
return self.__loggers[actor_id]
def setup_logging(): def setup_logging():
""" """
Инициализирует логирование для всего приложения. Инициализирует логирование для всего приложения.
Конфигурирует консольный и файловый логгеры с ротацией. Конфигурирует консольный логгер.
""" """
# Используем наш кастомный класс логгера # Используем наш кастомный класс логгера
@ -52,18 +92,6 @@ def setup_logging():
# Собираем хэндлеры # Собираем хэндлеры
handlers = [console_handler] handlers = [console_handler]
# Файловый хэндлер с ротацией (если указан путь в настройках)
if hasattr(settings, 'log_file_path') and settings.log_file_path:
file_handler = RotatingFileHandler(
filename=settings.log_file_path,
maxBytes=10 * 1024 * 1024, # 10 MB
backupCount=5,
encoding="utf-8"
)
file_handler.setLevel(log_level)
file_handler.setFormatter(formatter)
handlers.append(file_handler)
# Конфигурируем корневой логгер # Конфигурируем корневой логгер
root_logger = logging.getLogger() root_logger = logging.getLogger()
root_logger.setLevel(log_level) root_logger.setLevel(log_level)

View File

@ -20,7 +20,7 @@ from docbot.services.doctors_service import (
get_doctors_payment_link, get_doctor_by_code get_doctors_payment_link, get_doctor_by_code
) )
from docbot.services.session_service import create_session from docbot.services.session_service import create_session
from core.logging import logger from core.logging import logger, ActorLogger
from core.exceptions import DatabaseError from core.exceptions import DatabaseError
from core.enums.dialog_helpers import ConfirmationMessage from core.enums.dialog_helpers import ConfirmationMessage
from core.utils import is_phone_correct, make_a_payment_link from core.utils import is_phone_correct, make_a_payment_link
@ -34,6 +34,7 @@ ENTER_DOCTOR_NUMBER = 5
ENTER_CONSULTATION_DATE = 6 ENTER_CONSULTATION_DATE = 6
PAY_CONSULTATION = 7 PAY_CONSULTATION = 7
ERROR_PHONE_NUMBER = 8 ERROR_PHONE_NUMBER = 8
actor_logger = ActorLogger()
STOPPING = 99 STOPPING = 99
@ -78,14 +79,14 @@ async def accept_personal_data_agreement(update: Update, context: ContextTypes.D
try: try:
registered = await get_patient_by_telegram_id(user_id) registered = await get_patient_by_telegram_id(user_id)
except DatabaseError as e: except DatabaseError as e:
logger.error(f"Database error while fetching patient by Telegram ID {user_id}: {e}") actor_logger.error(user_id, f"Database error while fetching patient by Telegram ID {user_id}: {e}")
await update.message.reply_text( await update.message.reply_text(
"❌ Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже." "❌ Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже."
) )
return ConversationHandler.END return ConversationHandler.END
logger.info(f"User {user_id} initiated consultation process.") actor_logger.info(user_id, f"User {user_id} initiated consultation process.")
logger.info(f"User exists? {registered}") actor_logger.info(user_id, f"User exists? {registered}")
if registered: if registered:
await update.message.reply_text( await update.message.reply_text(
@ -113,7 +114,7 @@ async def receive_patient_aceptance(update: Update, context: ContextTypes.DEFAUL
try: try:
await create_patient(telegram_id=user_id, terms_acceptance=True) # Создаем пациента в БД await create_patient(telegram_id=user_id, terms_acceptance=True) # Создаем пациента в БД
except DatabaseError as e: except DatabaseError as e:
logger.error(f"Failed to create patient for user {user_id}: {e}") actor_logger.error(user_id, f"Failed to create patient for user {user_id}: {e}")
await update.callback_query.edit_message_text( await update.callback_query.edit_message_text(
text="❌ Произошла ошибка при создании вашей записи. Пожалуйста, попробуйте позже." text="❌ Произошла ошибка при создании вашей записи. Пожалуйста, попробуйте позже."
) )
@ -167,20 +168,20 @@ async def enter_patient_phone(update: Update, context: ContextTypes.DEFAULT_TYPE
if patient: if patient:
has_phone = await is_user_has_phone(user_id) has_phone = await is_user_has_phone(user_id)
logger.info(f"User {user_id} has phone: {has_phone}") actor_logger.info(user_id, f"User {user_id} has phone: {has_phone}")
if update.callback_query.data == 'back': if update.callback_query.data == 'back':
logger.info(f"User {user_id} requested correction of their phone number") actor_logger.info(user_id, f"User {user_id} requested correction of their phone number")
if not has_phone: if not has_phone:
logger.info(f"Ask user {user_id} enter a phone number.") actor_logger.info(user_id, f"Ask user {user_id} enter a phone number.")
await update.callback_query.message.reply_text( await update.callback_query.message.reply_text(
text="Пожалуйста, введите ваш номер мобильного телефона для записи на консультацию:\n" text="Пожалуйста, введите ваш номер мобильного телефона для записи на консультацию:\n"
"Без знака +, вида 79991112233" "Без знака +, вида 79991112233"
) )
return ENTER_PATIENT_PHONE return ENTER_PATIENT_PHONE
else: else:
logger.info(f"Ask user {user_id} enter a doctor's id.") actor_logger.info(user_id, f"Ask user {user_id} enter a doctor's id.")
await update.callback_query.edit_message_text( await update.callback_query.edit_message_text(
text="Введите серийный номер врача, к которому вы хотите записаться на консультацию:", text="Введите серийный номер врача, к которому вы хотите записаться на консультацию:",
parse_mode="Markdown" parse_mode="Markdown"
@ -196,7 +197,7 @@ async def receive_patient_phone(update: Update, context: ContextTypes.DEFAULT_TY
# Например, вызов сервиса для обновления информации о пациенте # Например, вызов сервиса для обновления информации о пациенте
# await update_patient_phone(telegram_id=user_id, phone=phone) # await update_patient_phone(telegram_id=user_id, phone=phone)
logger.info((f"Checking user's phone {is_phone_correct(phone=phone)}")) actor_logger.info(user_id, f"Checking user's phone {is_phone_correct(phone=phone)}")
if not is_phone_correct(phone=phone): if not is_phone_correct(phone=phone):
keyboard = [ keyboard = [
[ [
@ -213,7 +214,7 @@ async def receive_patient_phone(update: Update, context: ContextTypes.DEFAULT_TY
) )
return ERROR_PHONE_NUMBER return ERROR_PHONE_NUMBER
logger.info((f"receive_patient_phone User {user_id} provided phone: {phone}")) actor_logger.info(user_id, f"receive_patient_phone User {user_id} provided phone: {phone}")
context.user_data['phone'] = phone context.user_data['phone'] = phone
keyboard = [ keyboard = [
@ -244,7 +245,7 @@ async def receive_doctor_number(update: Update, context: ContextTypes.DEFAULT_TY
if not await is_user_has_phone(user_id): if not await is_user_has_phone(user_id):
patient_phone = context.user_data['phone'] patient_phone = context.user_data['phone']
await update_patient_phone(telegram_id=user_id, phone=patient_phone) await update_patient_phone(telegram_id=user_id, phone=patient_phone)
logger.info((f"Saved patient's phone number {patient_phone}")) actor_logger.info(user_id, f"Saved patient's phone number {patient_phone}")
await update.message.reply_text( await update.message.reply_text(
text=f"Вы ввели серийный номер врача: {doctor_number}\n" text=f"Вы ввели серийный номер врача: {doctor_number}\n"
@ -252,7 +253,7 @@ async def receive_doctor_number(update: Update, context: ContextTypes.DEFAULT_TY
parse_mode="Markdown" parse_mode="Markdown"
) )
else: else:
logger.info(f"User {user_id} requested correction of their consultation date") actor_logger.info(user_id, f"User {user_id} requested correction of their consultation date")
await update.effective_message.reply_text( await update.effective_message.reply_text(
text="Введите дату и время консультации в формате ДД.ММ.ГГ ЧЧ:ММ, например, 01.01.23 12:00.", text="Введите дату и время консультации в формате ДД.ММ.ГГ ЧЧ:ММ, например, 01.01.23 12:00.",
parse_mode="Markdown" parse_mode="Markdown"
@ -299,7 +300,7 @@ async def pay_consultation(update: Update, context: ContextTypes.DEFAULT_TYPE) -
patient = await get_patient_by_telegram_id(user_id) patient = await get_patient_by_telegram_id(user_id)
doctor = await get_doctor_by_code(context.user_data['doctor_number']) doctor = await get_doctor_by_code(context.user_data['doctor_number'])
except DatabaseError as e: except DatabaseError as e:
logger.error(f"Database error during payment process for user {user_id}: {e}") actor_logger.error(user_id, f"Database error during payment process for user {user_id}: {e}")
await update.callback_query.message.reply_text( await update.callback_query.message.reply_text(
"❌ Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже." "❌ Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже."
) )
@ -316,7 +317,7 @@ async def pay_consultation(update: Update, context: ContextTypes.DEFAULT_TYPE) -
doctor=doctor doctor=doctor
) )
except DatabaseError as e: except DatabaseError as e:
logger.error(f"Failed to create session for user {user_id}: {e}") actor_logger.error(user_id, f"Failed to create session for user {user_id}: {e}")
await update.callback_query.message.reply_text( await update.callback_query.message.reply_text(
"❌ Произошла ошибка при создании записи на консультацию. Пожалуйста, попробуйте позже." "❌ Произошла ошибка при создании записи на консультацию. Пожалуйста, попробуйте позже."
) )