diff --git a/titiler/cmr/main.py b/titiler/cmr/main.py index 5a3165a0..4712f5e8 100644 --- a/titiler/cmr/main.py +++ b/titiler/cmr/main.py @@ -1,6 +1,8 @@ """TiTiler+cmr FastAPI application.""" +import json import logging +from logging import config as logging_config import os from contextlib import asynccontextmanager @@ -33,12 +35,42 @@ auth_config = AuthSettings() +class JSONFormatter(logging.Formatter): + """Custom JSON formatter for request logs.""" + + def format(self, record): + """Format a log record as a JSON object.""" + log_entry = { + "timestamp": self.formatTime(record), + "level": record.levelname, + "logger": record.name, + "message": record.getMessage(), + } + + # Add extra fields if they exist + extra_fields = [ + "method", + "path", + "query_params", + "path_params", + "headers", + "referer", + "origin", + "route", + ] + + for field in extra_fields: + if hasattr(record, field): + value = getattr(record, field) + # Convert query_params and path_params to dict if they're not already + if field in ["query_params", "path_params"] and hasattr(value, "items"): + value = dict(value) + log_entry[field] = value + + return json.dumps(log_entry) + + log_level = os.getenv("LOG_LEVEL", "INFO") -logging.basicConfig( - level=getattr(logging, log_level), - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - handlers=[logging.StreamHandler()], -) @asynccontextmanager @@ -152,8 +184,49 @@ async def lifespan(app: FastAPI): app.add_middleware(CacheControlMiddleware, cachecontrol=settings.cachecontrol) -if settings.debug: - app.add_middleware(LoggerMiddleware, headers=True, querystrings=True) +# Configure logging with support for extra fields +logging_config.dictConfig( + { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + }, + "json": { + "()": JSONFormatter, + }, + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "level": log_level, + "formatter": "default", + }, + "console_request": { + "class": "logging.StreamHandler", + "level": log_level, + "formatter": "json", + }, + }, + "loggers": { + "": { # Root logger + "level": log_level, + "handlers": ["console"], + }, + "titiler.requests": { # Logger used by LoggerMiddleware + "level": log_level, + "handlers": ["console_request"], + "propagate": False, + }, + }, + } +) + +app.add_middleware( + LoggerMiddleware, + logger=logging.getLogger("titiler.requests"), +) ############################################################################### # application endpoints diff --git a/titiler/cmr/settings.py b/titiler/cmr/settings.py index 00f12528..7cc4eb76 100644 --- a/titiler/cmr/settings.py +++ b/titiler/cmr/settings.py @@ -18,7 +18,6 @@ class ApiSettings(BaseSettings): time_series_max_image_size: float = 5.625e7 time_series_statistics_max_total_size: float = 1.5e10 time_series_image_max_total_size: float = 1e8 - debug: bool = False model_config = { "env_prefix": "TITILER_CMR_",