Merge pull request #1 from korjavin/feature/structured-json-logging

Implement Structured JSON Error Logging
This commit is contained in:
Korjavin Ivan 2025-09-06 22:32:57 +02:00 committed by GitHub
commit 127c953801
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 12 deletions

38
main.py
View file

@ -14,6 +14,7 @@ from typing import List, Dict, Optional, Union, Any
import nest_asyncio import nest_asyncio
from dotenv import load_dotenv from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
from pythonjsonlogger import jsonlogger
from telethon import TelegramClient, functions, utils from telethon import TelegramClient, functions, utils
from telethon.sessions import StringSession from telethon.sessions import StringSession
from telethon.tl.types import ( from telethon.tl.types import (
@ -78,12 +79,19 @@ try:
file_handler = logging.FileHandler(log_file_path, mode="a") # Append mode file_handler = logging.FileHandler(log_file_path, mode="a") # Append mode
file_handler.setLevel(logging.ERROR) file_handler.setLevel(logging.ERROR)
# Create formatter and add to handlers # Create formatters
formatter = logging.Formatter( # Console formatter remains in the old format
"%(asctime)s [%(levelname)s] %(name)s - %(message)s - %(filename)s:%(lineno)d" console_formatter = logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s - %(message)s"
) )
console_handler.setFormatter(formatter) console_handler.setFormatter(console_formatter)
file_handler.setFormatter(formatter)
# File formatter is now JSON
json_formatter = jsonlogger.JsonFormatter(
"%(asctime)s %(name)s %(levelname)s %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S%z",
)
file_handler.setFormatter(json_formatter)
# Add handlers to logger # Add handlers to logger
logger.addHandler(console_handler) logger.addHandler(console_handler)
@ -140,17 +148,23 @@ def log_and_format_error(
break break
prefix_str = prefix.value if prefix else "GEN" prefix_str = prefix.value if prefix else "GEN"
error_code = f"{prefix_str}-ERR-{abs(hash(function_name)) % 1000:03d}" error_code = f"{prefix_str}-ERR-{abs(hash(function_name)) % 1000:03d}"
# Format the additional context parameters # Log the structured error
context = ", ".join(f"{k}={v}" for k, v in kwargs.items()) log_context = {
"function_name": function_name,
# Log the full technical error "error_code": error_code,
logger.exception(f"{function_name} failed ({context}): {error}") "error_message": str(error),
"parameters": kwargs,
}
logger.error(
f"Error in {function_name}",
extra=log_context,
exc_info=(type(error), error, error.__traceback__),
)
# Return a user-friendly message # Return a user-friendly message
return f"An error occurred (code: {error_code}). " f"Check mcp_errors.log for details." return f"An error occurred (code: {error_code}). Check mcp_errors.log for details."
def format_entity(entity) -> Dict[str, Any]: def format_entity(entity) -> Dict[str, Any]:

View file

@ -29,6 +29,7 @@ dependencies = [
"mcp[cli]>=1.4.1", "mcp[cli]>=1.4.1",
"nest-asyncio>=1.6.0", "nest-asyncio>=1.6.0",
"python-dotenv>=1.1.0", "python-dotenv>=1.1.0",
"python-json-logger>=3.3.0",
"telethon>=1.39.0" "telethon>=1.39.0"
] ]

View file

@ -3,4 +3,5 @@ httpx>=0.28.1
mcp[cli]>=1.4.1 mcp[cli]>=1.4.1
nest-asyncio>=1.6.0 nest-asyncio>=1.6.0
python-dotenv>=1.1.0 python-dotenv>=1.1.0
python-json-logger>=3.3.0
telethon>=1.39.0 telethon>=1.39.0