Log Startdialog added

This commit is contained in:
2026-09-29 11:40:50 +02:00
parent 1a8f654de9
commit c3f1ef43db
13 changed files with 274 additions and 123 deletions
+85
View File
@@ -23,6 +23,8 @@ import ssl
import time
import urllib.error
import urllib.request
import importlib.metadata
import platform
from typing import Any
from concurrent.futures import ThreadPoolExecutor
from urllib.parse import quote
@@ -214,6 +216,79 @@ if not ldap_logger.handlers:
ldap_console_handler.setFormatter(logging.Formatter("LDAP | %(levelname)s | %(message)s"))
ldap_logger.addHandler(ldap_console_handler)
def _runtime_package_version(distribution_name: str) -> str:
try:
return importlib.metadata.version(distribution_name)
except importlib.metadata.PackageNotFoundError:
return "not installed"
except Exception:
return "unknown"
def _database_server_version() -> str:
try:
with engine.connect() as connection:
version = connection.exec_driver_sql("SELECT version()").scalar()
return str(version or "unknown").split(",", 1)[0]
except Exception as exc:
return f"unavailable ({exc.__class__.__name__})"
def _configured_callback_base_url() -> str:
try:
software = load_config().get("software", {}) or {}
configured = str(software.get("callback_base_url", "") or "").strip().rstrip("/")
if configured:
return configured
general = load_config().get("general", {}) or {}
return str(general.get("callback_base_url", "") or "").strip().rstrip("/")
except Exception:
return ""
def _startup_banner(role: str) -> None:
role_name = "Application" if role == "app" else "Callback"
callback_base = _configured_callback_base_url()
callback_bind = str(os.getenv("CALLBACK_BIND_IP", "127.0.0.1") or "127.0.0.1")
callback_port = str(os.getenv("CALLBACK_PORT", "8090") or "8090")
internal_callback = str(os.getenv("CALLBACK_INTERNAL_BASE_URL", "http://callback:8001") or "http://callback:8001").rstrip("/")
lines = [
"=" * 80,
f" AssetManager - {role_name} Startup",
"=" * 80,
f" AssetManager version : {application_version()}",
f" Container role : {role}",
f" Container version : {application_version()}",
f" Image reference : {os.getenv('ASSETMANAGER_IMAGE_REF', 'not provided')}",
f" Build version : {APP_VERSION}",
f" Build date : {os.getenv('ASSETMANAGER_BUILD_DATE', 'not provided')}",
f" Git commit : {os.getenv('ASSETMANAGER_GIT_COMMIT', 'not provided')}",
f" Python : {platform.python_version()}",
f" FastAPI : {_runtime_package_version('fastapi')}",
f" Uvicorn : {_runtime_package_version('uvicorn')}",
f" SQLAlchemy : {_runtime_package_version('sqlalchemy')}",
f" psycopg : {_runtime_package_version('psycopg')}",
f" Database : {_database_server_version()}",
f" Data directory : {DATA_ROOT}",
f" Startup time UTC : {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}",
]
if role == "callback":
lines.extend([
"-" * 80,
f" Callback base URL : {callback_base or '<automatic / not fixed>'}",
f" Callback API URL : {(callback_base + '/api/software-jobs/<job_id>/callback') if callback_base else '<resolved by application per job>'}",
f" Callback health URL : {(callback_base + '/api/software-callback/health') if callback_base else '<resolved from callback base URL>'}",
f" Internal callback : {internal_callback}",
f" Published listener : {callback_bind}:{callback_port} -> container:8001",
])
lines.extend([
"-" * 80,
f" AssetManager {role} ready",
"=" * 80,
])
print("\n".join(lines), flush=True)
app = FastAPI(title=os.getenv("APP_TITLE", "AssetManager"))
# Dedicated callback-only ASGI application. It deliberately exposes only the
# software callback endpoint and its health endpoint so deployments may publish
@@ -956,6 +1031,16 @@ async def job_definition_script_generator(request: Request):
return JSONResponse({"script": script, "interpreter": interpreter, "action": action})
@app.on_event("startup")
def log_application_startup():
_startup_banner("app")
@callback_app.on_event("startup")
def log_callback_startup():
_startup_banner("callback")
@app.on_event("startup")
def start_backup_scheduler():
global BACKUP_THREAD