Add sentry, datadog and structured logging
This commit is contained in:
parent
ef769e40b6
commit
6c07d47b1b
@ -4,7 +4,7 @@ from pydantic_settings import BaseSettings
|
|||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
"""Application settings following 12-factor app principles."""
|
env: str = Field(default='DEV', alias="ENV")
|
||||||
|
|
||||||
# Server settings
|
# Server settings
|
||||||
host: str = Field(default="0.0.0.0", alias="HOST")
|
host: str = Field(default="0.0.0.0", alias="HOST")
|
||||||
@ -5,7 +5,7 @@ from sqlalchemy import MetaData, create_engine
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
42
app/logging.py
Normal file
42
app/logging.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"""Logging configuration with structured logging."""
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import structlog
|
||||||
|
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
def configure_logging() -> None:
|
||||||
|
"""Configure structured logging."""
|
||||||
|
|
||||||
|
structlog.configure(
|
||||||
|
processors=[
|
||||||
|
structlog.stdlib.filter_by_level,
|
||||||
|
structlog.stdlib.add_logger_name,
|
||||||
|
structlog.stdlib.add_log_level,
|
||||||
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||||
|
structlog.processors.TimeStamper(fmt="iso"),
|
||||||
|
structlog.processors.StackInfoRenderer(),
|
||||||
|
structlog.processors.format_exc_info,
|
||||||
|
structlog.processors.UnicodeDecoder(),
|
||||||
|
structlog.processors.JSONRenderer(
|
||||||
|
) if not settings.debug else structlog.dev.ConsoleRenderer(),
|
||||||
|
],
|
||||||
|
context_class=dict,
|
||||||
|
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||||
|
wrapper_class=structlog.stdlib.BoundLogger,
|
||||||
|
cache_logger_on_first_use=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure standard logging
|
||||||
|
logging.basicConfig(
|
||||||
|
format="%(message)s",
|
||||||
|
stream=sys.stdout,
|
||||||
|
level=getattr(logging, settings.log_level.upper()),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name: str) -> structlog.BoundLogger:
|
||||||
|
"""Get a configured logger instance."""
|
||||||
|
return structlog.get_logger(name) # type:ignore[no-any-return]
|
||||||
36
app/middleware.py
Normal file
36
app/middleware.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from fastapi import Request, Response
|
||||||
|
from slowapi import Limiter, _rate_limit_exceeded_handler
|
||||||
|
from slowapi.errors import RateLimitExceeded
|
||||||
|
from slowapi.middleware import SlowAPIMiddleware
|
||||||
|
from slowapi.util import get_remote_address
|
||||||
|
|
||||||
|
from app.config import settings
|
||||||
|
from app.logging import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# Rate limiter
|
||||||
|
limiter = Limiter(key_func=get_remote_address)
|
||||||
|
|
||||||
|
|
||||||
|
async def logging_middleware(request: Request, call_next: Callable) -> Response:
|
||||||
|
"""Log all requests and responses."""
|
||||||
|
logger.debug(
|
||||||
|
"Request received",
|
||||||
|
method=request.method,
|
||||||
|
url=str(request.url),
|
||||||
|
client_ip=get_remote_address(request)
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Response sent",
|
||||||
|
status_code=response.status_code,
|
||||||
|
method=request.method,
|
||||||
|
url=str(request.url)
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
@ -1,7 +1,7 @@
|
|||||||
"""Health check endpoints."""
|
"""Health check endpoints."""
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.core.database import DatabaseService
|
from app.database import DatabaseService
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
33
main.py
33
main.py
@ -1,17 +1,38 @@
|
|||||||
"""
|
"""
|
||||||
Main entry point for the Loan Operations API.
|
Main entry point for the Loan Operations API.
|
||||||
"""
|
"""
|
||||||
import uvicorn
|
import sentry_sdk
|
||||||
from fastapi import FastAPI
|
from ddtrace import patch_all
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi import APIRouter, FastAPI
|
||||||
|
from sentry_sdk.integrations.fastapi import FastApiIntegration
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.config import settings
|
||||||
|
from app.logging import configure_logging, get_logger
|
||||||
|
from app.middleware import logging_middleware
|
||||||
from app.resources import health
|
from app.resources import health
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
def create_app() -> FastAPI:
|
||||||
"""Create and configure the FastAPI application."""
|
"""Create and configure the FastAPI application."""
|
||||||
|
|
||||||
|
# Configure monitoring
|
||||||
|
if settings.sentry_dsn:
|
||||||
|
sentry_sdk.init(
|
||||||
|
dsn=settings.sentry_dsn,
|
||||||
|
integrations=[FastApiIntegration()],
|
||||||
|
traces_sample_rate=1.0,
|
||||||
|
environment=settings.dd_env,
|
||||||
|
)
|
||||||
|
|
||||||
|
if settings.dd_service:
|
||||||
|
# Configure Datadog tracing
|
||||||
|
patch_all()
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
configure_logging()
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# Create FastAPI app
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Loan Operations API",
|
title="Loan Operations API",
|
||||||
description="SBA Loan Operations API",
|
description="SBA Loan Operations API",
|
||||||
@ -28,7 +49,9 @@ def create_app() -> FastAPI:
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Run the application."""
|
"""Run the application."""
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app,
|
app,
|
||||||
host=settings.host,
|
host=settings.host,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user