anyway/backend/launcher.py
Remy Moll fa083a1080
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 2m9s
Run linting on the backend code / Build (pull_request) Failing after 30s
Run testing on the backend code / Build (pull_request) Failing after 1m41s
Build and deploy the backend to staging / Deploy to staging (pull_request) Successful in 18s
logging cleanup
2024-12-28 22:25:42 +01:00

83 lines
3.0 KiB
Python

"""Launcher for the FastAPI application. Fundametally this replicates the functionality of the uvicorn and fastapi CLI interfaces, but we need this to setup the logging correctly (and most importantly globally)"""
import os
import uvicorn
import logging
logger = logging.getLogger(__name__)
is_debug = os.getenv('DEBUG', "false") == "true"
is_kubernetes = os.getenv('KUBERNETES_SERVICE_HOST', None) is not None
def logger_setup():
"""
Setup the global logging configuration
"""
logging_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# Make uvicorn conform to the global logging configuration
uvicorn_logger = logging.getLogger('uvicorn')
uvicorn_logger.propagate = True
uvicorn_logger.handlers = [] # Remove default handlers to avoid duplicate logs
if is_kubernetes:
# in that case we want to log to stdout and also to loki
from loki_logger_handler.loki_logger_handler import LokiLoggerHandler
loki_url = os.getenv('LOKI_URL')
if loki_url is None:
raise ValueError("LOKI_URL environment variable is not set")
loki_handler = LokiLoggerHandler(
url = loki_url,
labels = {'app': 'anyway', 'environment': 'staging' if is_debug else 'production'}
)
logger.info(f"Logging to Loki at {loki_url} with {loki_handler.labels} and {is_debug=}")
if is_debug:
logging.basicConfig(
format = logging_format,
level = logging.DEBUG,
handlers = [loki_handler, logging.StreamHandler()]
)
# we need to silence the debug logs made by the loki handler
logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
else:
logging.basicConfig(
format = logging_format,
level = logging.INFO,
handlers = [loki_handler, logging.StreamHandler()]
)
else:
# if we are in a debug (local) session, set verbose and rich logging
from rich.logging import RichHandler
logging.basicConfig(
format = logging_format,
level = logging.DEBUG,
handlers = [RichHandler()]
)
def uvicorn_run():
"""
Run the FastAPI application using uvicorn
"""
num_workers = os.getenv('NUM_WORKERS', 1)
logger.info(f"Starting FastAPI+uvicorn with {num_workers=}, {is_debug=}, {is_kubernetes=}")
uvicorn.run(
# we could in theory directly import the app and pass it as an object
# this 'import string' is required for hot reloading and scaling
'src.main:app',
host = '0.0.0.0',
port = 8000,
log_config = None,
access_log = True,
# Disable uvicorn's logging configuration so that it inherits the global one
workers = num_workers,
# Hot reload breaks logging, so we leave it disabled
# reload = is_debug
)
if __name__ == '__main__':
logger_setup()
uvicorn_run()