refactor: migrate all remaining logging to structured_log, remove boilerplate helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Justin Visser 2026-02-26 21:59:34 +01:00
parent 19ca53186c
commit db0ac6a228
27 changed files with 361 additions and 913 deletions

View file

@ -20,6 +20,7 @@ from app.auth.deps import get_auth_service
from app.auth.service import AuthService
from app.db.models import User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.users import application as user_service
logger = logging.getLogger(__name__)
@ -48,14 +49,7 @@ async def list_users(
admin_user: User = Depends(get_api_admin_user),
):
users = await user_service.list_users(db_session)
logger.info(
"api.admin.users_listed",
extra={
"event": "api.admin.users_listed",
"admin_user_id": int(admin_user.id),
"user_count": len(users),
},
)
structured_log(logger, "info", "api.admin.users_listed", admin_user_id=int(admin_user.id), user_count=len(users))
return success_payload(
request,
data={
@ -92,15 +86,7 @@ async def create_user(
message=str(exc),
) from exc
logger.info(
"api.admin.user_created",
extra={
"event": "api.admin.user_created",
"admin_user_id": int(admin_user.id),
"target_user_id": int(created_user.id),
"target_is_admin": bool(created_user.is_admin),
},
)
structured_log(logger, "info", "api.admin.user_created", admin_user_id=int(admin_user.id), target_user_id=int(created_user.id), target_is_admin=bool(created_user.is_admin))
return success_payload(
request,
data=_serialize_user(created_user),
@ -140,15 +126,7 @@ async def set_user_active(
user=target_user,
is_active=bool(payload.is_active),
)
logger.info(
"api.admin.user_active_updated",
extra={
"event": "api.admin.user_active_updated",
"admin_user_id": int(admin_user.id),
"target_user_id": int(updated_user.id),
"is_active": bool(updated_user.is_active),
},
)
structured_log(logger, "info", "api.admin.user_active_updated", admin_user_id=int(admin_user.id), target_user_id=int(updated_user.id), is_active=bool(updated_user.is_active))
return success_payload(
request,
data=_serialize_user(updated_user),
@ -188,14 +166,7 @@ async def reset_user_password(
user=target_user,
password_hash=auth_service.hash_password(validated_password),
)
logger.info(
"api.admin.user_password_reset",
extra={
"event": "api.admin.user_password_reset",
"admin_user_id": int(admin_user.id),
"target_user_id": int(target_user.id),
},
)
structured_log(logger, "info", "api.admin.user_password_reset", admin_user_id=int(admin_user.id), target_user_id=int(target_user.id))
return success_payload(
request,
data={"message": f"Password reset: {target_user.email}"},

View file

@ -21,6 +21,7 @@ from app.api.schemas import (
)
from app.db.models import DataRepairJob, User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.dbops import (
collect_integrity_report,
list_repair_jobs,
@ -135,16 +136,7 @@ async def get_integrity_report(
admin_user: User = Depends(get_api_admin_user),
):
report = await collect_integrity_report(db_session)
logger.info(
"api.admin.db.integrity_checked",
extra={
"event": "api.admin.db.integrity_checked",
"admin_user_id": int(admin_user.id),
"status": report.get("status"),
"failure_count": len(report.get("failures", [])),
"warning_count": len(report.get("warnings", [])),
},
)
structured_log(logger, "info", "api.admin.db.integrity_checked", admin_user_id=int(admin_user.id), status=report.get("status"), failure_count=len(report.get("failures", [])), warning_count=len(report.get("warnings", [])))
return success_payload(request, data=report)
@ -159,15 +151,7 @@ async def get_repair_jobs(
admin_user: User = Depends(get_api_admin_user),
):
jobs = await list_repair_jobs(db_session, limit=limit)
logger.info(
"api.admin.db.repair_jobs_listed",
extra={
"event": "api.admin.db.repair_jobs_listed",
"admin_user_id": int(admin_user.id),
"limit": int(limit),
"job_count": len(jobs),
},
)
structured_log(logger, "info", "api.admin.db.repair_jobs_listed", admin_user_id=int(admin_user.id), limit=int(limit), job_count=len(jobs))
return success_payload(
request,
data={"jobs": [_serialize_repair_job(job) for job in jobs]},
@ -201,18 +185,15 @@ async def get_pdf_queue(
offset=resolved_offset,
status=normalized_status,
)
logger.info(
"api.admin.db.pdf_queue_listed",
extra={
"event": "api.admin.db.pdf_queue_listed",
"admin_user_id": int(admin_user.id),
"page": int(resolved_page),
"page_size": int(resolved_limit),
"offset": int(resolved_offset),
"status": normalized_status,
"item_count": len(queue_page.items),
"total_count": int(queue_page.total_count),
},
structured_log(
logger, "info", "api.admin.db.pdf_queue_listed",
admin_user_id=int(admin_user.id),
page=int(resolved_page),
page_size=int(resolved_limit),
offset=int(resolved_offset),
status=normalized_status,
item_count=len(queue_page.items),
total_count=int(queue_page.total_count),
)
return success_payload(
request,
@ -251,15 +232,7 @@ async def requeue_pdf_lookup(
message="Publication not found.",
)
status, message = _requeue_response_state(queued=result.queued)
logger.info(
"api.admin.db.pdf_queue_requeue_requested",
extra={
"event": "api.admin.db.pdf_queue_requeue_requested",
"admin_user_id": int(admin_user.id),
"publication_id": int(publication_id),
"queued": bool(result.queued),
},
)
structured_log(logger, "info", "api.admin.db.pdf_queue_requeue_requested", admin_user_id=int(admin_user.id), publication_id=int(publication_id), queued=bool(result.queued))
return success_payload(
request,
data={
@ -287,16 +260,7 @@ async def requeue_all_missing_pdfs(
request_email=admin_user.email,
limit=limit,
)
logger.info(
"api.admin.db.pdf_queue_requeue_all",
extra={
"event": "api.admin.db.pdf_queue_requeue_all",
"admin_user_id": int(admin_user.id),
"limit": int(limit),
"requested_count": int(result.requested_count),
"queued_count": int(result.queued_count),
},
)
structured_log(logger, "info", "api.admin.db.pdf_queue_requeue_all", admin_user_id=int(admin_user.id), limit=int(limit), requested_count=int(result.requested_count), queued_count=int(result.queued_count))
return success_payload(
request,
data={
@ -336,18 +300,15 @@ async def trigger_publication_link_repair(
code="invalid_repair_scope",
message=str(exc),
) from exc
logger.info(
"api.admin.db.publication_link_repair_triggered",
extra={
"event": "api.admin.db.publication_link_repair_triggered",
"admin_user_id": int(admin_user.id),
"scope_mode": payload.scope_mode,
"target_user_id": int(payload.user_id) if payload.user_id is not None else None,
"dry_run": bool(payload.dry_run),
"gc_orphan_publications": bool(payload.gc_orphan_publications),
"job_id": int(result["job_id"]),
"status": result["status"],
},
structured_log(
logger, "info", "api.admin.db.publication_link_repair_triggered",
admin_user_id=int(admin_user.id),
scope_mode=payload.scope_mode,
target_user_id=int(payload.user_id) if payload.user_id is not None else None,
dry_run=bool(payload.dry_run),
gc_orphan_publications=bool(payload.gc_orphan_publications),
job_id=int(result["job_id"]),
status=result["status"],
)
return success_payload(request, data=result)
@ -379,17 +340,7 @@ async def trigger_publication_near_duplicate_repair(
code="invalid_near_duplicate_repair_request",
message=str(exc),
) from exc
logger.info(
"api.admin.db.publication_near_duplicate_repair_triggered",
extra={
"event": "api.admin.db.publication_near_duplicate_repair_triggered",
"admin_user_id": int(admin_user.id),
"dry_run": bool(payload.dry_run),
"selected_cluster_count": len(payload.selected_cluster_keys),
"job_id": int(result["job_id"]),
"status": result["status"],
},
)
structured_log(logger, "info", "api.admin.db.publication_near_duplicate_repair_triggered", admin_user_id=int(admin_user.id), dry_run=bool(payload.dry_run), selected_cluster_count=len(payload.selected_cluster_keys), job_id=int(result["job_id"]), status=result["status"])
return success_payload(request, data=result)
@ -435,15 +386,7 @@ async def drop_all_publications(
)
await db_session.commit()
logger.warning(
"api.admin.db.all_publications_dropped",
extra={
"event": "api.admin.db.all_publications_dropped",
"admin_user_id": int(admin_user.id),
"admin_email": admin_user.email,
"deleted_count": int(total_publications),
},
)
structured_log(logger, "warning", "api.admin.db.all_publications_dropped", admin_user_id=int(admin_user.id), admin_email=admin_user.email, deleted_count=int(total_publications))
return success_payload(
request,
data={

View file

@ -17,6 +17,7 @@ from app.api.schemas import (
MessageEnvelope,
)
from app.auth.deps import get_auth_service, get_login_rate_limiter
from app.logging_utils import structured_log
from app.auth.rate_limit import SlidingWindowRateLimiter
from app.auth import runtime as auth_runtime
from app.auth.service import AuthService
@ -36,14 +37,7 @@ def _login_limiter_key_and_email(request: Request, payload: LoginRequest) -> tup
def _raise_rate_limited(normalized_email: str, retry_after_seconds: int) -> None:
logger.warning(
"api.auth.login_rate_limited",
extra={
"event": "api.auth.login_rate_limited",
"email": normalized_email,
"retry_after_seconds": retry_after_seconds,
},
)
structured_log(logger, "warning", "api.auth.login_rate_limited", email=normalized_email, retry_after_seconds=retry_after_seconds)
raise ApiException(
status_code=429,
code="rate_limited",
@ -62,10 +56,7 @@ def _serialize_user_payload(user: User) -> dict[str, object]:
def _raise_invalid_credentials(*, normalized_email: str) -> None:
logger.info(
"api.auth.login_failed",
extra={"event": "api.auth.login_failed", "email": normalized_email},
)
structured_log(logger, "info", "api.auth.login_failed", email=normalized_email)
raise ApiException(
status_code=401,
code="invalid_credentials",
@ -105,14 +96,7 @@ async def login(
email=user.email,
is_admin=user.is_admin,
)
logger.info(
"api.auth.login_succeeded",
extra={
"event": "api.auth.login_succeeded",
"user_id": user.id,
"is_admin": user.is_admin,
},
)
structured_log(logger, "info", "api.auth.login_succeeded", user_id=user.id, is_admin=user.is_admin)
return success_payload(
request,
data={
@ -199,13 +183,7 @@ async def change_password(
user=current_user,
password_hash=auth_service.hash_password(validated_password),
)
logger.info(
"api.auth.password_changed",
extra={
"event": "api.auth.password_changed",
"user_id": int(current_user.id),
},
)
structured_log(logger, "info", "api.auth.password_changed", user_id=int(current_user.id))
return success_payload(
request,
data={"message": "Password updated successfully."},
@ -222,13 +200,7 @@ async def logout(
):
current_user = await auth_runtime.get_authenticated_user(request, db_session)
auth_runtime.invalidate_session(request)
logger.info(
"api.auth.logout",
extra={
"event": "api.auth.logout",
"user_id": int(current_user.id) if current_user is not None else None,
},
)
structured_log(logger, "info", "api.auth.logout", user_id=int(current_user.id) if current_user is not None else None)
return success_payload(
request,
data={

View file

@ -22,6 +22,7 @@ from app.api.schemas import (
)
from app.db.models import User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.publication_identifiers import application as identifier_service
from app.services.domains.publications import application as publication_service
from app.services.domains.scholars import application as scholar_service
@ -340,30 +341,6 @@ async def _favorite_publication_state(
return identifiers[0] if identifiers else current
def _log_retry_pdf_result(
*,
current_user: User,
scholar_profile_id: int,
publication_id: int,
queued: bool,
resolved_pdf: bool,
pdf_status: str,
doi: str | None,
) -> None:
logger.info(
"api.publications.retry_pdf",
extra={
"event": "api.publications.retry_pdf",
"user_id": current_user.id,
"scholar_profile_id": scholar_profile_id,
"publication_id": publication_id,
"queued": queued,
"resolved_pdf": resolved_pdf,
"pdf_status": pdf_status,
},
)
@router.get(
"",
response_model=PublicationsListEnvelope,
@ -443,14 +420,7 @@ async def mark_all_publications_read(
db_session,
user_id=current_user.id,
)
logger.info(
"api.publications.mark_all_read",
extra={
"event": "api.publications.mark_all_read",
"user_id": current_user.id,
"updated_count": updated_count,
},
)
structured_log(logger, "info", "api.publications.mark_all_read", user_id=current_user.id, updated_count=updated_count)
return success_payload(
request,
data={
@ -481,15 +451,7 @@ async def mark_selected_publications_read(
user_id=current_user.id,
selections=selection_pairs,
)
logger.info(
"api.publications.mark_selected_read",
extra={
"event": "api.publications.mark_selected_read",
"user_id": current_user.id,
"requested_count": len(selection_pairs),
"updated_count": updated_count,
},
)
structured_log(logger, "info", "api.publications.mark_selected_read", user_id=current_user.id, requested_count=len(selection_pairs), updated_count=updated_count)
return success_payload(
request,
data={
@ -523,14 +485,14 @@ async def retry_publication_pdf(
resolved_pdf=resolved_pdf,
pdf_status=current.pdf_status,
)
_log_retry_pdf_result(
current_user=current_user,
structured_log(
logger, "info", "api.publications.retry_pdf",
user_id=current_user.id,
scholar_profile_id=payload.scholar_profile_id,
publication_id=publication_id,
queued=queued,
resolved_pdf=resolved_pdf,
pdf_status=current.pdf_status,
doi=None,
)
return success_payload(
request,
@ -561,16 +523,7 @@ async def toggle_publication_favorite(
publication_id=publication_id,
is_favorite=payload.is_favorite,
)
logger.info(
"api.publications.favorite",
extra={
"event": "api.publications.favorite",
"user_id": current_user.id,
"scholar_profile_id": payload.scholar_profile_id,
"publication_id": publication_id,
"is_favorite": bool(payload.is_favorite),
},
)
structured_log(logger, "info", "api.publications.favorite", user_id=current_user.id, scholar_profile_id=payload.scholar_profile_id, publication_id=publication_id, is_favorite=bool(payload.is_favorite))
return success_payload(
request,
data={

View file

@ -22,6 +22,7 @@ from app.api.schemas import (
)
from app.db.models import RunStatus, RunTriggerType, User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.ingestion import application as ingestion_service
from app.services.domains.ingestion import safety as run_safety_service
from app.services.domains.runs import application as run_service
@ -287,31 +288,21 @@ async def _load_safety_state(
if run_safety_service.clear_expired_cooldown(user_settings):
await db_session.commit()
await db_session.refresh(user_settings)
logger.info(
"api.runs.safety_cooldown_cleared",
extra={
"event": "api.runs.safety_cooldown_cleared",
"user_id": user_id,
"reason": previous_safety_state.get("cooldown_reason"),
"cooldown_until": previous_safety_state.get("cooldown_until"),
"metric_name": "api_runs_safety_cooldown_cleared_total",
"metric_value": 1,
},
)
structured_log(
logger, "info", "api.runs.safety_cooldown_cleared",
user_id=user_id,
reason=previous_safety_state.get("cooldown_reason"),
cooldown_until=previous_safety_state.get("cooldown_until"),
)
return run_safety_service.get_safety_state_payload(user_settings)
def _raise_manual_runs_disabled(*, user_id: int, safety_state: dict[str, Any]) -> None:
logger.warning(
"api.runs.manual_blocked_policy",
extra={
"event": "api.runs.manual_blocked_policy",
"user_id": user_id,
"policy": {"manual_run_allowed": False},
"safety_state": safety_state,
"metric_name": "api_runs_manual_blocked_policy_total",
"metric_value": 1,
},
structured_log(
logger, "warning", "api.runs.manual_blocked_policy",
user_id=user_id,
policy={"manual_run_allowed": False},
safety_state=safety_state,
)
raise ApiException(
status_code=403,
@ -399,7 +390,7 @@ async def _recover_integrity_error(
if idempotency_key is None:
logger.exception(
"api.runs.manual_integrity_error",
extra={"event": "api.runs.manual_integrity_error", "user_id": user_id},
extra={"user_id": user_id},
)
raise ApiException(status_code=500, code="manual_run_failed", message="Manual run failed.") from original_exc
existing_run = await run_service.get_manual_run_by_idempotency_key(
@ -410,7 +401,7 @@ async def _recover_integrity_error(
if existing_run is None:
logger.exception(
"api.runs.manual_integrity_error",
extra={"event": "api.runs.manual_integrity_error", "user_id": user_id},
extra={"user_id": user_id},
)
raise ApiException(status_code=500, code="manual_run_failed", message="Manual run failed.") from original_exc
if existing_run.status in (RunStatus.RUNNING, RunStatus.RESOLVING):
@ -471,17 +462,12 @@ async def _execute_manual_run(
def _raise_manual_blocked_safety(*, exc, user_id: int) -> None:
logger.info(
"api.runs.manual_blocked_safety",
extra={
"event": "api.runs.manual_blocked_safety",
"user_id": user_id,
"reason": exc.safety_state.get("cooldown_reason"),
"cooldown_until": exc.safety_state.get("cooldown_until"),
"cooldown_remaining_seconds": exc.safety_state.get("cooldown_remaining_seconds"),
"metric_name": "api_runs_manual_blocked_safety_total",
"metric_value": 1,
},
structured_log(
logger, "info", "api.runs.manual_blocked_safety",
user_id=user_id,
reason=exc.safety_state.get("cooldown_reason"),
cooldown_until=exc.safety_state.get("cooldown_until"),
cooldown_remaining_seconds=exc.safety_state.get("cooldown_remaining_seconds"),
)
raise ApiException(
status_code=429,
@ -494,7 +480,7 @@ def _raise_manual_blocked_safety(*, exc, user_id: int) -> None:
def _raise_manual_failed(*, exc: Exception, user_id: int) -> None:
logger.exception(
"api.runs.manual_failed",
extra={"event": "api.runs.manual_failed", "user_id": user_id},
extra={"user_id": user_id},
)
raise ApiException(status_code=500, code="manual_run_failed", message="Manual run failed.") from exc

View file

@ -23,6 +23,7 @@ from app.api.schemas import (
)
from app.db.models import User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.ingestion import queue as ingestion_queue_service
from app.services.domains.portability import application as import_export_service
from app.services.domains.scholar import rate_limit as scholar_rate_limit
@ -80,24 +81,18 @@ async def _enqueue_initial_scrape_job_for_scholar(
await db_session.commit()
except Exception:
await db_session.rollback()
logger.warning(
"api.scholars.initial_scrape_enqueue_failed",
extra={
"event": "api.scholars.initial_scrape_enqueue_failed",
"user_id": user_id,
"scholar_profile_id": profile.id,
},
structured_log(
logger, "warning", "api.scholars.initial_scrape_enqueue_failed",
user_id=user_id,
scholar_profile_id=profile.id,
)
return False
logger.info(
"api.scholars.initial_scrape_enqueued",
extra={
"event": "api.scholars.initial_scrape_enqueued",
"user_id": user_id,
"scholar_profile_id": profile.id,
"reason": INITIAL_SCHOLAR_SCRAPE_QUEUE_REASON,
},
structured_log(
logger, "info", "api.scholars.initial_scrape_enqueued",
user_id=user_id,
scholar_profile_id=profile.id,
reason=INITIAL_SCHOLAR_SCRAPE_QUEUE_REASON,
)
return True
@ -143,15 +138,12 @@ async def _hydrate_scholar_metadata_if_needed(
should_skip, remaining_seconds = _is_create_hydration_rate_limited()
if should_skip:
logger.info(
"api.scholars.create_metadata_hydration_skipped",
extra={
"event": "api.scholars.create_metadata_hydration_skipped",
"reason": "scholar_request_throttle_active",
"user_id": user_id,
"scholar_profile_id": profile.id,
"retry_after_seconds": round(remaining_seconds, 3),
},
structured_log(
logger, "info", "api.scholars.create_metadata_hydration_skipped",
reason="scholar_request_throttle_active",
user_id=user_id,
scholar_profile_id=profile.id,
retry_after_seconds=round(remaining_seconds, 3),
)
return profile
@ -165,23 +157,17 @@ async def _hydrate_scholar_metadata_if_needed(
timeout=CREATE_METADATA_HYDRATION_TIMEOUT_SECONDS,
)
except TimeoutError:
logger.info(
"api.scholars.create_metadata_hydration_skipped",
extra={
"event": "api.scholars.create_metadata_hydration_skipped",
"reason": "create_timeout",
"user_id": user_id,
"scholar_profile_id": profile.id,
},
structured_log(
logger, "info", "api.scholars.create_metadata_hydration_skipped",
reason="create_timeout",
user_id=user_id,
scholar_profile_id=profile.id,
)
except Exception:
logger.warning(
"api.scholars.create_metadata_hydration_failed",
extra={
"event": "api.scholars.create_metadata_hydration_failed",
"user_id": user_id,
"scholar_profile_id": profile.id,
},
structured_log(
logger, "warning", "api.scholars.create_metadata_hydration_failed",
user_id=user_id,
scholar_profile_id=profile.id,
)
return profile
@ -330,14 +316,7 @@ async def import_scholars_and_publications(
code="invalid_import_payload",
message=str(exc),
) from exc
logger.info(
"api.scholars.imported",
extra={
"event": "api.scholars.imported",
"user_id": current_user.id,
**result,
},
)
structured_log(logger, "info", "api.scholars.imported", user_id=current_user.id, **result)
return success_payload(request, data=result)
@ -367,14 +346,7 @@ async def create_scholar(
code="invalid_scholar",
message=str(exc),
) from exc
logger.info(
"api.scholars.created",
extra={
"event": "api.scholars.created",
"user_id": current_user.id,
"scholar_profile_id": created.id,
},
)
structured_log(logger, "info", "api.scholars.created", user_id=current_user.id, scholar_profile_id=created.id)
did_queue_initial_scrape = await _enqueue_initial_scrape_job_for_scholar(
db_session,
profile=created,
@ -421,16 +393,7 @@ async def search_scholars(
message=str(exc),
) from exc
logger.info(
"api.scholars.search.completed",
extra={
"event": "api.scholars.search.completed",
"user_id": current_user.id,
"query": query.strip(),
"candidate_count": len(parsed.candidates),
"state": parsed.state.value,
},
)
structured_log(logger, "info", "api.scholars.search.completed", user_id=current_user.id, query=query.strip(), candidate_count=len(parsed.candidates), state=parsed.state.value)
return success_payload(
request,
data=_search_response_data(query, parsed),
@ -459,15 +422,7 @@ async def toggle_scholar(
message="Scholar not found.",
)
updated = await scholar_service.toggle_scholar_enabled(db_session, profile=profile)
logger.info(
"api.scholars.toggled",
extra={
"event": "api.scholars.toggled",
"user_id": current_user.id,
"scholar_profile_id": updated.id,
"is_enabled": updated.is_enabled,
},
)
structured_log(logger, "info", "api.scholars.toggled", user_id=current_user.id, scholar_profile_id=updated.id, is_enabled=updated.is_enabled)
return success_payload(
request,
data=_serialize_scholar(updated),
@ -500,14 +455,7 @@ async def delete_scholar(
profile=profile,
upload_dir=settings.scholar_image_upload_dir,
)
logger.info(
"api.scholars.deleted",
extra={
"event": "api.scholars.deleted",
"user_id": current_user.id,
"scholar_profile_id": scholar_profile_id,
},
)
structured_log(logger, "info", "api.scholars.deleted", user_id=current_user.id, scholar_profile_id=scholar_profile_id)
return success_payload(
request,
data={"message": "Scholar deleted."},
@ -551,14 +499,7 @@ async def update_scholar_image_url(
message=str(exc),
) from exc
logger.info(
"api.scholars.image_url_updated",
extra={
"event": "api.scholars.image_url_updated",
"user_id": current_user.id,
"scholar_profile_id": updated.id,
},
)
structured_log(logger, "info", "api.scholars.image_url_updated", user_id=current_user.id, scholar_profile_id=updated.id)
return success_payload(
request,
data=_serialize_scholar(updated),
@ -600,16 +541,7 @@ async def upload_scholar_image(
) from exc
image_size = len(image_bytes)
logger.info(
"api.scholars.image_uploaded",
extra={
"event": "api.scholars.image_uploaded",
"user_id": current_user.id,
"scholar_profile_id": updated.id,
"content_type": image.content_type,
"size_bytes": image_size,
},
)
structured_log(logger, "info", "api.scholars.image_uploaded", user_id=current_user.id, scholar_profile_id=updated.id, content_type=image.content_type, size_bytes=image_size)
return success_payload(
request,
data=_serialize_scholar(updated),
@ -643,14 +575,7 @@ async def clear_scholar_image_customization(
profile=profile,
upload_dir=settings.scholar_image_upload_dir,
)
logger.info(
"api.scholars.image_customization_cleared",
extra={
"event": "api.scholars.image_customization_cleared",
"user_id": current_user.id,
"scholar_profile_id": updated.id,
},
)
structured_log(logger, "info", "api.scholars.image_customization_cleared", user_id=current_user.id, scholar_profile_id=updated.id)
return success_payload(
request,
data=_serialize_scholar(updated),

View file

@ -11,6 +11,7 @@ from app.api.responses import success_payload
from app.api.schemas import SettingsEnvelope, SettingsUpdateRequest
from app.db.models import User
from app.db.session import get_db_session
from app.logging_utils import structured_log
from app.services.domains.ingestion import safety as run_safety_service
from app.services.domains.settings import application as user_settings_service
from app.settings import settings as settings_module
@ -83,16 +84,11 @@ async def _clear_expired_cooldown_with_log(
return
await db_session.commit()
await db_session.refresh(user_settings)
logger.info(
"api.settings.safety_cooldown_cleared",
extra={
"event": "api.settings.safety_cooldown_cleared",
"user_id": user_id,
"reason": previous_safety_state.get("cooldown_reason"),
"cooldown_until": previous_safety_state.get("cooldown_until"),
"metric_name": "api_settings_safety_cooldown_cleared_total",
"metric_value": 1,
},
structured_log(
logger, "info", "api.settings.safety_cooldown_cleared",
user_id=user_id,
reason=previous_safety_state.get("cooldown_reason"),
cooldown_until=previous_safety_state.get("cooldown_until"),
)
@ -100,21 +96,6 @@ def _effective_auto_run_enabled(payload: SettingsUpdateRequest) -> bool:
return bool(payload.auto_run_enabled) and settings_module.ingestion_automation_allowed
def _log_settings_update(*, user_id: int, updated) -> None:
logger.info(
"api.settings.updated",
extra={
"event": "api.settings.updated",
"user_id": user_id,
"auto_run_enabled": updated.auto_run_enabled,
"run_interval_minutes": updated.run_interval_minutes,
"request_delay_seconds": updated.request_delay_seconds,
"nav_visible_pages": updated.nav_visible_pages,
"openalex_api_key": "SET" if updated.openalex_api_key else "UNSET",
},
)
@router.get(
"",
response_model=SettingsEnvelope,
@ -182,7 +163,15 @@ async def update_settings(
user_id=current_user.id,
user_settings=updated,
)
_log_settings_update(user_id=current_user.id, updated=updated)
structured_log(
logger, "info", "api.settings.updated",
user_id=current_user.id,
auto_run_enabled=updated.auto_run_enabled,
run_interval_minutes=updated.run_interval_minutes,
request_delay_seconds=updated.request_delay_seconds,
nav_visible_pages=updated.nav_visible_pages,
openalex_api_key="SET" if updated.openalex_api_key else "UNSET",
)
return success_payload(
request,
data=_serialize_settings(updated),