wip: replace AppLogger with AppStructLogger

This commit is contained in:
grillazz 2025-06-29 21:19:30 +02:00
parent d0d26687df
commit 1098e39f71
10 changed files with 53 additions and 65 deletions

View File

@ -6,9 +6,9 @@ from pydantic import EmailStr
from starlette.concurrency import run_in_threadpool from starlette.concurrency import run_in_threadpool
from app.services.smtp import SMTPEmailService from app.services.smtp import SMTPEmailService
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
router = APIRouter() router = APIRouter()

View File

@ -4,9 +4,9 @@ from fastapi import APIRouter, Depends, Form
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from app.services.llm import get_llm_service from app.services.llm import get_llm_service
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
router = APIRouter() router = APIRouter()

View File

@ -5,9 +5,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db from app.database import get_db
from app.models.stuff import Stuff from app.models.stuff import Stuff
from app.schemas.stuff import StuffResponse, StuffSchema from app.schemas.stuff import StuffResponse, StuffSchema
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
router = APIRouter(prefix="/v1/stuff") router = APIRouter(prefix="/v1/stuff")

View File

@ -7,9 +7,9 @@ from app.database import get_db
from app.models.user import User from app.models.user import User
from app.schemas.user import TokenResponse, UserLogin, UserResponse, UserSchema from app.schemas.user import TokenResponse, UserLogin, UserResponse, UserSchema
from app.services.auth import create_access_token from app.services.auth import create_access_token
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
router = APIRouter(prefix="/v1/user") router = APIRouter(prefix="/v1/user")

View File

@ -3,9 +3,9 @@ from collections.abc import AsyncGenerator
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from app.config import settings as global_settings from app.config import settings as global_settings
from app.utils.logging import setup_structlog from app.utils.logging import AppStructLogger
logger = setup_structlog() logger = AppStructLogger().get_logger()
engine = create_async_engine( engine = create_async_engine(
global_settings.asyncpg_url.unicode_string(), global_settings.asyncpg_url.unicode_string(),

View File

@ -15,9 +15,9 @@ from app.api.user import router as user_router
from app.config import settings as global_settings from app.config import settings as global_settings
from app.redis import get_redis from app.redis import get_redis
from app.services.auth import AuthBearer from app.services.auth import AuthBearer
from app.utils.logging import setup_structlog from app.utils.logging import AppStructLogger
logger = setup_structlog() logger = AppStructLogger().get_logger()
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates") templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")
@asynccontextmanager @asynccontextmanager

View File

@ -6,9 +6,9 @@ from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import DeclarativeBase, declared_attr from sqlalchemy.orm import DeclarativeBase, declared_attr
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
class Base(DeclarativeBase): class Base(DeclarativeBase):

View File

@ -6,9 +6,9 @@ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from app.config import settings as global_settings from app.config import settings as global_settings
from app.models.user import User from app.models.user import User
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
async def get_from_redis(request: Request, key: str): async def get_from_redis(request: Request, key: str):

View File

@ -7,10 +7,10 @@ from fastapi.templating import Jinja2Templates
from pydantic import EmailStr from pydantic import EmailStr
from app.config import settings as global_settings from app.config import settings as global_settings
from app.utils.logging import AppLogger from app.utils.logging import AppStructLogger
from app.utils.singleton import SingletonMetaNoArgs from app.utils.singleton import SingletonMetaNoArgs
logger = AppLogger().get_logger() logger = AppStructLogger().get_logger()
@define @define

View File

@ -5,30 +5,9 @@ from pathlib import Path
import orjson import orjson
import structlog import structlog
from rich.console import Console
from rich.logging import RichHandler
from whenever._whenever import Instant from whenever._whenever import Instant
from app.utils.singleton import SingletonMeta from app.utils.singleton import SingletonMetaNoArgs
class AppLogger(metaclass=SingletonMeta):
_logger = None
def __init__(self):
self._logger = logging.getLogger(__name__)
def get_logger(self):
return self._logger
class RichConsoleHandler(RichHandler):
def __init__(self, width=200, style=None, **kwargs):
super().__init__(
console=Console(color_system="256", width=width, style=style, stderr=True),
**kwargs,
)
# TODO: merge this wrapper with the one in structlog under one hood of AppLogger # TODO: merge this wrapper with the one in structlog under one hood of AppLogger
@ -50,30 +29,39 @@ class BytesToTextIOWrapper:
def close(self): def close(self):
self.handler.close() self.handler.close()
# @define
class AppStructLogger(metaclass=SingletonMetaNoArgs):
_logger = None
def setup_structlog() -> structlog.BoundLogger: def __init__(self):
log_date = Instant.now().py_datetime().strftime("%Y%m%d") _log_date = Instant.now().py_datetime().strftime("%Y%m%d")
log_path = Path(f"{log_date}_{os.getpid()}.log") _log_path = Path(f"{_log_date}_{os.getpid()}.log")
handler = RotatingFileHandler( _handler = RotatingFileHandler(
filename=log_path, filename=_log_path,
mode="a", # text mode mode="a", # text mode
maxBytes=10 * 1024 * 1024, maxBytes=10 * 1024 * 1024,
backupCount=5, backupCount=5,
encoding="utf-8" encoding="utf-8"
)
file_like = BytesToTextIOWrapper(handler)
structlog.configure(
cache_logger_on_first_use=True,
wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.format_exc_info,
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.processors.JSONRenderer(serializer=orjson.dumps),
],
logger_factory=structlog.BytesLoggerFactory(
file=file_like
) )
) structlog.configure(
return structlog.get_logger() cache_logger_on_first_use=True,
wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.format_exc_info,
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.processors.JSONRenderer(serializer=orjson.dumps),
],
logger_factory=structlog.BytesLoggerFactory(
file=BytesToTextIOWrapper(_handler)
)
)
self._logger = structlog.get_logger()
def get_logger(self) -> structlog.BoundLogger:
"""
Returns:
structlog.BoundLogger: The configured logger instance.
"""
return self._logger