feat: Implementierung von Benachrichtigungen und sozialen Funktionen; Hinzufügen von API-Endpunkten für Benachrichtigungen, Benutzer-Follows und soziale Interaktionen; Verbesserung des Logging-Systems zur besseren Nachverfolgbarkeit von Systemereignissen.

This commit is contained in:
2025-05-28 22:08:56 +02:00
parent 1f4394e9b6
commit f5c2e70a11
31 changed files with 9294 additions and 591 deletions

View File

@@ -29,8 +29,8 @@ __all__ = [
'delete_user',
'create_admin_user',
# Server management
'run_development_server',
# Server management (imported separately to avoid circular imports)
# 'run_development_server' - available in utils.server module
]
# Import remaining modules that might depend on app
@@ -38,4 +38,4 @@ from .db_fix import fix_database_schema
from .db_rebuild import rebuild_database
from .db_test import test_database_connection, test_models, print_database_stats, run_all_tests
from .user_manager import list_users, create_user, reset_password, delete_user, create_admin_user
from .server import run_development_server
# Removed server import to prevent circular import - access via utils.server directly

Binary file not shown.

View File

@@ -1,17 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import current_app
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import text
import time
def check_db_connection(db):
def check_db_connection(db, app=None):
"""
Überprüft die Datenbankverbindung und versucht ggf. die Verbindung wiederherzustellen
Args:
db: SQLAlchemy-Instanz
app: Flask-App-Instanz (optional, falls nicht im App-Kontext)
Returns:
bool: True, wenn die Verbindung erfolgreich ist, sonst False
@@ -22,7 +22,11 @@ def check_db_connection(db):
while retry_count < max_retries:
try:
# Führe eine einfache Abfrage durch, um die Verbindung zu testen
with current_app.app_context():
if app:
with app.app_context():
db.session.execute(text('SELECT 1'))
else:
# Versuche ohne expliziten App-Kontext (falls bereits im Kontext)
db.session.execute(text('SELECT 1'))
return True
except SQLAlchemyError as e:
@@ -38,42 +42,60 @@ def check_db_connection(db):
db.session.rollback()
except:
pass
except Exception as e:
print(f"Allgemeiner Fehler bei DB-Check: {str(e)}")
retry_count += 1
if retry_count < max_retries:
time.sleep(1)
return False
def initialize_db_if_needed(db, initialize_function=None):
def initialize_db_if_needed(db, initialize_function=None, app=None):
"""
Initialisiert die Datenbank, falls erforderlich
Args:
db: SQLAlchemy-Instanz
initialize_function: Funktion, die aufgerufen wird, um die Datenbank zu initialisieren
app: Flask-App-Instanz (optional, falls nicht im App-Kontext)
Returns:
bool: True, wenn die Datenbank bereit ist, sonst False
"""
# Prüfe die Verbindung
if not check_db_connection(db):
if not check_db_connection(db, app):
return False
# Prüfe, ob die Tabellen existieren
try:
with current_app.app_context():
# Führe eine Testabfrage auf einer Tabelle durch
if app:
with app.app_context():
# Führe eine Testabfrage auf einer Tabelle durch
db.session.execute(text('SELECT COUNT(*) FROM user'))
else:
# Versuche ohne expliziten App-Kontext
db.session.execute(text('SELECT COUNT(*) FROM user'))
except SQLAlchemyError:
# Tabellen existieren nicht, erstelle sie
try:
with current_app.app_context():
if app:
with app.app_context():
db.create_all()
# Rufe die Initialisierungsfunktion auf, falls vorhanden
if initialize_function and callable(initialize_function):
initialize_function()
else:
db.create_all()
# Rufe die Initialisierungsfunktion auf, falls vorhanden
if initialize_function and callable(initialize_function):
initialize_function()
return True
return True
except Exception as e:
print(f"Fehler bei DB-Initialisierung: {str(e)}")
return False
except Exception as e:
print(f"Fehler beim Prüfen der Datenbank-Tabellen: {str(e)}")
return False
return True

792
utils/logger.py Normal file
View File

@@ -0,0 +1,792 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import sys
from datetime import datetime
from functools import wraps
from flask import request, g, current_app
from flask_login import current_user
import traceback
import json
import time
# ANSI Color Codes für farbige Terminal-Ausgabe
class Colors:
# Standard Colors
RESET = '\033[0m'
BOLD = '\033[1m'
DIM = '\033[2m'
# Foreground Colors
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'
# Bright Colors
BRIGHT_RED = '\033[91m'
BRIGHT_GREEN = '\033[92m'
BRIGHT_YELLOW = '\033[93m'
BRIGHT_BLUE = '\033[94m'
BRIGHT_MAGENTA = '\033[95m'
BRIGHT_CYAN = '\033[96m'
BRIGHT_WHITE = '\033[97m'
# Background Colors
BG_RED = '\033[41m'
BG_GREEN = '\033[42m'
BG_YELLOW = '\033[43m'
BG_BLUE = '\033[44m'
BG_MAGENTA = '\033[45m'
BG_CYAN = '\033[46m'
class LoggerConfig:
"""Konfiguration für das Logging-System"""
LOG_DIR = 'logs'
MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB
BACKUP_COUNT = 5
LOG_FORMAT = '%(asctime)s | %(levelname)s | %(name)s | %(message)s'
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
class ColoredFormatter(logging.Formatter):
"""Custom Formatter für farbige Log-Ausgaben mit schönen Emojis"""
LEVEL_COLORS = {
'DEBUG': Colors.BRIGHT_CYAN,
'INFO': Colors.BRIGHT_GREEN,
'WARNING': Colors.BRIGHT_YELLOW,
'ERROR': Colors.BRIGHT_RED,
'CRITICAL': Colors.BG_RED + Colors.BRIGHT_WHITE
}
COMPONENT_COLORS = {
'AUTH': Colors.BLUE,
'API': Colors.GREEN,
'DB': Colors.MAGENTA,
'SOCIAL': Colors.CYAN,
'SYSTEM': Colors.YELLOW,
'ERROR': Colors.RED,
'SECURITY': Colors.BRIGHT_MAGENTA,
'PERFORMANCE': Colors.BRIGHT_BLUE,
'ACTIVITY': Colors.BRIGHT_CYAN
}
# Erweiterte Emoji-Mappings für verschiedene Komponenten und Aktionen
COMPONENT_EMOJIS = {
'AUTH': '🔐',
'API': '🌐',
'DB': '🗄️',
'SOCIAL': '👥',
'SYSTEM': '⚙️',
'ERROR': '💥',
'SECURITY': '🛡️',
'PERFORMANCE': '',
'ACTIVITY': '🎯'
}
# Spezielle Emojis für verschiedene Log-Level
LEVEL_EMOJIS = {
'DEBUG': '🔍',
'INFO': '',
'WARNING': '⚠️',
'ERROR': '',
'CRITICAL': '🚨'
}
# Action-spezifische Emojis
ACTION_EMOJIS = {
'login': '🚪',
'logout': '🚪',
'register': '📝',
'like': '❤️',
'unlike': '💔',
'comment': '💬',
'share': '🔄',
'follow': '',
'unfollow': '',
'bookmark': '🔖',
'unbookmark': '📑',
'post_created': '📝',
'post_deleted': '🗑️',
'upload': '📤',
'download': '📥',
'search': '🔍',
'notification': '🔔',
'message': '💌',
'profile_update': '👤',
'settings': '⚙️',
'admin': '👑',
'backup': '💾',
'restore': '🔄',
'migration': '🚚',
'cache': '',
'email': '📧',
'password_reset': '🔑',
'verification': '',
'ban': '🚫',
'unban': '',
'report': '🚩',
'moderate': '🛡️'
}
def _get_action_emoji(self, message: str) -> str:
"""Ermittelt das passende Emoji basierend auf der Nachricht"""
message_lower = message.lower()
for action, emoji in self.ACTION_EMOJIS.items():
if action in message_lower:
return emoji
return '📝'
def format(self, record):
# Zeitstempel mit schöner Formatierung
timestamp = datetime.fromtimestamp(record.created).strftime('%H:%M:%S.%f')[:-3]
colored_timestamp = f"{Colors.DIM}{timestamp}{Colors.RESET}"
# Level mit Farbe und Emoji
level_color = self.LEVEL_COLORS.get(record.levelname, Colors.WHITE)
level_emoji = self.LEVEL_EMOJIS.get(record.levelname, '📝')
colored_level = f"{level_color}{level_emoji} {record.levelname:<8}{Colors.RESET}"
# Component mit Farbe und Emoji
component = getattr(record, 'component', 'SYSTEM')
component_color = self.COMPONENT_COLORS.get(component, Colors.WHITE)
component_emoji = self.COMPONENT_EMOJIS.get(component, '📝')
colored_component = f"{component_color}{component_emoji} [{component:<11}]{Colors.RESET}"
# Message mit Action-spezifischem Emoji
message = record.getMessage()
action_emoji = self._get_action_emoji(message)
# User-Info hinzufügen falls verfügbar
user_info = ""
if hasattr(record, 'user') and record.user:
user_info = f" {Colors.BRIGHT_BLUE}👤 {record.user}{Colors.RESET}"
# IP-Info hinzufügen falls verfügbar
ip_info = ""
if hasattr(record, 'ip') and record.ip:
ip_info = f" {Colors.DIM}🌍 {record.ip}{Colors.RESET}"
# Duration-Info hinzufügen falls verfügbar
duration_info = ""
if hasattr(record, 'duration') and record.duration:
if record.duration > 1000:
duration_color = Colors.BRIGHT_RED
duration_emoji = "🐌"
elif record.duration > 500:
duration_color = Colors.BRIGHT_YELLOW
duration_emoji = "⏱️"
else:
duration_color = Colors.BRIGHT_GREEN
duration_emoji = ""
duration_info = f" {duration_color}{duration_emoji} {record.duration:.2f}ms{Colors.RESET}"
# Separator für bessere Lesbarkeit
separator = f"{Colors.DIM}{Colors.RESET}"
# Finale formatierte Nachricht mit schöner Struktur
formatted_message = (
f"{colored_timestamp} {separator} "
f"{colored_level} {separator} "
f"{colored_component} {separator} "
f"{action_emoji} {message}"
f"{user_info}{ip_info}{duration_info}"
)
return formatted_message
class JSONFormatter(logging.Formatter):
"""JSON-Formatter für strukturierte Logs"""
def format(self, record):
log_entry = {
'timestamp': datetime.now().isoformat(),
'level': record.levelname,
'module': record.module,
'function': record.funcName,
'line': record.lineno,
'message': record.getMessage(),
'logger': record.name
}
# Benutzerinformationen hinzufügen - nur im Application Context
try:
from flask import has_app_context, g, current_user
if has_app_context():
if hasattr(g, 'user_id'):
log_entry['user_id'] = g.user_id
elif current_user and hasattr(current_user, 'id') and current_user.is_authenticated:
log_entry['user_id'] = current_user.id
except (ImportError, RuntimeError):
# Flask ist nicht verfügbar oder kein App-Context
pass
# Request-Informationen hinzufügen - nur im Request Context
try:
from flask import has_request_context, request
if has_request_context() and request:
log_entry['request'] = {
'method': getattr(request, 'method', None),
'path': getattr(request, 'path', None),
'remote_addr': getattr(request, 'remote_addr', None),
'user_agent': str(getattr(request, 'user_agent', ''))
}
except (ImportError, RuntimeError):
# Flask ist nicht verfügbar oder kein Request-Context
pass
# Performance-Informationen hinzufügen - nur im Application Context
try:
from flask import has_app_context, g
if has_app_context() and hasattr(g, 'start_time'):
duration = (datetime.now() - g.start_time).total_seconds() * 1000
log_entry['duration_ms'] = round(duration, 2)
except (ImportError, RuntimeError):
# Flask ist nicht verfügbar oder kein App-Context
pass
# Exception-Informationen hinzufügen
if record.exc_info:
log_entry['exception'] = {
'type': record.exc_info[0].__name__,
'message': str(record.exc_info[1]),
'traceback': self.formatException(record.exc_info)
}
return json.dumps(log_entry)
class SocialNetworkLogger:
"""Hauptklasse für das Social Network Logging"""
def __init__(self, name: str = 'SysTades'):
self.name = name
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)
# Log-Verzeichnis erstellen
os.makedirs(LoggerConfig.LOG_DIR, exist_ok=True)
# Handler nur einmal hinzufügen
if not self.logger.handlers:
self._setup_handlers()
def _setup_handlers(self):
"""Setup für verschiedene Log-Handler"""
# Console Handler mit Farben
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(ColoredFormatter())
self.logger.addHandler(console_handler)
# File Handler für alle Logs
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler(
os.path.join(LoggerConfig.LOG_DIR, 'app.log'),
maxBytes=LoggerConfig.MAX_LOG_SIZE,
backupCount=LoggerConfig.BACKUP_COUNT,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter(
'%(asctime)s | %(levelname)s | %(name)s | %(component)s | %(message)s',
datefmt=LoggerConfig.DATE_FORMAT
)
file_handler.setFormatter(file_formatter)
self.logger.addHandler(file_handler)
# Error Handler für nur Fehler
error_handler = RotatingFileHandler(
os.path.join(LoggerConfig.LOG_DIR, 'errors.log'),
maxBytes=LoggerConfig.MAX_LOG_SIZE,
backupCount=LoggerConfig.BACKUP_COUNT,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(file_formatter)
self.logger.addHandler(error_handler)
# API Handler für API-spezifische Logs
api_handler = RotatingFileHandler(
os.path.join(LoggerConfig.LOG_DIR, 'api.log'),
maxBytes=LoggerConfig.MAX_LOG_SIZE,
backupCount=LoggerConfig.BACKUP_COUNT,
encoding='utf-8'
)
api_handler.setLevel(logging.INFO)
api_handler.addFilter(lambda record: hasattr(record, 'component') and record.component == 'API')
api_handler.setFormatter(file_formatter)
self.logger.addHandler(api_handler)
def _log_with_context(self, level: str, message: str, component: str = 'SYSTEM', **kwargs):
"""Log mit erweiterten Kontext-Informationen"""
extra = {'component': component}
# User-Info hinzufügen
if 'user' in kwargs:
extra['user'] = kwargs['user']
# IP-Info hinzufügen
if 'ip' in kwargs:
extra['ip'] = kwargs['ip']
# Duration-Info hinzufügen
if 'duration' in kwargs:
extra['duration'] = kwargs['duration']
# Weitere Context-Daten
extra.update({k: v for k, v in kwargs.items() if k not in ['user', 'ip', 'duration']})
getattr(self.logger, level.lower())(message, extra=extra)
def debug(self, message: str, component: str = 'SYSTEM', **kwargs):
"""Debug-Level Logging mit erweiterten Infos"""
self._log_with_context('DEBUG', message, component, **kwargs)
def info(self, message: str, component: str = 'SYSTEM', **kwargs):
"""Info-Level Logging mit erweiterten Infos"""
self._log_with_context('INFO', message, component, **kwargs)
def warning(self, message: str, component: str = 'SYSTEM', **kwargs):
"""Warning-Level Logging mit erweiterten Infos"""
self._log_with_context('WARNING', message, component, **kwargs)
def error(self, message: str, component: str = 'ERROR', **kwargs):
"""Error-Level Logging mit erweiterten Infos"""
self._log_with_context('ERROR', message, component, **kwargs)
def critical(self, message: str, component: str = 'ERROR', **kwargs):
"""Critical-Level Logging mit erweiterten Infos"""
self._log_with_context('CRITICAL', message, component, **kwargs)
# Erweiterte spezielle Logging-Methoden für Social Network
def auth_success(self, username: str, ip: str = None, method: str = 'password'):
"""Erfolgreiche Authentifizierung mit Details"""
message = f"Benutzer '{username}' erfolgreich angemeldet"
if method != 'password':
message += f" (Methode: {method})"
self.info(message, 'AUTH', user=username, ip=ip)
def auth_failure(self, username: str, ip: str = None, reason: str = None, method: str = 'password'):
"""Fehlgeschlagene Authentifizierung mit Details"""
message = f"Anmeldung fehlgeschlagen für '{username}'"
if reason:
message += f" - Grund: {reason}"
if method != 'password':
message += f" (Methode: {method})"
self.warning(message, 'AUTH', user=username, ip=ip)
def user_action(self, username: str, action: str, details: str = None, target: str = None):
"""Erweiterte Benutzer-Aktion mit mehr Details"""
message = f"{username}: {action}"
if target:
message += f"{target}"
if details:
message += f" ({details})"
self.info(message, 'ACTIVITY', user=username)
def api_request(self, method: str, endpoint: str, user: str = None, status: int = None, duration: float = None, size: int = None):
"""Erweiterte API Request Logging"""
message = f"{method} {endpoint}"
# Status-spezifische Emojis und Farben
if status:
if status >= 500:
message = f"Server Error: {message}"
component = 'ERROR'
elif status >= 400:
message = f"Client Error: {message}"
component = 'API'
elif status >= 300:
message = f"Redirect: {message}"
component = 'API'
else:
message = f"Success: {message}"
component = 'API'
else:
component = 'API'
# Zusätzliche Infos
extras = {}
if user:
extras['user'] = user
if duration:
extras['duration'] = duration * 1000 # Convert to ms
if size:
message += f" ({self._format_bytes(size)})"
if status and status >= 400:
self.warning(message, component, **extras)
else:
self.info(message, component, **extras)
def database_operation(self, operation: str, table: str, success: bool = True, details: str = None, affected_rows: int = None):
"""Erweiterte Datenbank-Operation Logging"""
message = f"DB {operation.upper()} auf '{table}'"
if affected_rows is not None:
message += f" ({affected_rows} Zeilen)"
if details:
message += f" - {details}"
if success:
self.info(message, 'DB')
else:
self.error(message, 'DB')
def security_event(self, event: str, user: str = None, ip: str = None, severity: str = 'warning', details: str = None):
"""Erweiterte Sicherheitsereignis Logging"""
message = f"Security Event: {event}"
if details:
message += f" - {details}"
extras = {}
if user:
extras['user'] = user
if ip:
extras['ip'] = ip
if severity == 'critical':
self.critical(message, 'SECURITY', **extras)
elif severity == 'error':
self.error(message, 'SECURITY', **extras)
else:
self.warning(message, 'SECURITY', **extras)
def performance_metric(self, metric_name: str, value: float, unit: str = 'ms', threshold: dict = None):
"""Erweiterte Performance-Metrik Logging"""
message = f"Performance: {metric_name} = {value}{unit}"
# Threshold-basierte Bewertung
if threshold and unit == 'ms':
if value > threshold.get('critical', 2000):
self.critical(message, 'PERFORMANCE', duration=value)
elif value > threshold.get('warning', 1000):
self.warning(message, 'PERFORMANCE', duration=value)
else:
self.info(message, 'PERFORMANCE', duration=value)
else:
self.info(message, 'PERFORMANCE')
def social_interaction(self, user: str, action: str, target: str, target_type: str = 'post', target_user: str = None):
"""Erweiterte Social Media Interaktion Logging"""
message = f"{user} {action} {target_type}"
if target_user and target_user != user:
message += f" von {target_user}"
message += f" (ID: {target})"
self.info(message, 'SOCIAL', user=user)
def system_startup(self, version: str = None, environment: str = None, port: int = None):
"""Erweiterte System-Start Logging"""
message = "🚀 SysTades Social Network gestartet"
if version:
message += f" (v{version})"
if environment:
message += f" in {environment} Umgebung"
if port:
message += f" auf Port {port}"
self.info(message, 'SYSTEM')
def system_shutdown(self, reason: str = None, uptime: float = None):
"""Erweiterte System-Shutdown Logging"""
message = "🛑 SysTades Social Network beendet"
if uptime:
message += f" (Laufzeit: {self._format_duration(uptime)})"
if reason:
message += f" - Grund: {reason}"
self.info(message, 'SYSTEM')
def file_operation(self, operation: str, filename: str, success: bool = True, size: int = None, user: str = None):
"""Datei-Operation Logging"""
message = f"File {operation.upper()}: {filename}"
if size:
message += f" ({self._format_bytes(size)})"
extras = {}
if user:
extras['user'] = user
if success:
self.info(message, 'SYSTEM', **extras)
else:
self.error(message, 'SYSTEM', **extras)
def cache_operation(self, operation: str, key: str, hit: bool = None, size: int = None):
"""Cache-Operation Logging"""
message = f"Cache {operation.upper()}: {key}"
if hit is not None:
message += f" ({'HIT' if hit else 'MISS'})"
if size:
message += f" ({self._format_bytes(size)})"
self.debug(message, 'SYSTEM')
def email_sent(self, recipient: str, subject: str, success: bool = True, error: str = None):
"""E-Mail Versand Logging"""
message = f"E-Mail an {recipient}: '{subject}'"
if not success and error:
message += f" - Fehler: {error}"
if success:
self.info(message, 'SYSTEM')
else:
self.error(message, 'SYSTEM')
def _format_bytes(self, bytes_count: int) -> str:
"""Formatiert Byte-Anzahl in lesbare Form"""
for unit in ['B', 'KB', 'MB', 'GB']:
if bytes_count < 1024.0:
return f"{bytes_count:.1f}{unit}"
bytes_count /= 1024.0
return f"{bytes_count:.1f}TB"
def _format_duration(self, seconds: float) -> str:
"""Formatiert Dauer in lesbare Form"""
if seconds < 60:
return f"{seconds:.1f}s"
elif seconds < 3600:
return f"{seconds/60:.1f}min"
else:
return f"{seconds/3600:.1f}h"
def exception(self, exc: Exception, context: str = None, user: str = None):
"""Erweiterte Exception Logging mit mehr Details"""
message = f"Exception: {type(exc).__name__}: {str(exc)}"
if context:
message = f"{context} - {message}"
# Stack-Trace hinzufügen
stack_trace = traceback.format_exc()
message += f"\n{stack_trace}"
extras = {}
if user:
extras['user'] = user
self.error(message, 'ERROR', **extras)
def log_execution_time(component: str = 'SYSTEM'):
"""Decorator für Ausführungszeit-Logging"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logger = SocialNetworkLogger()
start_time = time.time()
try:
result = func(*args, **kwargs)
execution_time = (time.time() - start_time) * 1000
logger.performance_metric(f"{func.__name__} Ausführungszeit", execution_time, 'ms')
return result
except Exception as e:
execution_time = (time.time() - start_time) * 1000
logger.exception(e, f"Fehler in {func.__name__} nach {execution_time:.2f}ms")
raise
return wrapper
return decorator
def log_api_call(func):
"""Decorator für API-Call Logging"""
@wraps(func)
def wrapper(*args, **kwargs):
from flask import request, current_user
logger = SocialNetworkLogger()
start_time = time.time()
# Request-Informationen sammeln
method = request.method
endpoint = request.endpoint or request.path
user = current_user.username if hasattr(current_user, 'username') and current_user.is_authenticated else 'Anonymous'
try:
result = func(*args, **kwargs)
duration = time.time() - start_time
# Status-Code ermitteln
status = getattr(result, 'status_code', 200) if hasattr(result, 'status_code') else 200
logger.api_request(method, endpoint, user, status, duration)
return result
except Exception as e:
duration = time.time() - start_time
logger.api_request(method, endpoint, user, 500, duration)
logger.exception(e, f"API-Fehler in {endpoint}")
raise
return wrapper
def performance_monitor(operation_name: str = None):
"""Erweiterte Decorator für Performance-Monitoring mit schönen Logs"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logger = SocialNetworkLogger()
start_time = time.time()
op_name = operation_name or func.__name__
# User-Info ermitteln falls verfügbar
user = None
try:
from flask import current_user
if hasattr(current_user, 'username') and current_user.is_authenticated:
user = current_user.username
except:
pass
try:
result = func(*args, **kwargs)
duration = (time.time() - start_time) * 1000
# Performance-Kategorisierung mit Emojis
if duration > 2000:
logger.critical(f"Kritisch langsame Operation: {op_name}", 'PERFORMANCE',
user=user, duration=duration)
elif duration > 1000:
logger.warning(f"Langsame Operation: {op_name}", 'PERFORMANCE',
user=user, duration=duration)
elif duration > 500:
logger.info(f"Mäßige Operation: {op_name}", 'PERFORMANCE',
user=user, duration=duration)
else:
logger.debug(f"Schnelle Operation: {op_name}", 'PERFORMANCE',
user=user, duration=duration)
return result
except Exception as e:
duration = (time.time() - start_time) * 1000
logger.error(f"Fehler in Operation: {op_name} nach {duration:.2f}ms", 'PERFORMANCE',
user=user, duration=duration)
logger.exception(e, f"Performance Monitor - {op_name}", user=user)
raise
return wrapper
return decorator
def log_user_activity(activity_name: str):
"""Erweiterte Decorator für User-Activity Logging mit Details"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
from flask import current_user, request
logger = SocialNetworkLogger()
start_time = time.time()
# User und Request-Info sammeln
username = 'Anonymous'
ip = None
user_agent = None
try:
if hasattr(current_user, 'username') and current_user.is_authenticated:
username = current_user.username
if request:
ip = request.remote_addr
user_agent = str(request.user_agent)[:100] # Begrenzen
except:
pass
try:
result = func(*args, **kwargs)
duration = (time.time() - start_time) * 1000
# Erfolgreiche Aktivität loggen
details = f"Erfolgreich in {duration:.2f}ms"
if user_agent:
details += f" (Browser: {user_agent.split('/')[0] if '/' in user_agent else user_agent})"
logger.user_action(username, activity_name, details=details)
return result
except Exception as e:
duration = (time.time() - start_time) * 1000
logger.error(f"Fehler in User-Activity '{activity_name}' für {username} nach {duration:.2f}ms: {str(e)}",
'ACTIVITY', user=username, ip=ip, duration=duration)
logger.exception(e, f"User Activity - {activity_name}", user=username)
raise
return wrapper
return decorator
# Globale Logger-Instanz
social_logger = SocialNetworkLogger()
def get_logger(name: str = None) -> SocialNetworkLogger:
"""Factory-Funktion für Logger-Instanzen"""
if name:
return SocialNetworkLogger(name)
return social_logger
# Convenience-Funktionen für häufige Log-Operationen
def log_user_login(username: str, ip: str = None, success: bool = True):
"""Shortcut für Login-Logging"""
if success:
social_logger.auth_success(username, ip)
else:
social_logger.auth_failure(username, ip)
def log_user_action(username: str, action: str, details: str = None):
"""Shortcut für Benutzer-Aktionen"""
social_logger.user_action(username, action, details)
def log_social_action(user: str, action: str, target: str, target_type: str = 'post'):
"""Shortcut für Social Media Aktionen"""
social_logger.social_interaction(user, action, target, target_type)
def log_error(message: str, exception: Exception = None):
"""Shortcut für Error-Logging"""
if exception:
social_logger.exception(exception, message)
else:
social_logger.error(message)
def log_performance(metric_name: str, value: float, unit: str = 'ms'):
"""Shortcut für Performance-Logging"""
social_logger.performance_metric(metric_name, value, unit)
# Setup-Funktion für initiale Konfiguration
def setup_logging(app=None, log_level: str = 'INFO'):
"""Setup-Funktion für die Flask-App"""
if app:
# Flask App Logging konfigurieren
app.logger.handlers.clear()
app.logger.addHandler(social_logger.logger.handlers[0]) # Console Handler
app.logger.setLevel(getattr(logging, log_level.upper()))
# System-Start loggen
social_logger.system_startup()
return social_logger
if __name__ == "__main__":
# Test des Logging-Systems
logger = SocialNetworkLogger()
logger.info("🧪 Teste das Logging-System")
logger.auth_success("testuser", "192.168.1.1")
logger.user_action("testuser", "Post erstellt", "Neuer Gedanke geteilt")
logger.social_interaction("user1", "like", "post_123")
logger.api_request("GET", "/api/social/posts", "testuser", 200, 0.045)
logger.database_operation("INSERT", "social_posts", True, "Neuer Post gespeichert")
logger.performance_metric("Seitenladezeit", 1234.5)
logger.warning("⚠️ Test-Warnung")
logger.error("❌ Test-Fehler")
logger.debug("🔍 Debug-Information")
print(f"\n{Colors.BRIGHT_GREEN}✅ Logging-System erfolgreich getestet!{Colors.RESET}")
print(f"{Colors.CYAN}📁 Logs gespeichert in: {LoggerConfig.LOG_DIR}/{Colors.RESET}")

View File

@@ -9,11 +9,22 @@ from datetime import datetime
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
from app import app
# Import models direkt, app wird lazy geladen
from models import db, User
def get_app():
"""Lazy loading der Flask app um zirkuläre Imports zu vermeiden"""
try:
from flask import current_app
return current_app
except RuntimeError:
# Fallback wenn kein app context existiert
from app import app
return app
def list_users():
"""List all users in the database."""
app = get_app()
with app.app_context():
try:
users = User.query.all()
@@ -37,6 +48,7 @@ def list_users():
def create_user(username, email, password, is_admin=False):
"""Create a new user in the database."""
app = get_app()
with app.app_context():
try:
# Check if user already exists
@@ -73,6 +85,7 @@ def create_user(username, email, password, is_admin=False):
def reset_password(username, new_password):
"""Reset password for a user."""
app = get_app()
with app.app_context():
try:
user = User.query.filter_by(username=username).first()
@@ -93,6 +106,7 @@ def reset_password(username, new_password):
def delete_user(username):
"""Delete a user from the database."""
app = get_app()
with app.app_context():
try:
user = User.query.filter_by(username=username).first()