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
83 lines
3.0 KiB
Python
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()
|