feat: add log for export api

This commit is contained in:
liuyuanchuang
2026-03-12 11:40:19 +08:00
parent bb1cf66137
commit 92b56d61d8
7 changed files with 101 additions and 5 deletions

View File

@@ -1,5 +1,7 @@
"""Format conversion endpoints."""
import logging
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import Response
@@ -7,6 +9,8 @@ from app.core.dependencies import get_converter
from app.schemas.convert import LatexToOmmlRequest, LatexToOmmlResponse, MarkdownToDocxRequest
from app.services.converter import Converter
logger = logging.getLogger(__name__)
router = APIRouter()
@@ -19,14 +23,25 @@ async def convert_markdown_to_docx(
Returns the generated DOCX file as a binary response.
"""
logger.info(
"Converting markdown to DOCX, filename=%s, content_length=%d",
request.filename,
len(request.markdown),
)
try:
docx_bytes = converter.export_to_file(request.markdown, export_type="docx")
logger.info(
"DOCX conversion successful, filename=%s, size=%d bytes",
request.filename,
len(docx_bytes),
)
return Response(
content=docx_bytes,
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
headers={"Content-Disposition": f'attachment; filename="{request.filename}.docx"'},
)
except Exception as e:
logger.exception("DOCX conversion failed, filename=%s: %s", request.filename, e)
raise HTTPException(status_code=500, detail=f"Conversion failed: {e}")
@@ -55,12 +70,17 @@ async def convert_latex_to_omml(
```
"""
if not request.latex or not request.latex.strip():
logger.warning("LaTeX to OMML request received with empty formula")
raise HTTPException(status_code=400, detail="LaTeX formula cannot be empty")
logger.info("Converting LaTeX to OMML, latex=%r", request.latex)
try:
omml = converter.convert_to_omml(request.latex)
logger.info("LaTeX to OMML conversion successful")
return LatexToOmmlResponse(omml=omml)
except ValueError as e:
logger.warning("LaTeX to OMML conversion invalid input: %s", e)
raise HTTPException(status_code=400, detail=str(e))
except RuntimeError as e:
logger.error("LaTeX to OMML conversion runtime error: %s", e)
raise HTTPException(status_code=503, detail=str(e))

View File

@@ -2,11 +2,15 @@
import logging
import logging.handlers
from contextvars import ContextVar
from pathlib import Path
from typing import Any
from app.core.config import get_settings
# Context variable to hold the current request_id across async boundaries
request_id_ctx: ContextVar[str] = ContextVar("request_id", default="-")
class TimedRotatingAndSizeFileHandler(logging.handlers.TimedRotatingFileHandler):
"""File handler that rotates by both time (daily) and size (100MB)."""
@@ -92,14 +96,13 @@ def setup_logging(log_dir: str | None = None) -> logging.Logger:
# Remove existing handlers to avoid duplicates
logger.handlers.clear()
# Create custom formatter that handles missing request_id
# Create custom formatter that automatically injects request_id from context
class RequestIDFormatter(logging.Formatter):
"""Formatter that handles request_id in log records."""
"""Formatter that injects request_id from ContextVar into log records."""
def format(self, record):
# Add request_id if not present
if not hasattr(record, "request_id"):
record.request_id = getattr(record, "request_id", "unknown")
record.request_id = request_id_ctx.get()
return super().format(record)
formatter = RequestIDFormatter(

View File

@@ -8,6 +8,7 @@ from app.api.v1.router import api_router
from app.core.config import get_settings
from app.core.dependencies import init_layout_detector
from app.core.logging_config import setup_logging
from app.middleware.request_id import RequestIDMiddleware
settings = get_settings()
@@ -33,6 +34,8 @@ app = FastAPI(
lifespan=lifespan,
)
app.add_middleware(RequestIDMiddleware)
# Include API router
app.include_router(api_router, prefix=settings.api_prefix)

View File

View File

@@ -0,0 +1,34 @@
"""Middleware to propagate or generate request_id for every request."""
import uuid
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from app.core.logging_config import request_id_ctx
REQUEST_ID_HEADER = "X-Request-ID"
class RequestIDMiddleware(BaseHTTPMiddleware):
"""Extract X-Request-ID from incoming request headers or generate one.
The request_id is stored in a ContextVar so that all log records emitted
during the request are automatically annotated with it, without needing to
pass it explicitly through every call.
The same request_id is also echoed back in the response header so that
callers can correlate logs.
"""
async def dispatch(self, request: Request, call_next) -> Response:
request_id = request.headers.get(REQUEST_ID_HEADER) or str(uuid.uuid4())
token = request_id_ctx.set(request_id)
try:
response = await call_next(request)
finally:
request_id_ctx.reset(token)
response.headers[REQUEST_ID_HEADER] = request_id
return response