feat: add logging

This commit is contained in:
parent a43873109c
commit 8bf781b67d
4 changed files with 108 additions and 6 deletions

View File

@ -1,5 +1,6 @@
from src.integrations.gigachat_api_client import GigaChatClient
from src.bot.telegram_userbot import TelegramUserBot
from src.utils.logging import setup_logging
from src.core.configuration import config
@ -7,6 +8,9 @@ def main() -> None:
"""
Entry point for starting the Telegram user bot.
"""
# Configure logging
setup_logging()
# Load API credentials and configuration
api_id: str = config.API_ID
api_hash: str = config.API_HASH

View File

@ -1,3 +1,5 @@
import logging
from logging import Logger
from typing import Optional
from pyrogram import filters
@ -27,6 +29,9 @@ class TelegramUserBot:
api_hash (str): The API hash for the Telegram application.
gigachat_client (GigaChatClient): An instance of GigaChatClient for handling AI responses.
"""
# Configure logging
self.logger: Logger = logging.getLogger(__name__)
self.app: Client = Client(session_name, api_id=api_id, api_hash=api_hash)
self.gigachat_client: GigaChatClient = gigachat_client
self.register_handlers()
@ -35,6 +40,7 @@ class TelegramUserBot:
"""
Registers the message handlers for the bot.
"""
self.logger.debug("Registering handlers.")
self.app.on_message(filters.command("ai") & filters.text)(self.handle_ai_command)
async def handle_ai_command(self, client: Client, message: Message) -> None:
@ -45,14 +51,18 @@ class TelegramUserBot:
client (Client): The Pyrogram client instance.
message (Message): The incoming Telegram message.
"""
self.logger.info(f"Received /ai command from chat_id={message.chat.id}")
# Extract the command argument
command_arg: Optional[str] = " ".join(message.text.split()[1:])
if not command_arg:
self.logger.warning(f"No argument provided for /ai command by chat_id={message.chat.id}")
await message.reply_text("Please provide a message after /ai.")
return
# Send an initial message indicating processing
self.logger.debug(f"Processing request for chat_id={message.chat.id}")
processing_message: Message = await message.reply_text(f"{self.gigachat_client.model_name} is processing your request...")
try:
@ -61,6 +71,7 @@ class TelegramUserBot:
# Get a response from GigaChat
response: str = self.gigachat_client.get_response(str(message.chat.id), command_arg)
self.logger.debug(f"Received response for chat_id={message.chat.id}")
# Stop typing animation
await client.send_chat_action(message.chat.id, ChatAction.CANCEL)
@ -68,6 +79,9 @@ class TelegramUserBot:
# Edit the processing message with the generated response
await processing_message.edit_text(response)
except Exception as e:
# Log the exception details
self.logger.error(f"Error processing /ai command for chat_id={message.chat.id}: {e}", exc_info=True)
# Stop typing animation in case of an error
await client.send_chat_action(message.chat.id, ChatAction.CANCEL)
@ -78,5 +92,9 @@ class TelegramUserBot:
"""
Starts the bot.
"""
self.logger.info("Bot is starting.")
print("Bot is running.")
self.app.run()
try:
self.app.run()
except Exception as e:
self.logger.critical(f"Failed to start the bot: {e}", exc_info=True)

View File

@ -1,3 +1,5 @@
import logging
from logging import Logger
from typing import Dict
from langchain_core.runnables.history import RunnableWithMessageHistory
@ -18,8 +20,13 @@ class GigaChatClient:
api_token (str): The API token for authenticating with the GigaChat API.
model_name (str): The GigaChat model to use. Defaults to "GigaChat".
"""
# Configure logging
self.logger: Logger = logging.getLogger(__name__)
self.api_token: str = api_token
self.model_name: str = model_name
self.logger.info(f"Initialize GigaChat client Using model: {self.model_name}")
self.llm: GigaChat = self._create_llm(model_name)
self.store: Dict[str, InMemoryChatMessageHistory] = {}
self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history)
@ -34,6 +41,7 @@ class GigaChatClient:
Returns:
GigaChat: Configured GigaChat instance.
"""
self.logger.debug(f"Creating GigaChat LLM with model: {model_name}")
return GigaChat(
credentials=self.api_token,
scope="GIGACHAT_API_PERS",
@ -53,7 +61,10 @@ class GigaChatClient:
InMemoryChatMessageHistory: The chat history for the session.
"""
if session_id not in self.store:
self.logger.debug(f"Creating new session history for session_id: {session_id}")
self.store[session_id] = InMemoryChatMessageHistory()
else:
self.logger.debug(f"Retrieving existing session history for session_id: {session_id}")
return self.store[session_id]
def set_model(self, model_name: str) -> None:
@ -63,6 +74,7 @@ class GigaChatClient:
Args:
model_name (str): The new GigaChat model to use.
"""
self.logger.info(f"Switching model to: {model_name}")
self.llm = self._create_llm(model_name)
self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history)
@ -77,8 +89,14 @@ class GigaChatClient:
Returns:
str: The response text.
"""
response = self.conversation.invoke(
input=text,
config={"configurable": {"session_id": session_id}},
)
return response.content
self.logger.info(f"Generating response for session_id: {session_id}")
try:
response = self.conversation.invoke(
input=text,
config={"configurable": {"session_id": session_id}},
)
self.logger.debug(f"Response for session_id {session_id}")
return response.content
except Exception as e:
self.logger.error(f"Error while getting response for session_id: {session_id}. Error: {e}", exc_info=True)
raise

62
src/utils/logging.py Normal file
View File

@ -0,0 +1,62 @@
import logging
import logging.config
from datetime import datetime
def setup_logging(output_to_console=False) -> None:
"""
Configures the logging system.
This function sets up logging with optional output to the console and ensures
log files are rotated daily. It creates a detailed logging format and retains
log files for a week.
Args:
output_to_console (bool): If True, log messages will also be printed to the console.
Defaults to False.
"""
# Define the default handlers to use. Always logs to a file.
handlers: list[str] = ['file']
if output_to_console:
# Add console logging if requested
handlers.append('console')
# Generate the log file name with the current date
log_filename: str = f'logs/log-{datetime.now().strftime("%Y-%m-%d")}.log'
# Configure the logging settings using a dictionary
logging.config.dictConfig({
'version': 1, # Logging configuration version
'disable_existing_loggers': True, # Deny other loggers to remain active
'formatters': {
'detailed': { # Define a detailed logging format
'format': (
'%(asctime)s | %(levelname)-8s | '
'%(filename)s.%(funcName)s, line %(lineno)d: '
'%(message)s'
),
'datefmt': '%Y-%m-%d %H:%M:%S' # Timestamp format
},
},
'handlers': {
# Console handler outputs log messages to the console
'console': {
'class': 'logging.StreamHandler', # Standard output stream
'formatter': 'detailed', # Use the detailed formatter
},
# File handler writes log messages to a file, rotating daily
'file': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': log_filename, # Log file path
'when': 'midnight', # Rotate log files at midnight
'interval': 1, # Rotate daily
'backupCount': 7, # Keep up to 7 old log files
'formatter': 'detailed', # Use the detailed formatter
},
},
# Define the root logger configuration
'root': {
'handlers': handlers, # Handlers to use (console, file, or both)
'level': 'DEBUG', # Log level (DEBUG logs all levels)
},
})