Initial commit
Dieser Commit ist enthalten in:
335
application/use_cases/log_account_creation_use_case.py
Normale Datei
335
application/use_cases/log_account_creation_use_case.py
Normale Datei
@ -0,0 +1,335 @@
|
||||
"""
|
||||
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)
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren