feat: implement structured JSON error logging

- Add python-json-logger dependency.
- Configure file handler to use JsonFormatter for structured logging.
- Keep console handler with the existing text format.
- Refactor log_and_format_error to log a dictionary with structured error data.
This commit is contained in:
google-labs-jules[bot] 2025-09-01 14:39:30 +00:00
parent 3cd0489769
commit 50b01ca494
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