-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add logging endpoint #41
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,7 @@ | ||
repos: | ||
- repo: https://github.com/ambv/black | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just use ruff for linting and formatting |
||
rev: 23.9.1 | ||
hooks: | ||
- id: black | ||
language_version: python3 | ||
- repo: https://github.com/astral-sh/ruff-pre-commit | ||
# Ruff version. | ||
rev: v0.0.289 | ||
rev: v0.7.4 | ||
hooks: | ||
- id: ruff | ||
args: [--fix] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,21 @@ | ||
#--- BEGIN Usual Python stuff --- | ||
|
||
FROM ghcr.io/valhalla/valhalla:latest as builder | ||
LABEL [email protected] | ||
FROM ghcr.io/valhalla/valhalla:latest AS builder | ||
LABEL maintainer="Nils Nolde <[email protected]>" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. old format was deprecated |
||
|
||
WORKDIR /app | ||
|
||
# Install vis | ||
RUN apt-get update -y > /dev/null && \ | ||
apt-get install -y \ | ||
apt-transport-https \ | ||
ca-certificates \ | ||
python-is-python3 \ | ||
python3-pip \ | ||
python3-venv \ | ||
curl > /dev/null && \ | ||
python -m pip install --upgrade pip --break-system-packages | ||
apt-transport-https \ | ||
ca-certificates \ | ||
python-is-python3 \ | ||
python3-pip \ | ||
python3-venv \ | ||
curl > /dev/null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also contains the ubuntu 24.04 lts update |
||
|
||
ENV POETRY_BIN /root/.local/bin/poetry | ||
ENV POETRY_BIN=/root/.local/bin/poetry | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. old format was deprecated |
||
|
||
RUN curl -sSL https://install.python-poetry.org | python && \ | ||
$POETRY_BIN config virtualenvs.create false && \ | ||
|
@@ -42,21 +41,21 @@ RUN . app_venv/bin/activate && \ | |
|
||
# remove some stuff from the original image | ||
RUN cd /usr/local/bin && \ | ||
preserve="valhalla_service valhalla_build_tiles valhalla_build_config valhalla_build_admins valhalla_build_timezones valhalla_build_elevation valhalla_ways_to_edges valhalla_build_extract valhalla_export_edges valhalla_add_predicted_traffic" && \ | ||
mv $preserve .. && \ | ||
for f in valhalla*; do rm $f; done && \ | ||
cd .. && mv $preserve ./bin | ||
preserve="valhalla_service valhalla_build_tiles valhalla_build_config valhalla_build_admins valhalla_build_timezones valhalla_build_elevation valhalla_ways_to_edges valhalla_build_extract valhalla_export_edges valhalla_add_predicted_traffic" && \ | ||
mv $preserve .. && \ | ||
for f in valhalla*; do rm $f; done && \ | ||
cd .. && mv $preserve ./bin | ||
|
||
FROM ubuntu:23.04 as runner_base | ||
MAINTAINER Nils Nolde <[email protected]> | ||
FROM ubuntu:24.04 AS runner_base | ||
LABEL maintainer="Nils Nolde <[email protected]>" | ||
|
||
# install Valhalla stuff | ||
RUN apt-get update > /dev/null && \ | ||
export DEBIAN_FRONTEND=noninteractive && \ | ||
apt-get install -y libluajit-5.1-2 \ | ||
libzmq5 libgdal-dev libczmq4 spatialite-bin libprotobuf-lite32 sudo locales wget \ | ||
libsqlite3-0 libsqlite3-mod-spatialite libcurl4 python-is-python3 osmctools \ | ||
python3.11-minimal python3-distutils curl unzip moreutils jq spatialite-bin supervisor > /dev/null | ||
apt-get install -y libluajit-5.1-dev \ | ||
libzmq5 libgdal-dev libczmq4 spatialite-bin libprotobuf-lite32 sudo locales wget \ | ||
libsqlite3-0 libsqlite3-mod-spatialite libcurl4 python-is-python3 osmctools \ | ||
python3.12-minimal curl unzip moreutils jq spatialite-bin supervisor > /dev/null | ||
|
||
WORKDIR /app | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,17 +15,14 @@ | |
from routing_packager_app import SETTINGS | ||
from routing_packager_app.db import get_db | ||
from routing_packager_app.api_v1.models import Job, User | ||
from routing_packager_app.logger import AppSmtpHandler, get_smtp_details | ||
from routing_packager_app.logger import AppSmtpHandler, get_smtp_details, LOGGER | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consistent use of the logger throughout the app |
||
from routing_packager_app.utils.geom_utils import wkbe_to_geom, wkbe_to_str | ||
|
||
JOB_TIMEOUT = 60 * 60 # one hour to compress a single graph | ||
|
||
description = "Runs the worker to update the ZIP packages." | ||
parser = ArgumentParser(description=description) | ||
|
||
# set up the logger basics | ||
LOGGER = logging.getLogger("packager") | ||
|
||
|
||
def _sort_jobs(jobs_: List[Job]): | ||
out = list() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
from routing_packager_app.logger import LOGGING_CONFIG | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is nice: gunicorn allows us to pass a custom logging config! |
||
|
||
bind = "0.0.0.0:5000" | ||
workers = 1 | ||
worker_class = "uvicorn.workers.UvicornWorker" | ||
logconfig_dict = LOGGING_CONFIG |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,7 @@ async def lifespan(app: FastAPI): | |
p.mkdir(exist_ok=True) | ||
SETTINGS.get_output_path().mkdir(exist_ok=True) | ||
yield | ||
app.state.redis_pool.shutdown() | ||
await app.state.redis_pool.shutdown() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was causing a warning |
||
|
||
|
||
app: FastAPI = create_app(lifespan=lifespan) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
from fastapi import APIRouter | ||
|
||
from .routes import users, jobs | ||
from .routes import jobs, logs, users | ||
|
||
api_v1_router = APIRouter() | ||
api_v1_router.include_router(jobs.router, prefix="/jobs", tags=["jobs"]) | ||
api_v1_router.include_router(users.router, prefix="/users", tags=["users"]) | ||
api_v1_router.include_router(logs.router, prefix="/logs", tags=["logs"]) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
from datetime import datetime | ||
from typing import Optional, List | ||
from enum import Enum | ||
from typing import List, Optional | ||
|
||
from fastapi.security import HTTPBasicCredentials | ||
from geoalchemy2 import Geography | ||
from pydantic import EmailStr | ||
from sqlalchemy import Column | ||
from sqlalchemy_utils import PasswordType | ||
from sqlmodel import SQLModel, Field, DateTime, Relationship, Session, select, AutoString | ||
from sqlmodel import AutoString, DateTime, Field, Relationship, Session, SQLModel, select | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ruff also does isort's job now |
||
|
||
from ..config import SETTINGS | ||
from ..constants import Providers, Statuses | ||
|
@@ -108,3 +109,9 @@ def add_admin_user(session: Session): | |
admin_user = User(email=admin_email, password=admin_pass) | ||
session.add(admin_user) | ||
session.commit() | ||
|
||
|
||
class LogType(str, Enum): | ||
WORKER = "worker" | ||
APP = "app" | ||
BUILDER = "builder" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from fastapi.responses import PlainTextResponse | ||
from fastapi.security import HTTPBasicCredentials | ||
from sqlmodel import Session | ||
from starlette.status import ( | ||
HTTP_400_BAD_REQUEST, | ||
HTTP_401_UNAUTHORIZED, | ||
) | ||
|
||
from ...auth.basic_auth import BasicAuth | ||
from ...config import SETTINGS | ||
from ...db import get_db | ||
from ..models import LogType, User | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get("/{log_type}", response_class=PlainTextResponse) | ||
def get_logs( | ||
log_type: LogType, | ||
lines: int | None = None, | ||
db: Session = Depends(get_db), | ||
auth: HTTPBasicCredentials = Depends(BasicAuth), | ||
): | ||
# first authenticate | ||
req_user = User.get_user(db, auth) | ||
if not req_user: | ||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Not authorized to read logs.") | ||
|
||
# figure out the type of logs | ||
log_file = SETTINGS.get_logging_dir() / f"{log_type.value}.log" | ||
|
||
try: | ||
with open(log_file) as fh: | ||
if lines is None: | ||
return fh.read() | ||
line_count = len([1 for _ in fh.readlines()]) | ||
start_i = line_count - lines if line_count > lines else 0 | ||
response = "" | ||
fh.seek(0) | ||
for i, line in enumerate(fh.readlines()): | ||
if i < start_i: | ||
continue | ||
response += line | ||
return response | ||
|
||
except: # noqa | ||
return HTTP_400_BAD_REQUEST(f"Unable to open {log_file}.") |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ class BaseSettings(_BaseSettings): | |
|
||
DESCRIPTION_PATH: Path = BASE_DIR.joinpath("DESCRIPTION.md") | ||
|
||
### APP ### | ||
# APP ### | ||
ADMIN_EMAIL: str = "[email protected]" | ||
ADMIN_PASS: str = "admin" | ||
# TODO: clarify if there's a need to restrict origins | ||
|
@@ -30,15 +30,15 @@ class BaseSettings(_BaseSettings): | |
|
||
ENABLED_PROVIDERS: list[str] = list(CommaSeparatedStrings("osm")) | ||
|
||
### DATABASES ### | ||
# DATABASES ### | ||
POSTGRES_HOST: str = "localhost" | ||
POSTGRES_PORT: int = 5432 | ||
POSTGRES_DB: str = "gis" | ||
POSTGRES_USER: str = "admin" | ||
POSTGRES_PASS: str = "admin" | ||
REDIS_URL: str = "redis://localhost" | ||
|
||
### SMTP ### | ||
# SMTP ### | ||
SMTP_HOST: str = "localhost" | ||
SMTP_PORT: int = 1025 | ||
SMTP_FROM: str = "[email protected]" | ||
|
@@ -79,6 +79,19 @@ def get_tmp_data_dir(self) -> Path: | |
|
||
return tmp_data_dir | ||
|
||
def get_logging_dir(self) -> Path: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Internally all logs are written into the |
||
""" | ||
Gets the path where logs are stored for both worker and builder/app | ||
""" | ||
tmp_data_dir = self.TMP_DATA_DIR | ||
if os.path.isdir("/app") and not os.getenv("CI", None): # pragma: no cover | ||
tmp_data_dir = Path("/app/tmp_data") | ||
log_dir = tmp_data_dir / "logs" | ||
|
||
log_dir.mkdir(exist_ok=True) | ||
|
||
return log_dir | ||
|
||
|
||
class ProdSettings(BaseSettings): | ||
model_config = SettingsConfigDict(case_sensitive=True, env_file=ENV_FILE, extra="ignore") | ||
|
@@ -108,7 +121,6 @@ class TestSettings(BaseSettings): | |
|
||
# decide which settings we'll use | ||
SETTINGS: Optional[BaseSettings] = None | ||
print("LOADING SETTINGS") | ||
env = os.getenv("API_CONFIG", "prod") | ||
if env == "prod": # pragma: no cover | ||
SETTINGS = ProdSettings() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
superfluous