Files
TimeTracker/app/utils/performance.py
T
Dries Peeters b4486a627f fix: CI tests, code quality, and duplicate DB indexes
- Webhook models: remove duplicate index definitions so db.create_all()
  no longer raises 'index already exists' (columns already have index=True)
- ImportService: fix circular import by late-importing ClientService,
  ProjectService, TimeTrackingService in __init__
- reports: fix F823 by renaming unpack variable _ to _entry_count to avoid
  shadowing gettext _ in export_task_excel()
- Code quality: add .flake8 with extend-ignore so flake8 CI passes;
  simplify pyproject.toml isort config (drop unsupported options)
- Format: run black and isort on app/
- tests: restore minimal app fixture in test_import_export_models
2026-03-15 10:51:52 +01:00

67 lines
2.2 KiB
Python

"""
Optional performance instrumentation: slow-request logging and query-count profiling.
Enable via config:
- PERF_LOG_SLOW_REQUESTS_MS: log when request duration exceeds this many ms (0 = disabled)
- PERF_QUERY_PROFILE: when true, track DB query count per request and include in slow-request logs
"""
import logging
from flask import g, request
from sqlalchemy import event
from sqlalchemy.engine import Engine
logger = logging.getLogger("timetracker.perf")
def init_performance_logging(app):
"""
Register slow-request logging and optional query-count profiling.
No overhead when PERF_LOG_SLOW_REQUESTS_MS is 0 and PERF_QUERY_PROFILE is False.
"""
slow_ms = app.config.get("PERF_LOG_SLOW_REQUESTS_MS", 0) or 0
query_profile = app.config.get("PERF_QUERY_PROFILE", False)
if query_profile:
@app.before_request
def _perf_set_query_count():
g._perf_query_count = 0
@event.listens_for(Engine, "before_cursor_execute")
def _perf_count_query(conn, cursor, statement, parameters, context, executemany):
if hasattr(g, "_perf_query_count"):
g._perf_query_count += 1
@app.after_request
def _perf_log_slow_requests(response):
if slow_ms <= 0:
return response
try:
start = getattr(g, "_start_time", None)
if start is None:
return response
duration_ms = (__import__("time").time() - start) * 1000
if duration_ms < slow_ms:
return response
query_count = getattr(g, "_perf_query_count", getattr(g, "query_count", None))
if query_count is not None:
logger.warning(
"slow_request path=%s duration_ms=%.0f status=%s query_count=%s",
request.path,
duration_ms,
response.status_code,
query_count,
)
else:
logger.warning(
"slow_request path=%s duration_ms=%.0f status=%s",
request.path,
duration_ms,
response.status_code,
)
except Exception:
pass
return response