use additional loki logger #48
| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -9,18 +9,16 @@ | |||||||
|             "name": "Backend - debug", |             "name": "Backend - debug", | ||||||
|             "type": "debugpy", |             "type": "debugpy", | ||||||
|             "request": "launch", |             "request": "launch", | ||||||
|             "module": "uvicorn", |  | ||||||
|             "env": { |             "env": { | ||||||
|                 "DEBUG": "true" |                 "DEBUG": "true" | ||||||
|             }, |             }, | ||||||
|             "args": [ |  | ||||||
|                 // "--app-dir", |  | ||||||
|                 // "src", |  | ||||||
|                 "src.main:app", |  | ||||||
|                 "--reload", |  | ||||||
|             ], |  | ||||||
|             "jinja": true, |             "jinja": true, | ||||||
|             "cwd": "${workspaceFolder}/backend" |             "cwd": "${workspaceFolder}/backend", | ||||||
|  |             "module": "fastapi", | ||||||
|  |             "args": [ | ||||||
|  |                 "dev", | ||||||
|  |                 "src/main.py" | ||||||
|  |             ] | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "Backend - tester", |             "name": "Backend - tester", | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +0,0 @@ | |||||||
| { |  | ||||||
|     "cmake.ignoreCMakeListsMissing": true |  | ||||||
| } |  | ||||||
| @@ -14,5 +14,7 @@ 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 | ||||||
|  |  | ||||||
|  | # explicitly use a string instead of an argument list to force a shell and variable expansion | ||||||
| CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS | CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS | ||||||
|   | |||||||
| @@ -25,3 +25,4 @@ pymemcache = "*" | |||||||
| fastapi-cli = "*" | fastapi-cli = "*" | ||||||
| scikit-learn = "*" | scikit-learn = "*" | ||||||
| pyqt6 = "*" | pyqt6 = "*" | ||||||
|  | loki-logger-handler = "*" | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								backend/Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								backend/Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|     "_meta": { |     "_meta": { | ||||||
|         "hash": { |         "hash": { | ||||||
|             "sha256": "bb22b4e28c7aa199c94b688ad93d3ab0ccf1089a172131f4aec03b78e7bd7f1c" |             "sha256": "6edd6644586e8814a0b4526adb3352dfc17828ca129de7a68c1d5929efe94daa" | ||||||
|         }, |         }, | ||||||
|         "pipfile-spec": 6, |         "pipfile-spec": 6, | ||||||
|         "requires": {}, |         "requires": {}, | ||||||
| @@ -507,6 +507,15 @@ | |||||||
|             "markers": "python_version >= '3.8'", |             "markers": "python_version >= '3.8'", | ||||||
|             "version": "==1.4.7" |             "version": "==1.4.7" | ||||||
|         }, |         }, | ||||||
|  |         "loki-logger-handler": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:aa1a9c933282c134a1e4271aba3cbaa2a3660eab6ea415bad7a072444ab98aa8", | ||||||
|  |                 "sha256:f6114727a9e5e6f3f2058b9b5324d1cab6d1a04e802079f7b57a8aeb7bd0a112" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "markers": "python_version >= '2.7'", | ||||||
|  |             "version": "==1.0.2" | ||||||
|  |         }, | ||||||
|         "lxml": { |         "lxml": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e", |                 "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e", | ||||||
|   | |||||||
 Submodule backend/deployment updated: 718df09e88...904f16bfc0
									
								
							
							
								
								
									
										1094
									
								
								backend/report.html
									
									
									
									
									
								
							
							
						
						
									
										1094
									
								
								backend/report.html
									
									
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,6 +1,5 @@ | |||||||
| """Module allowing to access the parameters of route generation""" | """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,21 +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 session, set verbose and rich logging |  | ||||||
| if os.getenv('DEBUG', "false") == "true": |  | ||||||
|     from rich.logging import RichHandler |  | ||||||
|     logging.basicConfig( |  | ||||||
|         level=logging.DEBUG, |  | ||||||
|         format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |  | ||||||
|         handlers=[RichHandler()] |  | ||||||
|     ) |  | ||||||
| else: |  | ||||||
|     logging.basicConfig( |  | ||||||
|         level=logging.INFO, |  | ||||||
|         format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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 | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								backend/src/logging_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								backend/src/logging_config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | """Sets up global logging configuration for the application.""" | ||||||
|  |  | ||||||
|  | import logging | ||||||
|  | import os | ||||||
|  |  | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def configure_logging(): | ||||||
|  |     """ | ||||||
|  |     Called at startup of a FastAPI application instance to setup logging. Depending on the environment, it will log to stdout or to Loki. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     is_debug = os.getenv('DEBUG', "false") == "true" | ||||||
|  |     is_kubernetes = os.getenv('KUBERNETES_SERVICE_HOST') is not None | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     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') | ||||||
|  |         loki_url = "http://localhost:3100/loki/api/v1/push" | ||||||
|  |         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=}") | ||||||
|  |         logging_handlers = [loki_handler, logging.StreamHandler()] | ||||||
|  |         logging_level = logging.DEBUG if is_debug else logging.INFO | ||||||
|  |         # silence the chatty logs loki generates itself | ||||||
|  |         logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) | ||||||
|  |         # no need for time since it's added by loki or can be shown in kube logs | ||||||
|  |         logging_format = '%(name)s - %(levelname)s - %(message)s' | ||||||
|  |  | ||||||
|  |     else: | ||||||
|  |         # if we are in a debug (local) session, set verbose and rich logging | ||||||
|  |         from rich.logging import RichHandler | ||||||
|  |         logging_handlers = [RichHandler()] | ||||||
|  |         logging_level = logging.DEBUG if is_debug else logging.INFO | ||||||
|  |         logging_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     logging.basicConfig( | ||||||
|  |         level = logging_level, | ||||||
|  |         format = logging_format, | ||||||
|  |         handlers = logging_handlers | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     # also overwrite the uvicorn loggers | ||||||
|  |     logging.getLogger('uvicorn').handlers = logging_handlers | ||||||
|  |     logging.getLogger('uvicorn.access').handlers = logging_handlers | ||||||
|  |     logging.getLogger('uvicorn.error').handlers = logging_handlers | ||||||
|  |  | ||||||
| @@ -2,7 +2,9 @@ | |||||||
|  |  | ||||||
| import logging | import logging | ||||||
| from fastapi import FastAPI, HTTPException, Query | from fastapi import FastAPI, HTTPException, Query | ||||||
|  | from contextlib import asynccontextmanager | ||||||
|  |  | ||||||
|  | from .logging_config import configure_logging | ||||||
| from .structs.landmark import Landmark, Toilets | from .structs.landmark import Landmark, Toilets | ||||||
| from .structs.preferences import Preferences | from .structs.preferences import Preferences | ||||||
| from .structs.linked_landmarks import LinkedLandmarks | from .structs.linked_landmarks import LinkedLandmarks | ||||||
| @@ -11,17 +13,28 @@ from .utils.landmarks_manager import LandmarkManager | |||||||
| from .utils.toilets_manager import ToiletsManager | from .utils.toilets_manager import ToiletsManager | ||||||
| from .utils.optimizer import Optimizer | from .utils.optimizer import Optimizer | ||||||
| from .utils.refiner import Refiner | from .utils.refiner import Refiner | ||||||
| from .persistence import client as cache_client | from .cache import client as cache_client | ||||||
|  |  | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| app = FastAPI() |  | ||||||
| manager = LandmarkManager() | manager = LandmarkManager() | ||||||
| optimizer = Optimizer() | optimizer = Optimizer() | ||||||
| refiner = Refiner(optimizer=optimizer) | refiner = Refiner(optimizer=optimizer) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @asynccontextmanager | ||||||
|  | async def lifespan(app: FastAPI): | ||||||
|  |     """Function to run at the start of the app""" | ||||||
|  |     logger.info("Setting up logging") | ||||||
|  |     configure_logging() | ||||||
|  |     yield | ||||||
|  |     logger.info("Shutting down logging") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | app = FastAPI(lifespan=lifespan) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.post("/trip/new") | @app.post("/trip/new") | ||||||
| def new_trip(preferences: Preferences, | def new_trip(preferences: Preferences, | ||||||
|              start: tuple[float, float], |              start: tuple[float, float], | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| """Definition of the Landmark class to handle visitable objects across the world.""" | """Definition of the Landmark class to handle visitable objects across the world.""" | ||||||
|  |  | ||||||
| from typing import Optional, Literal | from typing import Optional, Literal | ||||||
| from uuid import uuid4 | from uuid import uuid4, UUID | ||||||
| from pydantic import BaseModel, Field | from pydantic import BaseModel, Field | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -29,12 +29,12 @@ class Landmark(BaseModel) : | |||||||
|         description (Optional[str]): A text description of the landmark. |         description (Optional[str]): A text description of the landmark. | ||||||
|         duration (Optional[int]): The estimated time to visit the landmark (in minutes). |         duration (Optional[int]): The estimated time to visit the landmark (in minutes). | ||||||
|         name_en (Optional[str]): The English name of the landmark. |         name_en (Optional[str]): The English name of the landmark. | ||||||
|         uuid (str): A unique identifier for the landmark, generated by default using uuid4. |         uuid (UUID): A unique identifier for the landmark, generated by default using uuid4. | ||||||
|         must_do (Optional[bool]): Whether the landmark is a "must-do" attraction. |         must_do (Optional[bool]): Whether the landmark is a "must-do" attraction. | ||||||
|         must_avoid (Optional[bool]): Whether the landmark should be avoided. |         must_avoid (Optional[bool]): Whether the landmark should be avoided. | ||||||
|         is_secondary (Optional[bool]): Whether the landmark is secondary or less important. |         is_secondary (Optional[bool]): Whether the landmark is secondary or less important. | ||||||
|         time_to_reach_next (Optional[int]): Estimated time (in minutes) to reach the next landmark. |         time_to_reach_next (Optional[int]): Estimated time (in minutes) to reach the next landmark. | ||||||
|         next_uuid (Optional[str]): UUID of the next landmark in sequence (if applicable). |         next_uuid (Optional[UUID]): UUID of the next landmark in sequence (if applicable). | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     # Properties of the landmark |     # Properties of the landmark | ||||||
| @@ -52,7 +52,7 @@ class Landmark(BaseModel) : | |||||||
|     name_en : Optional[str] = None |     name_en : Optional[str] = None | ||||||
|  |  | ||||||
|     # Unique ID of a given landmark |     # Unique ID of a given landmark | ||||||
|     uuid: str = Field(default_factory=uuid4) |     uuid: UUID = Field(default_factory=uuid4) | ||||||
|  |  | ||||||
|     # Additional properties depending on specific tour |     # Additional properties depending on specific tour | ||||||
|     must_do : Optional[bool] = False |     must_do : Optional[bool] = False | ||||||
| @@ -60,7 +60,7 @@ class Landmark(BaseModel) : | |||||||
|     is_secondary : Optional[bool] = False |     is_secondary : Optional[bool] = False | ||||||
|  |  | ||||||
|     time_to_reach_next : Optional[int] = 0 |     time_to_reach_next : Optional[int] = 0 | ||||||
|     next_uuid : Optional[str] = None |     next_uuid : Optional[UUID] = None | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         """ |         """ | ||||||
| @@ -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 | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| """Definition of the Trip class.""" | """Definition of the Trip class.""" | ||||||
|  |  | ||||||
| import uuid | from uuid import uuid4, UUID | ||||||
| from pydantic import BaseModel, Field | from pydantic import BaseModel, Field | ||||||
| from pymemcache.client.base import Client | from pymemcache.client.base import Client | ||||||
|  |  | ||||||
| @@ -19,9 +19,9 @@ class Trip(BaseModel): | |||||||
|     Methods: |     Methods: | ||||||
|         from_linked_landmarks: create a Trip from LinkedLandmarks object. |         from_linked_landmarks: create a Trip from LinkedLandmarks object. | ||||||
|     """ |     """ | ||||||
|     uuid: str = Field(default_factory=uuid.uuid4) |     uuid: UUID = Field(default_factory=uuid4) | ||||||
|     total_time: int |     total_time: int | ||||||
|     first_landmark_uuid: str |     first_landmark_uuid: UUID | ||||||
|  |  | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
| @@ -31,7 +31,7 @@ class Trip(BaseModel): | |||||||
|         """ |         """ | ||||||
|         trip = Trip( |         trip = Trip( | ||||||
|             total_time = landmarks.total_time, |             total_time = landmarks.total_time, | ||||||
|             first_landmark_uuid = str(landmarks[0].uuid) |             first_landmark_uuid = landmarks[0].uuid | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Store the trip in the cache |         # Store the trip in the cache | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ from fastapi import HTTPException | |||||||
| from pydantic import ValidationError | from pydantic import ValidationError | ||||||
|  |  | ||||||
| from ..structs.landmark import Landmark | from ..structs.landmark import Landmark | ||||||
| from ..persistence import client as cache_client | from ..cache import client as cache_client | ||||||
|  |  | ||||||
|  |  | ||||||
| def landmarks_to_osmid(landmarks: list[Landmark]) -> list[int] : | def landmarks_to_osmid(landmarks: list[Landmark]) -> list[int] : | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user