first commit

This commit is contained in:
2026-04-12 21:58:52 +03:00
commit acfaa2a40c
44 changed files with 2895 additions and 0 deletions

103
bot/utils/logging_config.py Normal file
View File

@@ -0,0 +1,103 @@
import json
import logging
import os
import sys
from datetime import datetime, timezone
STANDARD_LOG_RECORD_FIELDS = {
"args",
"asctime",
"created",
"exc_info",
"exc_text",
"filename",
"funcName",
"levelname",
"levelno",
"lineno",
"module",
"msecs",
"message",
"msg",
"name",
"pathname",
"process",
"processName",
"relativeCreated",
"stack_info",
"thread",
"threadName",
"taskName",
}
class JsonFormatter(logging.Formatter):
def __init__(self, service_name: str):
super().__init__()
self.service_name = service_name
def format(self, record: logging.LogRecord) -> str:
log_payload = {
"timestamp": datetime.fromtimestamp(
record.created, tz=timezone.utc
).isoformat(),
"level": record.levelname,
"service": self.service_name,
"logger": record.name,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
"line": record.lineno,
}
extra_fields = self._collect_extra_fields(record)
if extra_fields:
log_payload["extra"] = extra_fields
if record.exc_info:
log_payload["exception"] = self.formatException(record.exc_info)
return json.dumps(log_payload, ensure_ascii=False)
def _collect_extra_fields(self, record: logging.LogRecord) -> dict:
extra_fields = {}
for key, value in record.__dict__.items():
if key in STANDARD_LOG_RECORD_FIELDS or key.startswith("_"):
continue
if isinstance(value, (str, int, float, bool)) or value is None:
extra_fields[key] = value
else:
extra_fields[key] = repr(value)
return extra_fields
def setup_logging(service_name: str) -> None:
log_level_name = os.getenv("LOG_LEVEL", "INFO").upper()
log_level = getattr(logging, log_level_name, logging.INFO)
root_logger = logging.getLogger()
root_logger.handlers.clear()
root_logger.setLevel(log_level)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter(service_name=service_name))
root_logger.addHandler(handler)
for logger_name in (
"uvicorn",
"uvicorn.error",
"uvicorn.access",
"aiogram",
"aiohttp",
"asyncio",
):
logger = logging.getLogger(logger_name)
logger.handlers.clear()
logger.propagate = True
logging.captureWarnings(True)