From c92e23241803034e0c3ef3d72ed430b1646eb063 Mon Sep 17 00:00:00 2001 From: Giorgio Ravera Date: Wed, 11 Mar 2026 19:35:43 +0100 Subject: [PATCH] better version of logger management --- backend/{main.py => app.py} | 203 +++++++++++------------------------- backend/db/aliases.py | 7 +- backend/db/db.py | 11 +- backend/db/hosts.py | 7 +- backend/db/users.py | 7 +- backend/routes/aliases.py | 13 +-- backend/routes/backup.py | 7 +- backend/routes/dhcp.py | 10 +- backend/routes/dns.py | 9 +- backend/routes/health.py | 10 +- backend/routes/hosts.py | 13 +-- backend/security.py | 11 +- log/log.py | 5 - 13 files changed, 116 insertions(+), 197 deletions(-) rename backend/{main.py => app.py} (56%) diff --git a/backend/main.py b/backend/app.py similarity index 56% rename from backend/main.py rename to backend/app.py index 846847e..41eef47 100644 --- a/backend/main.py +++ b/backend/app.py @@ -1,4 +1,4 @@ -# backend/main.py +# backend/app.py # import standard modules from contextlib import asynccontextmanager @@ -6,8 +6,9 @@ from fastapi import FastAPI, Request, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, RedirectResponse, JSONResponse, Response from starlette.middleware.base import BaseHTTPMiddleware -import logging +from typing import Callable import os + # Import Routers from backend.routes.about import router as about_router from backend.routes.backup import router as backup_router @@ -17,124 +18,22 @@ from backend.routes.hosts import router as hosts_router from backend.routes.aliases import router as aliases_router from backend.routes.dns import router as dns_router from backend.routes.dhcp import router as dhcp_router + # Import Security from backend.security import is_logged_in, apply_session -# Import Settings -from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger - -# ------------------------------------------------------------------------------ -# Logging setup -# ------------------------------------------------------------------------------ -setup_logging(level=settings.LOG_LEVEL, to_file=settings.LOG_TO_FILE, log_file=settings.LOG_FILE, log_access_file=settings.LOG_ACCESS_FILE) -logger = get_logger("backend.main") - -# ------------------------------------------------------------------------------ -# Welcome log -# ------------------------------------------------------------------------------ -def print_welcome(): - masked_secret = "****" if settings.SECRET_KEY else "undefined" - masked_admin_pwd = "****" if settings.ADMIN_PASSWORD else "undefined" - masked_admin_hash = "****" if settings.ADMIN_PASSWORD_HASH else "undefined" - - logger.info( - "%s starting | app_version=%s | baseimg_version=%s", - settings.APP_NAME, settings.APP_VERSION, settings.BASEIMG_VERSION - ) - logger.info( - "App settings: frontend=%s | port=%d | secret=%s", - settings.FRONTEND_DIR, settings.HTTP_PORT, masked_secret - ) - logger.info( - "Database: file=%s | reset=%s", - settings.DB_FILE, settings.DB_RESET - ) - logger.info( - "Log: level=%s, to_file=%s, file=%s", - settings.LOG_LEVEL, settings.LOG_TO_FILE, settings.LOG_FILE - ) - logger.info( - "Users: admin=%s | password=%s | hash=%s | hash_file=%s", - settings.ADMIN_USER, masked_admin_pwd, masked_admin_hash, settings.ADMIN_PASSWORD_HASH_FILE - ) - logger.info( - "DNS: host file=%s | alias file=%s | reverse file=%s", - settings.DNS_HOST_FILE, settings.DNS_ALIAS_FILE, settings.DNS_REVERSE_FILE - ) - logger.info( - "DHCP: ipv4 host file=%s | ipv4 leases file=%s | ipv6 host file=%s | ipv6 leases file=%s", - settings.DHCP4_HOST_FILE, settings.DHCP4_LEASES_FILE, settings.DHCP6_HOST_FILE, settings.DHCP6_LEASES_FILE - ) -# ------------------------------------------------------------------------------ -# Shutdown log -# ------------------------------------------------------------------------------ -def print_goodbye(): - logger = get_logger("backend.main") - - logger.info( - "Application %s shutting down | app_version=%s", - settings.APP_NAME, settings.APP_VERSION - ) - -# ------------------------------------------------------------------------------ -# Lifespan for startup and shutdown events -# ------------------------------------------------------------------------------ -@asynccontextmanager -async def lifespan(app: FastAPI): - # STARTUP - print_welcome() - - try: - yield - finally: - # SHUTDOWN - print_goodbye() - -# ------------------------------------------------------------------------------ -# App init -# ------------------------------------------------------------------------------ -app = FastAPI( - title=settings.APP_NAME, - version=settings.APP_VERSION, - lifespan=lifespan, -) +# Import Settings & Logging +from settings.settings import settings +from log.log import get_logger -# ------------------------------------------------------------------------------ -# Routers -# ------------------------------------------------------------------------------ -app.include_router(about_router) -app.include_router(backup_router) -app.include_router(health_router) -app.include_router(login_router) -app.include_router(hosts_router) -app.include_router(aliases_router) -app.include_router(dns_router) -app.include_router(dhcp_router) - -# ------------------------------------------------------------------------------ -# CORS -# ------------------------------------------------------------------------------ -cors_origins = [ - f"http://localhost:{settings.HTTP_PORT}", - f"http://127.0.0.1:{settings.HTTP_PORT}", - # aggiungi qui host reali quando servi da hostname: - # "http://miohost:8000", "https://dominio.tld" -] -app.add_middleware( - CORSMiddleware, - allow_origins=cors_origins, - allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], - allow_headers=["Content-Type", "Authorization"], - allow_credentials=True, -) +# Logger initialization +logger = get_logger(__name__) # ------------------------------------------------------------------------------ # Security Headers middleware (basic hardening) # ------------------------------------------------------------------------------ class SecurityHeadersMiddleware(BaseHTTPMiddleware): - async def dispatch(self, request: Request, call_next): + async def dispatch(self, request: Request, call_next: Callable): response: Response = await call_next(request) # Hardening base @@ -171,9 +70,6 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware): ) return response -# GRGR -> to be enabled in production -#app.add_middleware(SecurityHeadersMiddleware) - # ------------------------------------------------------------------------------ # Public paths # ------------------------------------------------------------------------------ @@ -199,7 +95,6 @@ STATIC_SUFFIXES = (".js", ".css", ".png", ".jpg", ".jpeg", ".ico", ".svg", ".map # ------------------------------------------------------------------------------ # Session / Auth middleware # ------------------------------------------------------------------------------ -@app.middleware("http") async def session_middleware(request: Request, call_next): path = request.url.path method = request.method.upper() @@ -265,60 +160,80 @@ async def session_middleware(request: Request, call_next): return response # ------------------------------------------------------------------------------ -# FRONTEND (FileResponse): pages and assets +# FRONTEND Handlers # ------------------------------------------------------------------------------ # Homepage -@app.get("/") def home(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "hosts.html")) # CSS variables -@app.get("/css/variables.css") def css_variables(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "css/variables.css")) # CSS Layout -@app.get("/css/layout.css") def css_layout(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "css/layout.css")) # JS Common -@app.get("/js/common.js") def js_common(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "js/common.js")) # JS Services -@app.get("/js/services.js") def js_services(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "js/services.js")) # favicon -@app.get("/favicon.ico") def favicon(request: Request): return FileResponse(os.path.join(settings.FRONTEND_DIR, "favicon.ico")) # ------------------------------------------------------------------------------ -# Entry-point +# Creates and configures the FastAPI app # ------------------------------------------------------------------------------ -if __name__ == "__main__": - import uvicorn - - # Uvicorn config da settings con fallback - host = getattr(settings, "HTTP_HOST", "0.0.0.0") - port = int(getattr(settings, "HTTP_PORT", 8000)) - reload_flag = bool(getattr(settings, "DEV_RELOAD", False)) - - logger.info(f"Server running on http://{host}:{port} (reload={reload_flag})") - - uvicorn.run( - app, - host=host, - port=port, - reload=reload_flag, - proxy_headers=True, - forwarded_allow_ips="*", - log_level=(settings.LOG_LEVEL or "info").lower(), - #access_log=True, - log_config=None, - # workers=1, # GRGR in prod valuta gunicorn+uvicorn workers +def create_app() -> FastAPI: + + # App init + app = FastAPI( + title=settings.APP_NAME, + version=settings.APP_VERSION, + ) + + # Routers + app.include_router(about_router) + app.include_router(backup_router) + app.include_router(health_router) + app.include_router(login_router) + app.include_router(hosts_router) + app.include_router(aliases_router) + app.include_router(dns_router) + app.include_router(dhcp_router) + + # CORS + cors_origins = [ + f"http://localhost:{settings.HTTP_PORT}", + f"http://127.0.0.1:{settings.HTTP_PORT}", + # Aggiungi qui eventuali host reali: + # "http://miohost:8000", "https://dominio.tld" + ] + app.add_middleware( + CORSMiddleware, + allow_origins=cors_origins, + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allow_headers=["Content-Type", "Authorization"], + allow_credentials=True, ) + + # Security headers (GRGR -> to be enabled in production) + # app.add_middleware(SecurityHeadersMiddleware) + + # Session/Auth middleware (funzionale) + app.middleware("http")(session_middleware) + + # Route per file del frontend + app.add_api_route("/", home, methods=["GET"]) + app.add_api_route("/css/variables.css", css_variables, methods=["GET"]) + app.add_api_route("/css/layout.css", css_layout, methods=["GET"]) + app.add_api_route("/js/common.js", js_common, methods=["GET"]) + app.add_api_route("/js/services.js", js_services, methods=["GET"]) + app.add_api_route("/favicon.ico", favicon, methods=["GET"]) + + return app diff --git a/backend/db/aliases.py b/backend/db/aliases.py index 4c5a7cd..2852ff4 100644 --- a/backend/db/aliases.py +++ b/backend/db/aliases.py @@ -8,11 +8,13 @@ import re import sqlite3 # Import local modules from backend.db.db import get_db, register_init -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Log from log.log import get_logger +# Logger initialization +logger = get_logger(__name__) + # ----------------------------- # Check Data Input # ----------------------------- @@ -151,7 +153,6 @@ def delete_alias(alias_id: int) -> bool: # ----------------------------- @register_init def init_db_alias_table(cur): - logger = get_logger(__name__) # ALIASES TABLE cur.execute(""" diff --git a/backend/db/db.py b/backend/db/db.py index d818501..608874b 100644 --- a/backend/db/db.py +++ b/backend/db/db.py @@ -3,14 +3,19 @@ # Import standard modules import os import sqlite3 -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Log from log.log import get_logger +# Logger initialization +logger = get_logger(__name__) + _connection = None _init_functions = [] +# ----------------------------- +# Register DB Init Function +# ----------------------------- def register_init(func): _init_functions.append(func) return func @@ -31,7 +36,7 @@ def get_db(): # Init Database # ----------------------------- def init_db(): - logger = get_logger(__name__) + logger.info("Starting DB Initialization") conn = get_db() diff --git a/backend/db/hosts.py b/backend/db/hosts.py index 6decded..eed87e9 100644 --- a/backend/db/hosts.py +++ b/backend/db/hosts.py @@ -8,11 +8,13 @@ import re import sqlite3 # Import local modules from backend.db.db import get_db, register_init -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Log from log.log import get_logger +# Logger initialization +logger = get_logger(__name__) + # Regex for MAC check MAC_RE = re.compile(r"^([0-9A-Fa-f]{2}([:\-])){5}([0-9A-Fa-f]{2})$") @@ -187,7 +189,6 @@ def delete_host(host_id: int) -> bool: # ----------------------------- @register_init def init_db_hosts_table(cur): - logger = get_logger(__name__) # SETTINGS TABLE cur.execute(""" diff --git a/backend/db/users.py b/backend/db/users.py index 9b74a45..111d870 100644 --- a/backend/db/users.py +++ b/backend/db/users.py @@ -7,11 +7,13 @@ import logging import os # Import local modules from backend.db.db import get_db, register_init -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Log from log.log import get_logger +# Logger initialization +logger = get_logger(__name__) + # ================================ # Create hash password # ================================ @@ -34,7 +36,6 @@ def get_user_by_username(username): # ----------------------------- @register_init def init_db_users_table(cur): - logger = get_logger(__name__) # USERS TABLE cur.execute(""" diff --git a/backend/routes/aliases.py b/backend/routes/aliases.py index be7c2c5..cd0f686 100644 --- a/backend/routes/aliases.py +++ b/backend/routes/aliases.py @@ -14,10 +14,12 @@ from backend.db.aliases import ( update_alias, delete_alias ) -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -48,7 +50,6 @@ def api_get_aliases(request: Request): return aliases or [] except Exception as e: - logger = get_logger("aliases") logger.exception("Error getting list alias %s", str(e).strip()) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -84,7 +85,6 @@ def api_get_alias(request: Request, alias_id: int): return alias except Exception as e: - logger = get_logger("aliases") logger.exception("Error adding alias %s: %s", alias_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -141,7 +141,6 @@ def api_add_alias(request: Request, data: dict): raise httpe except Exception as e: - logger = get_logger("aliases") logger.exception("Error adding alias: %s", str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -196,7 +195,6 @@ def api_update_alias(request: Request, data: dict, alias_id: int): ) except Exception as e: - logger = get_logger("aliases") logger.exception("Error updating alias %s: %s", alias_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -251,7 +249,6 @@ def api_delete_alias(request: Request, alias_id: int): ) except Exception as e: - logger = get_logger("aliases") logger.exception("Error deleting alias %s: %s", alias_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( diff --git a/backend/routes/backup.py b/backend/routes/backup.py index 20df6b9..31d86e9 100644 --- a/backend/routes/backup.py +++ b/backend/routes/backup.py @@ -10,8 +10,12 @@ import ipaddress import time # Import local modules from backend.db.hosts import get_hosts -# Import Settings +# Import Settings & Logging from settings.settings import settings +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -54,7 +58,6 @@ async def api_dns_reload(request: Request): except Exception as err: took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 - logger = get_logger("hosts") logger.exception("Error executing backup: %s", str(e).strip()) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, diff --git a/backend/routes/dhcp.py b/backend/routes/dhcp.py index 14158bf..73fe0e8 100644 --- a/backend/routes/dhcp.py +++ b/backend/routes/dhcp.py @@ -12,10 +12,12 @@ from pathlib import Path import time # Import local modules from backend.db.hosts import get_hosts -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -80,7 +82,6 @@ async def api_dhcp_reload(request: Request): ) except Exception as err: - logger = get_logger("dhcp") logger.exception("Error reloading DHCP: %s", str(err).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -183,7 +184,6 @@ def api_dhcp_leases(request: Request): ) except Exception as err: - logger = get_logger("dhcp") logger.exception("Error reading DHCP leases: %s", str(err).strip()) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, diff --git a/backend/routes/dns.py b/backend/routes/dns.py index 6cd51f1..c260865 100644 --- a/backend/routes/dns.py +++ b/backend/routes/dns.py @@ -11,10 +11,12 @@ import time # Import local modules from backend.db.hosts import get_hosts from backend.db.aliases import get_aliases -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -74,7 +76,6 @@ async def api_dns_reload(request: Request): ) except Exception as err: - logger = get_logger("dns") logger.exception("Error reloading DNS: %s", str(err).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 diff --git a/backend/routes/health.py b/backend/routes/health.py index 574f7e6..bf9cc3f 100644 --- a/backend/routes/health.py +++ b/backend/routes/health.py @@ -5,10 +5,12 @@ from fastapi import APIRouter import sqlite3 import time import os -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -40,7 +42,7 @@ def health(): db_size = round(os.path.getsize(settings.DB_FILE) / (1024 * 1024), 2) except Exception as err: - get_logger("health").exception("Database health check failed: " + str(err).strip()) + logger.exception("Database health check failed: " + str(err).strip()) db_status = "error" db_version = None diff --git a/backend/routes/hosts.py b/backend/routes/hosts.py index 15c0368..bcb26d0 100644 --- a/backend/routes/hosts.py +++ b/backend/routes/hosts.py @@ -14,10 +14,12 @@ from backend.db.hosts import ( update_host, delete_host ) -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Logging -from log.log import setup_logging, get_logger +from log.log import get_logger + +# Logger initialization +logger = get_logger(__name__) # Create Router router = APIRouter() @@ -48,7 +50,6 @@ def api_get_hosts(request: Request): return hosts or [] except Exception as e: - logger = get_logger("hosts") logger.exception("Error getting list host %s", str(e).strip()) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -84,7 +85,6 @@ def api_get_host(request: Request, host_id: int): return host except Exception as e: - logger = get_logger("hosts") logger.exception("Error adding host %s: %s", host_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -141,7 +141,6 @@ def api_add_host(request: Request, data: dict): raise httpe except Exception as e: - logger = get_logger("hosts") logger.exception("Error adding host: %s", str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -196,7 +195,6 @@ def api_update_host(request: Request, data: dict, host_id: int): ) except Exception as e: - logger = get_logger("hosts") logger.exception("Error updating host %s: %s", host_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( @@ -251,7 +249,6 @@ def api_delete_host(request: Request, host_id: int): ) except Exception as e: - logger = get_logger("hosts") logger.exception("Error deleting host %s: %s", host_id, str(e).strip()) took_ms = (time.monotonic_ns() - start_ns) / 1_000_000 raise HTTPException( diff --git a/backend/security.py b/backend/security.py index 5974114..69b49ce 100644 --- a/backend/security.py +++ b/backend/security.py @@ -7,18 +7,20 @@ from fastapi import Request, HTTPException from itsdangerous import TimestampSigner # Import local modules from backend.db.users import get_user_by_username -# Import Settings +# Import Settings & Logging from settings.settings import settings -# Import Log from log.log import get_logger +# Logger initialization +logger = get_logger(__name__) + signer = TimestampSigner(settings.SECRET_KEY) # ----------------------------- # Verify Login # ----------------------------- def verify_login(username, password): - logger = get_logger(__name__) + user = get_user_by_username(username) if not user: logger.error("Login failed - user %s not found", username) @@ -39,7 +41,6 @@ def verify_login(username, password): # creates or renew the cookie # ---------------------------- def apply_session(response, username: str | None = None, token: str | None = None): - logger = get_logger(__name__) # First Login if username is not None and token is None: @@ -68,6 +69,7 @@ def apply_session(response, username: str | None = None, token: str | None = Non # check session cookie # ----------------------------- def is_logged_in(request: Request) -> bool: + token = request.cookies.get("session") if not token: return False @@ -81,7 +83,6 @@ def is_logged_in(request: Request) -> bool: # Close Session # ----------------------------- def close_session(response): - logger = get_logger(__name__) response.delete_cookie( key="session", diff --git a/log/log.py b/log/log.py index fa0b77a..899b179 100644 --- a/log/log.py +++ b/log/log.py @@ -151,11 +151,6 @@ def setup_logging(level: str = "INFO", to_file: bool = False, log_file: Optional config = build_log_config(level=level, to_file=to_file, log_file=log_file, log_access_file=log_access_file) logging.config.dictConfig(config) - logging.getLogger("main").info( - "Logging configured (level=%s, to_file=%s, log_file=%s, log_access_file=%s)", - level.upper(), to_file, log_file, log_access_file - ) - _INITIALIZED = True # --------------------------------------------------------- -- 2.47.3