logging cleanup
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
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
This commit is contained in:
parent
c448e2dfb7
commit
fa083a1080
@ -25,8 +25,6 @@ jobs:
|
|||||||
ls -la
|
ls -la
|
||||||
# only install dev-packages
|
# only install dev-packages
|
||||||
pipenv install --categories=dev-packages
|
pipenv install --categories=dev-packages
|
||||||
pipenv run pip freeze
|
|
||||||
|
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
|
@ -25,7 +25,6 @@ jobs:
|
|||||||
ls -la
|
ls -la
|
||||||
# install all packages, including dev-packages
|
# install all packages, including dev-packages
|
||||||
pipenv install --dev
|
pipenv install --dev
|
||||||
pipenv run pip freeze
|
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
|
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@ -4,21 +4,15 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
// backend - python using fastapi
|
// backend - python app that launches a uvicorn server
|
||||||
{
|
{
|
||||||
"name": "Backend - debug",
|
"name": "Backend - debug",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "uvicorn",
|
"program": "launcher.py",
|
||||||
"env": {
|
"env": {
|
||||||
"DEBUG": "true"
|
"DEBUG": "true"
|
||||||
},
|
},
|
||||||
"args": [
|
|
||||||
// "--app-dir",
|
|
||||||
// "src",
|
|
||||||
"src.main:app",
|
|
||||||
"--reload",
|
|
||||||
],
|
|
||||||
"jinja": true,
|
"jinja": true,
|
||||||
"cwd": "${workspaceFolder}/backend"
|
"cwd": "${workspaceFolder}/backend"
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ RUN pip install pipenv
|
|||||||
RUN pipenv install --deploy --system
|
RUN pipenv install --deploy --system
|
||||||
|
|
||||||
COPY src src
|
COPY src src
|
||||||
|
COPY launcher.py .
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
@ -14,5 +15,6 @@ EXPOSE 8000
|
|||||||
ENV NUM_WORKERS=1
|
ENV NUM_WORKERS=1
|
||||||
ENV OSM_CACHE_DIR=/cache
|
ENV OSM_CACHE_DIR=/cache
|
||||||
ENV MEMCACHED_HOST_PATH=none
|
ENV MEMCACHED_HOST_PATH=none
|
||||||
|
ENV LOKI_URL=none
|
||||||
|
|
||||||
CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS
|
CMD ["python", "launcher.py"]
|
||||||
|
82
backend/launcher.py
Normal file
82
backend/launcher.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""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()
|
29
backend/logging_config.yaml
Normal file
29
backend/logging_config.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
version: 1
|
||||||
|
disable_existing_loggers: False
|
||||||
|
formatters:
|
||||||
|
standard:
|
||||||
|
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
handlers:
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: standard
|
||||||
|
level: DEBUG
|
||||||
|
rich:
|
||||||
|
class: rich.logging.RichHandler
|
||||||
|
level: DEBUG
|
||||||
|
loki:
|
||||||
|
class: loki_logger_handler.loki_logger_handler.LokiLoggerHandler
|
||||||
|
level: DEBUG
|
||||||
|
formatter: standard
|
||||||
|
url: ${LOKI_URL}
|
||||||
|
labels:
|
||||||
|
app: anyway
|
||||||
|
environment: ${ENVIRONMENT}
|
||||||
|
loggers:
|
||||||
|
uvicorn:
|
||||||
|
handlers: [console, rich, loki]
|
||||||
|
level: DEBUG
|
||||||
|
propagate: False
|
||||||
|
root:
|
||||||
|
handlers: [console, rich, loki]
|
||||||
|
level: DEBUG
|
@ -1,6 +1,5 @@
|
|||||||
"""Module setting global parameters for the application, such as logging, cache, route generation, etc."""
|
"""Module setting global parameters for the application such as cache, route generation, etc."""
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -16,43 +15,6 @@ cache_dir_string = os.getenv('OSM_CACHE_DIR', './cache')
|
|||||||
OSM_CACHE_DIR = Path(cache_dir_string)
|
OSM_CACHE_DIR = Path(cache_dir_string)
|
||||||
|
|
||||||
|
|
||||||
# if we are in a debug (local) session, set verbose and rich logging
|
|
||||||
debug = os.getenv('DEBUG', "false") == "true"
|
|
||||||
if os.getenv('KUBERNETES_SERVICE_HOST', None) is not None:
|
|
||||||
# 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': 'production' if debug else 'staging'}
|
|
||||||
)
|
|
||||||
print(f"Logging to Loki at {loki_url} with {loki_handler.labels} and {debug=}")
|
|
||||||
if debug:
|
|
||||||
# we need to silence the debug logs made by the loki handler
|
|
||||||
logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
|
||||||
handlers=[loki_handler, logging.StreamHandler()]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
handlers=[loki_handler, logging.StreamHandler()]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# in that case we are local and we want to log to stdout only, but make it pretty
|
|
||||||
from rich.logging import RichHandler
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[RichHandler()]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
MEMCACHED_HOST_PATH = os.getenv('MEMCACHED_HOST_PATH', None)
|
MEMCACHED_HOST_PATH = os.getenv('MEMCACHED_HOST_PATH', None)
|
||||||
if MEMCACHED_HOST_PATH == "none":
|
if MEMCACHED_HOST_PATH == "none":
|
||||||
MEMCACHED_HOST_PATH = None
|
MEMCACHED_HOST_PATH = None
|
||||||
|
@ -139,4 +139,4 @@ class Toilets(BaseModel) :
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
# This allows us to easily convert the model to and from dictionaries
|
# This allows us to easily convert the model to and from dictionaries
|
||||||
orm_mode = True
|
from_attributes = True
|
Loading…
x
Reference in New Issue
Block a user