""" Log Account Creation Use Case - Strukturiertes Logging für Account-Erstellung """ import logging import time from typing import Dict, Any, Optional, List from datetime import datetime, timedelta import uuid from domain.services.analytics_service import IAnalyticsService from domain.entities.account_creation_event import ( AccountCreationEvent, AccountData, WorkflowStep, WorkflowStepStatus, ErrorDetails ) from domain.value_objects.action_timing import ActionType logger = logging.getLogger("log_account_creation_use_case") class LogAccountCreationUseCase: """ Use Case für strukturiertes Logging von Account-Erstellungen. Trackt detaillierte Steps, Performance-Metriken und Fehler-Kontextualisierung. """ def __init__(self, analytics_service: IAnalyticsService): self.analytics_service = analytics_service self.active_events = {} # event_id -> AccountCreationEvent def start_tracking(self, platform: str, session_id: str, fingerprint_id: str, context: Optional[Dict[str, Any]] = None) -> str: """ Startet Tracking für neue Account-Erstellung. Args: platform: Zielplattform session_id: Session ID fingerprint_id: Fingerprint ID context: Zusätzlicher Kontext Returns: Event ID für weiteres Tracking """ event = AccountCreationEvent( event_id=str(uuid.uuid4()), timestamp=datetime.now(), session_id=session_id, fingerprint_id=fingerprint_id, proxy_used=context.get('proxy_used', False) if context else False, proxy_type=context.get('proxy_type') if context else None, browser_type=context.get('browser_type', 'chromium') if context else 'chromium', headless=context.get('headless', False) if context else False ) # Speichere temporär für Step-Tracking self.active_events[event.event_id] = event logger.info(f"Started tracking account creation {event.event_id} for {platform}") return event.event_id def track_step(self, event_id: str, step_name: str, metadata: Optional[Dict[str, Any]] = None) -> None: """ Beginnt Tracking eines Workflow-Schritts. Args: event_id: Event ID step_name: Name des Schritts metadata: Zusätzliche Metadaten """ event = self.active_events.get(event_id) if not event: logger.error(f"No active event found for {event_id}") return step = WorkflowStep( step_name=step_name, start_time=datetime.now(), status=WorkflowStepStatus.IN_PROGRESS, metadata=metadata or {} ) event.add_step(step) logger.debug(f"Started step '{step_name}' for event {event_id}") def complete_step(self, event_id: str, step_name: str, success: bool = True, error_message: Optional[str] = None, retry_count: int = 0) -> None: """ Markiert einen Schritt als abgeschlossen. Args: event_id: Event ID step_name: Name des Schritts success: Ob Schritt erfolgreich war error_message: Fehlermeldung bei Misserfolg retry_count: Anzahl der Wiederholungen """ event = self.active_events.get(event_id) if not event: logger.error(f"No active event found for {event_id}") return step = event.get_step(step_name) if not step: logger.error(f"Step '{step_name}' not found in event {event_id}") return step.end_time = datetime.now() step.status = WorkflowStepStatus.COMPLETED if success else WorkflowStepStatus.FAILED step.retry_count = retry_count step.error_message = error_message # Update Metriken event.network_requests += step.metadata.get('network_requests', 0) event.screenshots_taken += step.metadata.get('screenshots', 0) logger.debug(f"Completed step '{step_name}' for event {event_id} " f"(success: {success}, duration: {step.duration})") def set_account_data(self, event_id: str, username: str, password: str, email: str, additional_data: Optional[Dict[str, Any]] = None) -> None: """ Setzt Account-Daten für erfolgreich erstellten Account. Args: event_id: Event ID username: Benutzername password: Passwort email: E-Mail additional_data: Zusätzliche Daten """ event = self.active_events.get(event_id) if not event: logger.error(f"No active event found for {event_id}") return event.account_data = AccountData( platform=additional_data.get('platform', 'unknown') if additional_data else 'unknown', username=username, password=password, email=email, phone=additional_data.get('phone') if additional_data else None, full_name=additional_data.get('full_name') if additional_data else None, birthday=additional_data.get('birthday') if additional_data else None, verification_status=additional_data.get('verification_status', 'unverified') if additional_data else 'unverified', metadata=additional_data or {} ) logger.info(f"Set account data for {username} in event {event_id}") def log_error(self, event_id: str, error_type: str, error_message: str, stack_trace: Optional[str] = None, screenshot_path: Optional[str] = None, context: Optional[Dict[str, Any]] = None) -> None: """ Loggt einen Fehler während der Account-Erstellung. Args: event_id: Event ID error_type: Typ des Fehlers error_message: Fehlermeldung stack_trace: Stack Trace screenshot_path: Pfad zum Fehler-Screenshot context: Fehler-Kontext """ event = self.active_events.get(event_id) if not event: logger.error(f"No active event found for {event_id}") return event.error_details = ErrorDetails( error_type=error_type, error_message=error_message, stack_trace=stack_trace, screenshot_path=screenshot_path, context=context or {} ) logger.error(f"Logged error for event {event_id}: {error_type} - {error_message}") def finish_tracking(self, event_id: str, success: bool, final_status: Optional[Dict[str, Any]] = None) -> None: """ Beendet Tracking und speichert Event. Args: event_id: Event ID success: Ob Account-Erstellung erfolgreich war final_status: Finaler Status/Metadaten """ event = self.active_events.get(event_id) if not event: logger.error(f"No active event found for {event_id}") return # Setze finale Eigenschaften event.success = success event.calculate_duration() # Füge finale Metadaten hinzu if final_status: if event.account_data: event.account_data.metadata.update(final_status) # Logge Event self.analytics_service.log_event(event) # Entferne aus aktiven Events del self.active_events[event_id] # Log Summary self._log_summary(event) def _log_summary(self, event: AccountCreationEvent) -> None: """Loggt eine Zusammenfassung des Events""" summary = f"Account creation {event.event_id} " if event.success: summary += f"SUCCEEDED" if event.account_data: summary += f" - {event.account_data.username} on {event.account_data.platform}" else: summary += f"FAILED" if event.error_details: summary += f" - {event.error_details.error_type}: {event.error_details.error_message}" if event.duration: summary += f" (duration: {event.duration.total_seconds():.1f}s" summary += f", steps: {len(event.steps_completed)}" summary += f", retries: {event.total_retry_count})" logger.info(summary) def track_performance_metric(self, event_id: str, metric_name: str, value: float, tags: Optional[Dict[str, str]] = None) -> None: """ Trackt eine Performance-Metrik. Args: event_id: Event ID metric_name: Name der Metrik value: Wert der Metrik tags: Zusätzliche Tags """ # Tracke über Analytics Service metric_tags = tags or {} metric_tags['event_id'] = event_id self.analytics_service.track_performance(metric_name, value, metric_tags) def get_active_events(self) -> List[Dict[str, Any]]: """Gibt Liste aktiver Events zurück""" active = [] for event_id, event in self.active_events.items(): duration = (datetime.now() - event.timestamp).total_seconds() current_step = None # Finde aktuellen Schritt for step in event.steps_completed: if step.status == WorkflowStepStatus.IN_PROGRESS: current_step = step.step_name break active.append({ 'event_id': event_id, 'started_at': event.timestamp.isoformat(), 'duration_seconds': duration, 'current_step': current_step, 'steps_completed': len([s for s in event.steps_completed if s.status == WorkflowStepStatus.COMPLETED]), 'platform': event.account_data.platform if event.account_data else 'unknown' }) return active def cleanup_stale_events(self, timeout_minutes: int = 30) -> int: """ Bereinigt Events die zu lange laufen. Args: timeout_minutes: Timeout in Minuten Returns: Anzahl bereinigter Events """ stale_events = [] timeout = timedelta(minutes=timeout_minutes) for event_id, event in self.active_events.items(): if datetime.now() - event.timestamp > timeout: stale_events.append(event_id) for event_id in stale_events: event = self.active_events[event_id] # Markiere als Timeout self.log_error( event_id, 'timeout', f'Event timed out after {timeout_minutes} minutes', context={'timeout_minutes': timeout_minutes} ) # Beende Tracking self.finish_tracking(event_id, success=False, final_status={'reason': 'timeout'}) if stale_events: logger.warning(f"Cleaned up {len(stale_events)} stale events") return len(stale_events)