Give wishlist modules their own loggers

- add module-level loggers for the wishlist package instead of threading the web server logger through runtime objects
- default wishlist helper runtimes and cleanup helpers to their package logger while still allowing test overrides
- keep web_server.py as a thin caller that no longer injects its logger into wishlist flows
This commit is contained in:
Antti Kettunen 2026-04-28 17:21:57 +03:00
parent d2af9f8bdf
commit f5226bd5b5
No known key found for this signature in database
GPG key ID: C6B2A3D250359BD7
6 changed files with 23 additions and 23 deletions

View file

@ -8,7 +8,7 @@ from typing import Any, Dict, Optional
from utils.logging_config import get_logger from utils.logging_config import get_logger
logger = get_logger("wishlist_service") logger = get_logger("wishlist.payloads")
def sanitize_track_data_for_processing(track_data): def sanitize_track_data_for_processing(track_data):

View file

@ -12,6 +12,11 @@ from typing import Any, Callable, Dict
from core.wishlist.payloads import build_failed_track_wishlist_context from core.wishlist.payloads import build_failed_track_wishlist_context
from core.wishlist.selection import filter_wishlist_tracks_by_category, sanitize_and_dedupe_wishlist_tracks from core.wishlist.selection import filter_wishlist_tracks_by_category, sanitize_and_dedupe_wishlist_tracks
from core.wishlist.state import get_wishlist_cycle, set_wishlist_cycle from core.wishlist.state import get_wishlist_cycle, set_wishlist_cycle
from utils.logging_config import get_logger
module_logger = get_logger("wishlist.processing")
logger = module_logger
@dataclass @dataclass
@ -32,9 +37,9 @@ class WishlistAutoProcessingRuntime:
run_full_missing_tracks_process: Callable[[str, str, list[dict[str, Any]]], Any] run_full_missing_tracks_process: Callable[[str, str, list[dict[str, Any]]], Any]
get_batch_max_concurrent: Callable[[], int] get_batch_max_concurrent: Callable[[], int]
get_active_server: Callable[[], str] get_active_server: Callable[[], str]
logger: Any
current_time_fn: Callable[[], float] current_time_fn: Callable[[], float]
profile_id: int = 1 profile_id: int = 1
logger: Any = module_logger
def remove_completed_tracks_from_wishlist( def remove_completed_tracks_from_wishlist(
@ -42,7 +47,7 @@ def remove_completed_tracks_from_wishlist(
download_tasks: Dict[str, Dict[str, Any]], download_tasks: Dict[str, Dict[str, Any]],
remove_from_wishlist: Callable[[Dict[str, Any]], Any], remove_from_wishlist: Callable[[Dict[str, Any]], Any],
*, *,
logger, logger=logger,
) -> int: ) -> int:
"""Remove completed batch tasks from the wishlist.""" """Remove completed batch tasks from the wishlist."""
removed_count = 0 removed_count = 0
@ -65,7 +70,7 @@ def add_cancelled_tracks_to_failed_tracks(
download_tasks: Dict[str, Dict[str, Any]], download_tasks: Dict[str, Dict[str, Any]],
permanently_failed_tracks: list[Dict[str, Any]], permanently_failed_tracks: list[Dict[str, Any]],
*, *,
logger, logger=logger,
max_process: int = 100, max_process: int = 100,
) -> int: ) -> int:
"""Promote cancelled-but-missing tasks into the failed-track list.""" """Promote cancelled-but-missing tasks into the failed-track list."""
@ -111,7 +116,7 @@ def recover_uncaptured_failed_tracks(
download_tasks: Dict[str, Dict[str, Any]], download_tasks: Dict[str, Dict[str, Any]],
permanently_failed_tracks: list[Dict[str, Any]], permanently_failed_tracks: list[Dict[str, Any]],
*, *,
logger, logger=logger,
) -> int: ) -> int:
"""Recover tasks force-marked failed/not_found so wishlist processing does not skip them.""" """Recover tasks force-marked failed/not_found so wishlist processing does not skip them."""
recovered_count = 0 recovered_count = 0
@ -164,7 +169,7 @@ def finalize_auto_wishlist_completion(
add_activity_item: Callable[[Any, Any, Any, Any], Any], add_activity_item: Callable[[Any, Any, Any, Any], Any],
automation_engine, automation_engine,
db_factory: Callable[[], Any], db_factory: Callable[[], Any],
logger, logger=logger,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Finalize auto wishlist processing after a batch finishes.""" """Finalize auto wishlist processing after a batch finishes."""
tracks_added = completion_summary.get('tracks_added', 0) tracks_added = completion_summary.get('tracks_added', 0)
@ -222,7 +227,7 @@ def remove_tracks_already_in_library(
music_database, music_database,
active_server: str, active_server: str,
*, *,
logger, logger=logger,
skip_track_fn: Callable[[dict[str, Any]], bool] | None = None, skip_track_fn: Callable[[dict[str, Any]], bool] | None = None,
log_prefix: str = "[Auto-Wishlist]", log_prefix: str = "[Auto-Wishlist]",
) -> int: ) -> int:
@ -296,8 +301,8 @@ class WishlistManualDownloadRuntime:
get_batch_max_concurrent: Callable[[], int] get_batch_max_concurrent: Callable[[], int]
add_activity_item: Callable[[Any, Any, Any, Any], Any] add_activity_item: Callable[[Any, Any, Any, Any], Any]
active_server: str active_server: str
logger: Any
profile_id: int profile_id: int
logger: Any = module_logger
def start_manual_wishlist_download_batch( def start_manual_wishlist_download_batch(
@ -411,7 +416,7 @@ def cleanup_wishlist_against_library(
profile_id: int, profile_id: int,
active_server: str, active_server: str,
*, *,
logger, logger=logger,
) -> tuple[Dict[str, Any], int]: ) -> tuple[Dict[str, Any], int]:
"""Remove wishlist tracks that already exist in the library for one profile.""" """Remove wishlist tracks that already exist in the library for one profile."""
try: try:
@ -617,7 +622,7 @@ def automatic_wishlist_cleanup_after_db_update(
profiles_database=None, profiles_database=None,
music_database=None, music_database=None,
active_server: str | None = None, active_server: str | None = None,
logger, logger=logger,
) -> int: ) -> int:
"""Remove wishlist entries that already exist in the library after a DB update.""" """Remove wishlist entries that already exist in the library after a DB update."""
try: try:

View file

@ -16,7 +16,7 @@ from database.music_database import get_database
from utils.logging_config import get_logger from utils.logging_config import get_logger
logger = get_logger("imports.side_effects") logger = get_logger("wishlist.resolution")
def _primary_track_artist_name(track_info: Dict[str, Any]) -> str: def _primary_track_artist_name(track_info: Dict[str, Any]) -> str:

View file

@ -12,6 +12,11 @@ from core.wishlist.reporting import build_wishlist_stats_payload
from core.wishlist.selection import prepare_wishlist_tracks_for_display from core.wishlist.selection import prepare_wishlist_tracks_for_display
from core.wishlist.state import get_wishlist_cycle as _get_wishlist_cycle from core.wishlist.state import get_wishlist_cycle as _get_wishlist_cycle
from core.wishlist.state import set_wishlist_cycle as _set_wishlist_cycle from core.wishlist.state import set_wishlist_cycle as _set_wishlist_cycle
from utils.logging_config import get_logger
module_logger = get_logger("wishlist.routes")
logger = module_logger
@dataclass @dataclass
@ -28,8 +33,8 @@ class WishlistRouteRuntime:
is_wishlist_actually_processing: Callable[[], bool] is_wishlist_actually_processing: Callable[[], bool]
reset_wishlist_processing_state: Callable[[], None] reset_wishlist_processing_state: Callable[[], None]
add_activity_item: Callable[[Any, Any, Any, Any], Any] add_activity_item: Callable[[Any, Any, Any, Any], Any]
logger: Any
active_server: str active_server: str
logger: Any = module_logger
get_next_run_seconds: Callable[[str], int] | None = None get_next_run_seconds: Callable[[str], int] | None = None
thread_factory: Callable[..., Any] = threading.Thread thread_factory: Callable[..., Any] = threading.Thread

View file

@ -11,7 +11,7 @@ from database.music_database import get_database
from utils.logging_config import get_logger from utils.logging_config import get_logger
logger = get_logger("wishlist_service") logger = get_logger("wishlist.service")
class WishlistService: class WishlistService:

View file

@ -17442,7 +17442,6 @@ def check_and_recover_stuck_flags():
timeout_seconds=stuck_timeout, timeout_seconds=stuck_timeout,
now=current_time, now=current_time,
label="Wishlist auto-processing", label="Wishlist auto-processing",
logger=logger,
reset_callback=_reset_wishlist_processing_state, reset_callback=_reset_wishlist_processing_state,
): ):
return True return True
@ -17476,7 +17475,6 @@ def is_wishlist_actually_processing():
timeout_seconds=900, timeout_seconds=900,
now=current_time, now=current_time,
on_stuck=check_and_recover_stuck_flags, on_stuck=check_and_recover_stuck_flags,
logger=logger,
) )
def is_watchlist_actually_scanning(): def is_watchlist_actually_scanning():
@ -17542,7 +17540,6 @@ def _process_wishlist_automatically(automation_id=None):
run_full_missing_tracks_process=_run_full_missing_tracks_process, run_full_missing_tracks_process=_run_full_missing_tracks_process,
get_batch_max_concurrent=_get_batch_max_concurrent, get_batch_max_concurrent=_get_batch_max_concurrent,
get_active_server=config_manager.get_active_media_server, get_active_server=config_manager.get_active_media_server,
logger=logger,
current_time_fn=time.time, current_time_fn=time.time,
profile_id=1, profile_id=1,
) )
@ -18366,7 +18363,6 @@ def _build_wishlist_route_runtime(
is_wishlist_actually_processing=is_actually_processing_fn or is_wishlist_actually_processing, is_wishlist_actually_processing=is_actually_processing_fn or is_wishlist_actually_processing,
reset_wishlist_processing_state=reset_wishlist_processing_state or (lambda: None), reset_wishlist_processing_state=reset_wishlist_processing_state or (lambda: None),
add_activity_item=add_activity_item, add_activity_item=add_activity_item,
logger=logger,
active_server=config_manager.get_active_media_server(), active_server=config_manager.get_active_media_server(),
get_next_run_seconds=get_next_run_seconds, get_next_run_seconds=get_next_run_seconds,
) )
@ -18405,7 +18401,6 @@ def start_wishlist_missing_downloads():
get_batch_max_concurrent=_get_batch_max_concurrent, get_batch_max_concurrent=_get_batch_max_concurrent,
add_activity_item=add_activity_item, add_activity_item=add_activity_item,
active_server=config_manager.get_active_media_server(), active_server=config_manager.get_active_media_server(),
logger=logger,
profile_id=manual_profile_id, profile_id=manual_profile_id,
) )
@ -18456,7 +18451,6 @@ def cleanup_wishlist():
db, db,
get_current_profile_id(), get_current_profile_id(),
active_server, active_server,
logger=logger,
) )
return jsonify(payload), status_code return jsonify(payload), status_code
@ -20353,7 +20347,6 @@ def _process_failed_tracks_to_wishlist_exact(batch_id):
batch, batch,
download_tasks, download_tasks,
_check_and_remove_from_wishlist, _check_and_remove_from_wishlist,
logger=logger,
) )
# STEP 1: Add cancelled tracks that were missing to permanently_failed_tracks (replicating sync.py) # STEP 1: Add cancelled tracks that were missing to permanently_failed_tracks (replicating sync.py)
@ -20364,7 +20357,6 @@ def _process_failed_tracks_to_wishlist_exact(batch_id):
batch, batch,
download_tasks, download_tasks,
permanently_failed_tracks, permanently_failed_tracks,
logger=logger,
) )
logger.warning(f"[Wishlist Processing] Processed {processed_count} cancelled tracks") logger.warning(f"[Wishlist Processing] Processed {processed_count} cancelled tracks")
@ -20376,7 +20368,6 @@ def _process_failed_tracks_to_wishlist_exact(batch_id):
batch, batch,
download_tasks, download_tasks,
permanently_failed_tracks, permanently_failed_tracks,
logger=logger,
) )
if recovered_count: if recovered_count:
logger.warning(f"[Wishlist Processing] Recovered {recovered_count} uncaptured failed tracks for wishlist") logger.warning(f"[Wishlist Processing] Recovered {recovered_count} uncaptured failed tracks for wishlist")
@ -20535,7 +20526,6 @@ def _process_failed_tracks_to_wishlist_exact_with_auto_completion(batch_id):
add_activity_item=add_activity_item, add_activity_item=add_activity_item,
automation_engine=automation_engine, automation_engine=automation_engine,
db_factory=MusicDatabase, db_factory=MusicDatabase,
logger=logger,
) )
except Exception as e: except Exception as e: