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

View File

@ -1,3 +1,5 @@
import logging
from logging import Logger
from typing import Optional from typing import Optional
from pyrogram import filters from pyrogram import filters
@ -27,6 +29,9 @@ class TelegramUserBot:
api_hash (str): The API hash for the Telegram application. api_hash (str): The API hash for the Telegram application.
gigachat_client (GigaChatClient): An instance of GigaChatClient for handling AI responses. 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.app: Client = Client(session_name, api_id=api_id, api_hash=api_hash)
self.gigachat_client: GigaChatClient = gigachat_client self.gigachat_client: GigaChatClient = gigachat_client
self.register_handlers() self.register_handlers()
@ -35,6 +40,7 @@ class TelegramUserBot:
""" """
Registers the message handlers for the bot. 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) self.app.on_message(filters.command("ai") & filters.text)(self.handle_ai_command)
async def handle_ai_command(self, client: Client, message: Message) -> None: async def handle_ai_command(self, client: Client, message: Message) -> None:
@ -45,14 +51,18 @@ class TelegramUserBot:
client (Client): The Pyrogram client instance. client (Client): The Pyrogram client instance.
message (Message): The incoming Telegram message. message (Message): The incoming Telegram message.
""" """
self.logger.info(f"Received /ai command from chat_id={message.chat.id}")
# Extract the command argument # Extract the command argument
command_arg: Optional[str] = " ".join(message.text.split()[1:]) command_arg: Optional[str] = " ".join(message.text.split()[1:])
if not command_arg: 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.") await message.reply_text("Please provide a message after /ai.")
return return
# Send an initial message indicating processing # 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...") processing_message: Message = await message.reply_text(f"{self.gigachat_client.model_name} is processing your request...")
try: try:
@ -61,6 +71,7 @@ class TelegramUserBot:
# Get a response from GigaChat # Get a response from GigaChat
response: str = self.gigachat_client.get_response(str(message.chat.id), command_arg) 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 # Stop typing animation
await client.send_chat_action(message.chat.id, ChatAction.CANCEL) await client.send_chat_action(message.chat.id, ChatAction.CANCEL)
@ -68,6 +79,9 @@ class TelegramUserBot:
# Edit the processing message with the generated response # Edit the processing message with the generated response
await processing_message.edit_text(response) await processing_message.edit_text(response)
except Exception as e: 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 # Stop typing animation in case of an error
await client.send_chat_action(message.chat.id, ChatAction.CANCEL) await client.send_chat_action(message.chat.id, ChatAction.CANCEL)
@ -78,5 +92,9 @@ class TelegramUserBot:
""" """
Starts the bot. Starts the bot.
""" """
self.logger.info("Bot is starting.")
print("Bot is running.") 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 typing import Dict
from langchain_core.runnables.history import RunnableWithMessageHistory 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. api_token (str): The API token for authenticating with the GigaChat API.
model_name (str): The GigaChat model to use. Defaults to "GigaChat". 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.api_token: str = api_token
self.model_name: str = model_name 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.llm: GigaChat = self._create_llm(model_name)
self.store: Dict[str, InMemoryChatMessageHistory] = {} self.store: Dict[str, InMemoryChatMessageHistory] = {}
self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history) self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history)
@ -34,6 +41,7 @@ class GigaChatClient:
Returns: Returns:
GigaChat: Configured GigaChat instance. GigaChat: Configured GigaChat instance.
""" """
self.logger.debug(f"Creating GigaChat LLM with model: {model_name}")
return GigaChat( return GigaChat(
credentials=self.api_token, credentials=self.api_token,
scope="GIGACHAT_API_PERS", scope="GIGACHAT_API_PERS",
@ -53,7 +61,10 @@ class GigaChatClient:
InMemoryChatMessageHistory: The chat history for the session. InMemoryChatMessageHistory: The chat history for the session.
""" """
if session_id not in self.store: 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() self.store[session_id] = InMemoryChatMessageHistory()
else:
self.logger.debug(f"Retrieving existing session history for session_id: {session_id}")
return self.store[session_id] return self.store[session_id]
def set_model(self, model_name: str) -> None: def set_model(self, model_name: str) -> None:
@ -63,6 +74,7 @@ class GigaChatClient:
Args: Args:
model_name (str): The new GigaChat model to use. 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.llm = self._create_llm(model_name)
self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history) self.conversation = RunnableWithMessageHistory(self.llm, self.get_session_history)
@ -77,8 +89,14 @@ class GigaChatClient:
Returns: Returns:
str: The response text. str: The response text.
""" """
response = self.conversation.invoke( self.logger.info(f"Generating response for session_id: {session_id}")
input=text, try:
config={"configurable": {"session_id": session_id}}, response = self.conversation.invoke(
) input=text,
return response.content 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)
},
})