refactor(logging): standardize logging approach across services and steps
This commit is contained in:
@@ -17,7 +17,7 @@ import pytz
|
|||||||
from services.exceptions import LockAcquisitionError, SyncError, ValidationError
|
from services.exceptions import LockAcquisitionError, SyncError, ValidationError
|
||||||
from services.redis_client import get_redis_client
|
from services.redis_client import get_redis_client
|
||||||
from services.config import SYNC_CONFIG, get_lock_key, get_retry_delay_seconds
|
from services.config import SYNC_CONFIG, get_lock_key, get_retry_delay_seconds
|
||||||
from services.logging_utils import get_logger
|
from services.logging_utils import get_service_logger
|
||||||
|
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ class BeteiligteSync:
|
|||||||
def __init__(self, espocrm_api, redis_client: Optional[redis.Redis] = None, context=None):
|
def __init__(self, espocrm_api, redis_client: Optional[redis.Redis] = None, context=None):
|
||||||
self.espocrm = espocrm_api
|
self.espocrm = espocrm_api
|
||||||
self.context = context
|
self.context = context
|
||||||
self.logger = get_logger('beteiligte_sync', context)
|
self.logger = get_service_logger('beteiligte_sync', context)
|
||||||
|
|
||||||
# Use provided Redis client or get from factory
|
# Use provided Redis client or get from factory
|
||||||
self.redis = redis_client or get_redis_client(strict=False)
|
self.redis = redis_client or get_redis_client(strict=False)
|
||||||
@@ -46,6 +46,11 @@ class BeteiligteSync:
|
|||||||
from services.notification_utils import NotificationManager
|
from services.notification_utils import NotificationManager
|
||||||
self.notification_manager = NotificationManager(espocrm_api=self.espocrm, context=context)
|
self.notification_manager = NotificationManager(espocrm_api=self.espocrm, context=context)
|
||||||
|
|
||||||
|
def _log(self, message: str, level: str = 'info') -> None:
|
||||||
|
"""Delegate logging to the logger with optional level"""
|
||||||
|
log_func = getattr(self.logger, level, self.logger.info)
|
||||||
|
log_func(message)
|
||||||
|
|
||||||
async def acquire_sync_lock(self, entity_id: str) -> bool:
|
async def acquire_sync_lock(self, entity_id: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Atomic distributed lock via Redis + syncStatus update
|
Atomic distributed lock via Redis + syncStatus update
|
||||||
|
|||||||
@@ -10,12 +10,9 @@ Hilfsfunktionen für Document-Synchronisation mit xAI:
|
|||||||
|
|
||||||
from typing import Dict, Any, Optional, List, Tuple
|
from typing import Dict, Any, Optional, List, Tuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import logging
|
|
||||||
|
|
||||||
from services.sync_utils_base import BaseSyncUtils
|
from services.sync_utils_base import BaseSyncUtils
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Max retry before permanent failure
|
# Max retry before permanent failure
|
||||||
MAX_SYNC_RETRIES = 5
|
MAX_SYNC_RETRIES = 5
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,59 @@ Vereinheitlicht Logging über:
|
|||||||
- Standard Python Logger
|
- Standard Python Logger
|
||||||
- Motia FlowContext Logger
|
- Motia FlowContext Logger
|
||||||
- Structured Logging
|
- Structured Logging
|
||||||
|
|
||||||
|
Usage Guidelines:
|
||||||
|
=================
|
||||||
|
|
||||||
|
FOR SERVICES: Use get_service_logger('service_name', context)
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
Example:
|
||||||
|
from services.logging_utils import get_service_logger
|
||||||
|
|
||||||
|
class XAIService:
|
||||||
|
def __init__(self, ctx=None):
|
||||||
|
self.logger = get_service_logger('xai', ctx)
|
||||||
|
|
||||||
|
def upload(self):
|
||||||
|
self.logger.info("Uploading file...")
|
||||||
|
|
||||||
|
FOR STEPS: Use ctx.logger directly (preferred)
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
Steps already have ctx.logger available - use it directly:
|
||||||
|
async def handler(event_data, ctx: FlowContext):
|
||||||
|
ctx.logger.info("Processing event")
|
||||||
|
|
||||||
|
Alternative: Use get_step_logger() for additional loggers:
|
||||||
|
step_logger = get_step_logger('beteiligte_sync', ctx)
|
||||||
|
|
||||||
|
FOR SYNC UTILS: Inherit from BaseSyncUtils (provides self.logger)
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
from services.sync_utils_base import BaseSyncUtils
|
||||||
|
|
||||||
|
class MySync(BaseSyncUtils):
|
||||||
|
def __init__(self, espocrm, redis, context):
|
||||||
|
super().__init__(espocrm, redis, context)
|
||||||
|
# self.logger is now available
|
||||||
|
|
||||||
|
def sync(self):
|
||||||
|
self._log("Syncing...", level='info')
|
||||||
|
|
||||||
|
FOR STANDALONE UTILITIES: Use get_logger()
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
from services.logging_utils import get_logger
|
||||||
|
|
||||||
|
logger = get_logger('my_module', context)
|
||||||
|
logger.info("Processing...")
|
||||||
|
|
||||||
|
CONSISTENCY RULES:
|
||||||
|
==================
|
||||||
|
✅ Services: get_service_logger('service_name', ctx)
|
||||||
|
✅ Steps: ctx.logger (direct) or get_step_logger('step_name', ctx)
|
||||||
|
✅ Sync Utils: Inherit from BaseSyncUtils → use self._log() or self.logger
|
||||||
|
✅ Standalone: get_logger('module_name', ctx)
|
||||||
|
|
||||||
|
❌ DO NOT: Use module-level logging.getLogger(__name__)
|
||||||
|
❌ DO NOT: Mix get_logger() and get_service_logger() in same module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import pytz
|
|||||||
from services.exceptions import RedisConnectionError, LockAcquisitionError
|
from services.exceptions import RedisConnectionError, LockAcquisitionError
|
||||||
from services.redis_client import get_redis_client
|
from services.redis_client import get_redis_client
|
||||||
from services.config import SYNC_CONFIG, get_lock_key
|
from services.config import SYNC_CONFIG, get_lock_key
|
||||||
from services.logging_utils import get_logger
|
from services.logging_utils import get_service_logger
|
||||||
|
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ class BaseSyncUtils:
|
|||||||
"""
|
"""
|
||||||
self.espocrm = espocrm_api
|
self.espocrm = espocrm_api
|
||||||
self.context = context
|
self.context = context
|
||||||
self.logger = get_logger('sync_utils', context)
|
self.logger = get_service_logger('sync_utils', context)
|
||||||
|
|
||||||
# Use provided Redis client or get from factory
|
# Use provided Redis client or get from factory
|
||||||
self.redis = redis_client or get_redis_client(strict=False)
|
self.redis = redis_client or get_redis_client(strict=False)
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
"""xAI Files & Collections Service"""
|
"""xAI Files & Collections Service"""
|
||||||
import os
|
import os
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import logging
|
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
from services.logging_utils import get_service_logger
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
XAI_FILES_URL = "https://api.x.ai"
|
XAI_FILES_URL = "https://api.x.ai"
|
||||||
XAI_MANAGEMENT_URL = "https://management-api.x.ai"
|
XAI_MANAGEMENT_URL = "https://management-api.x.ai"
|
||||||
@@ -23,6 +21,7 @@ class XAIService:
|
|||||||
self.api_key = os.getenv('XAI_API_KEY', '')
|
self.api_key = os.getenv('XAI_API_KEY', '')
|
||||||
self.management_key = os.getenv('XAI_MANAGEMENT_KEY', '')
|
self.management_key = os.getenv('XAI_MANAGEMENT_KEY', '')
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
|
self.logger = get_service_logger('xai', ctx)
|
||||||
self._session: Optional[aiohttp.ClientSession] = None
|
self._session: Optional[aiohttp.ClientSession] = None
|
||||||
|
|
||||||
if not self.api_key:
|
if not self.api_key:
|
||||||
@@ -31,10 +30,9 @@ class XAIService:
|
|||||||
raise ValueError("XAI_MANAGEMENT_KEY not configured in environment")
|
raise ValueError("XAI_MANAGEMENT_KEY not configured in environment")
|
||||||
|
|
||||||
def _log(self, msg: str, level: str = 'info') -> None:
|
def _log(self, msg: str, level: str = 'info') -> None:
|
||||||
if self.ctx:
|
"""Delegate logging to service logger"""
|
||||||
getattr(self.ctx.logger, level, self.ctx.logger.info)(msg)
|
log_func = getattr(self.logger, level, self.logger.info)
|
||||||
else:
|
log_func(msg)
|
||||||
getattr(logger, level, logger.info)(msg)
|
|
||||||
|
|
||||||
async def _get_session(self) -> aiohttp.ClientSession:
|
async def _get_session(self) -> aiohttp.ClientSession:
|
||||||
if self._session is None or self._session.closed:
|
if self._session is None or self._session.closed:
|
||||||
|
|||||||
@@ -32,23 +32,33 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
body={'error': 'Endpoint required as query parameter'}
|
body={'error': 'Endpoint required as query parameter'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("🔄 ADVOWARE PROXY: DELETE REQUEST")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info(f"Endpoint: {endpoint}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Initialize Advoware client
|
# Initialize Advoware client
|
||||||
advoware = AdvowareAPI(ctx)
|
advoware = AdvowareAPI(ctx)
|
||||||
|
|
||||||
# Forward all query params except 'endpoint'
|
# Forward all query params except 'endpoint'
|
||||||
params = {k: v for k, v in request.query_params.items() if k != 'endpoint'}
|
params = {k: v for k, v in request.query_params.items() if k != 'endpoint'}
|
||||||
|
|
||||||
ctx.logger.info(f"Proxying DELETE request to Advoware: {endpoint}")
|
|
||||||
result = await advoware.api_call(
|
result = await advoware.api_call(
|
||||||
endpoint,
|
endpoint,
|
||||||
method='DELETE',
|
method='DELETE',
|
||||||
params=params
|
params=params
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Proxy DELETE erfolgreich")
|
||||||
return ApiResponse(status=200, body={'result': result})
|
return ApiResponse(status=200, body={'result': result})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Proxy error: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ ADVOWARE PROXY DELETE FEHLER")
|
||||||
|
ctx.logger.error(f"Endpoint: {request.query_params.get('endpoint', 'N/A')}")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={'error': 'Internal server error', 'details': str(e)}
|
body={'error': 'Internal server error', 'details': str(e)}
|
||||||
|
|||||||
@@ -32,23 +32,33 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
body={'error': 'Endpoint required as query parameter'}
|
body={'error': 'Endpoint required as query parameter'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("🔄 ADVOWARE PROXY: GET REQUEST")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info(f"Endpoint: {endpoint}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Initialize Advoware client
|
# Initialize Advoware client
|
||||||
advoware = AdvowareAPI(ctx)
|
advoware = AdvowareAPI(ctx)
|
||||||
|
|
||||||
# Forward all query params except 'endpoint'
|
# Forward all query params except 'endpoint'
|
||||||
params = {k: v for k, v in request.query_params.items() if k != 'endpoint'}
|
params = {k: v for k, v in request.query_params.items() if k != 'endpoint'}
|
||||||
|
|
||||||
ctx.logger.info(f"Proxying GET request to Advoware: {endpoint}")
|
|
||||||
result = await advoware.api_call(
|
result = await advoware.api_call(
|
||||||
endpoint,
|
endpoint,
|
||||||
method='GET',
|
method='GET',
|
||||||
params=params
|
params=params
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Proxy GET erfolgreich")
|
||||||
return ApiResponse(status=200, body={'result': result})
|
return ApiResponse(status=200, body={'result': result})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Proxy error: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ ADVOWARE PROXY GET FEHLER")
|
||||||
|
ctx.logger.error(f"Endpoint: {request.query_params.get('endpoint', 'N/A')}")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={'error': 'Internal server error', 'details': str(e)}
|
body={'error': 'Internal server error', 'details': str(e)}
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
body={'error': 'Endpoint required as query parameter'}
|
body={'error': 'Endpoint required as query parameter'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("🔄 ADVOWARE PROXY: POST REQUEST")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info(f"Endpoint: {endpoint}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Initialize Advoware client
|
# Initialize Advoware client
|
||||||
advoware = AdvowareAPI(ctx)
|
advoware = AdvowareAPI(ctx)
|
||||||
|
|
||||||
@@ -43,7 +49,6 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
# Get request body
|
# Get request body
|
||||||
json_data = request.body
|
json_data = request.body
|
||||||
|
|
||||||
ctx.logger.info(f"Proxying POST request to Advoware: {endpoint}")
|
|
||||||
result = await advoware.api_call(
|
result = await advoware.api_call(
|
||||||
endpoint,
|
endpoint,
|
||||||
method='POST',
|
method='POST',
|
||||||
@@ -51,11 +56,17 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
json_data=json_data
|
json_data=json_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Proxy POST erfolgreich")
|
||||||
return ApiResponse(status=200, body={'result': result})
|
return ApiResponse(status=200, body={'result': result})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Proxy error: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ ADVOWARE PROXY POST FEHLER")
|
||||||
|
ctx.logger.error(f"Endpoint: {request.query_params.get('endpoint', 'N/A')}")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={'error': 'Internal server error', 'details': str(e)}
|
body={'error': 'Internal server error', 'details': str(e)}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
body={'error': 'Endpoint required as query parameter'}
|
body={'error': 'Endpoint required as query parameter'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("🔄 ADVOWARE PROXY: PUT REQUEST")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info(f"Endpoint: {endpoint}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Initialize Advoware client
|
# Initialize Advoware client
|
||||||
advoware = AdvowareAPI(ctx)
|
advoware = AdvowareAPI(ctx)
|
||||||
|
|
||||||
@@ -43,7 +49,6 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
# Get request body
|
# Get request body
|
||||||
json_data = request.body
|
json_data = request.body
|
||||||
|
|
||||||
ctx.logger.info(f"Proxying PUT request to Advoware: {endpoint}")
|
|
||||||
result = await advoware.api_call(
|
result = await advoware.api_call(
|
||||||
endpoint,
|
endpoint,
|
||||||
method='PUT',
|
method='PUT',
|
||||||
@@ -51,11 +56,17 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
json_data=json_data
|
json_data=json_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Proxy PUT erfolgreich")
|
||||||
return ApiResponse(status=200, body={'result': result})
|
return ApiResponse(status=200, body={'result': result})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Proxy error: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ ADVOWARE PROXY PUT FEHLER")
|
||||||
|
ctx.logger.error(f"Endpoint: {request.query_params.get('endpoint', 'N/A')}")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={'error': 'Internal server error', 'details': str(e)}
|
body={'error': 'Internal server error', 'details': str(e)}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|||||||
@@ -42,16 +42,13 @@ config = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]) -> Optional[Dict[str, Any]]:
|
async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Zentraler Sync-Handler für Beteiligte
|
Zentraler Sync-Handler für Beteiligte
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_data: Event data mit entity_id, action, source
|
event_data: Event data mit entity_id, action, source
|
||||||
ctx: Motia FlowContext
|
ctx: Motia FlowContext
|
||||||
|
|
||||||
Returns:
|
|
||||||
Optional result dict
|
|
||||||
"""
|
"""
|
||||||
entity_id = event_data.get('entity_id')
|
entity_id = event_data.get('entity_id')
|
||||||
action = event_data.get('action')
|
action = event_data.get('action')
|
||||||
@@ -61,11 +58,13 @@ async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]) -> Optional
|
|||||||
|
|
||||||
if not entity_id:
|
if not entity_id:
|
||||||
step_logger.error("Keine entity_id im Event gefunden")
|
step_logger.error("Keine entity_id im Event gefunden")
|
||||||
return None
|
return
|
||||||
|
|
||||||
step_logger.info(
|
step_logger.info("=" * 80)
|
||||||
f"🔄 Sync-Handler gestartet: {action.upper()} | Entity: {entity_id} | Source: {source}"
|
step_logger.info(f"🔄 BETEILIGTE SYNC HANDLER: {action.upper()}")
|
||||||
)
|
step_logger.info("=" * 80)
|
||||||
|
step_logger.info(f"Entity: {entity_id} | Source: {source}")
|
||||||
|
step_logger.info("=" * 80)
|
||||||
|
|
||||||
# Get shared Redis client (centralized)
|
# Get shared Redis client (centralized)
|
||||||
redis_client = get_redis_client(strict=False)
|
redis_client = get_redis_client(strict=False)
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ from motia import FlowContext
|
|||||||
from services.espocrm import EspoCRMAPI
|
from services.espocrm import EspoCRMAPI
|
||||||
from services.document_sync_utils import DocumentSync
|
from services.document_sync_utils import DocumentSync
|
||||||
from services.xai_service import XAIService
|
from services.xai_service import XAIService
|
||||||
|
from services.redis_client import get_redis_client
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import redis
|
|
||||||
import os
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"name": "VMH Document Sync Handler",
|
"name": "VMH Document Sync Handler",
|
||||||
@@ -32,7 +31,7 @@ config = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]):
|
async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]) -> None:
|
||||||
"""Zentraler Sync-Handler für Documents"""
|
"""Zentraler Sync-Handler für Documents"""
|
||||||
entity_id = event_data.get('entity_id')
|
entity_id = event_data.get('entity_id')
|
||||||
entity_type = event_data.get('entity_type', 'CDokumente') # Default: CDokumente
|
entity_type = event_data.get('entity_type', 'CDokumente') # Default: CDokumente
|
||||||
@@ -52,20 +51,11 @@ async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]):
|
|||||||
ctx.logger.info(f"Source: {source}")
|
ctx.logger.info(f"Source: {source}")
|
||||||
ctx.logger.info("=" * 80)
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Shared Redis client for distributed locking
|
# Shared Redis client for distributed locking (centralized factory)
|
||||||
redis_host = os.getenv('REDIS_HOST', 'localhost')
|
redis_client = get_redis_client(strict=False)
|
||||||
redis_port = int(os.getenv('REDIS_PORT', '6379'))
|
|
||||||
redis_db = int(os.getenv('REDIS_DB_ADVOWARE_CACHE', '1'))
|
|
||||||
|
|
||||||
redis_client = redis.Redis(
|
# APIs initialisieren (mit Context für besseres Logging)
|
||||||
host=redis_host,
|
espocrm = EspoCRMAPI(ctx)
|
||||||
port=redis_port,
|
|
||||||
db=redis_db,
|
|
||||||
decode_responses=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# APIs initialisieren
|
|
||||||
espocrm = EspoCRMAPI()
|
|
||||||
sync_utils = DocumentSync(espocrm, redis_client, ctx)
|
sync_utils = DocumentSync(espocrm, redis_client, ctx)
|
||||||
xai_service = XAIService(ctx)
|
xai_service = XAIService(ctx)
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
try:
|
try:
|
||||||
payload = request.body or []
|
payload = request.body or []
|
||||||
|
|
||||||
ctx.logger.info("VMH Webhook Beteiligte Create empfangen")
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("📥 VMH WEBHOOK: BETEILIGTE CREATE")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
ctx.logger.info(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
ctx.logger.info(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Sammle alle IDs aus dem Batch
|
# Sammle alle IDs aus dem Batch
|
||||||
entity_ids = set()
|
entity_ids = set()
|
||||||
@@ -53,7 +56,8 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.logger.info(f"VMH Create Webhook verarbeitet: {len(entity_ids)} Events emittiert")
|
ctx.logger.info("✅ VMH Create Webhook verarbeitet: "
|
||||||
|
f"{len(entity_ids)} Events emittiert")
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=200,
|
status=200,
|
||||||
@@ -65,7 +69,10 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Fehler beim Verarbeiten des VMH Create Webhooks: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ FEHLER: VMH CREATE WEBHOOK")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={
|
body={
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
try:
|
try:
|
||||||
payload = request.body or []
|
payload = request.body or []
|
||||||
|
|
||||||
ctx.logger.info("VMH Webhook Beteiligte Update empfangen")
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("📥 VMH WEBHOOK: BETEILIGTE UPDATE")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
ctx.logger.info(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
ctx.logger.info(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
# Sammle alle IDs aus dem Batch
|
# Sammle alle IDs aus dem Batch
|
||||||
entity_ids = set()
|
entity_ids = set()
|
||||||
@@ -53,7 +56,8 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.logger.info(f"VMH Update Webhook verarbeitet: {len(entity_ids)} Events emittiert")
|
ctx.logger.info("✅ VMH Update Webhook verarbeitet: "
|
||||||
|
f"{len(entity_ids)} Events emittiert")
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=200,
|
status=200,
|
||||||
@@ -65,7 +69,10 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Fehler beim Verarbeiten des VMH Update Webhooks: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ FEHLER: VMH UPDATE WEBHOOK")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
body={
|
body={
|
||||||
|
|||||||
@@ -25,16 +25,21 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
try:
|
try:
|
||||||
payload = request.body or []
|
payload = request.body or []
|
||||||
|
|
||||||
ctx.logger.info("VMH Webhook Document Update empfangen")
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("📥 VMH WEBHOOK: DOCUMENT UPDATE")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
ctx.logger.debug(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
ctx.logger.debug(f"Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
||||||
|
|
||||||
# Sammle alle IDs aus dem Batch
|
# Sammle alle IDs aus dem Batch
|
||||||
entity_ids = set()
|
entity_ids = set()
|
||||||
|
entity_type = 'CDokumente' # Default
|
||||||
|
|
||||||
if isinstance(payload, list):
|
if isinstance(payload, list):
|
||||||
for entity in payload:
|
for entity in payload:
|
||||||
if isinstance(entity, dict) and 'id' in entity:
|
if isinstance(entity, dict) and 'id' in entity:
|
||||||
entity_ids.add(entity['id'])
|
entity_ids.add(entity['id'])
|
||||||
|
# Take entityType from first entity if present
|
||||||
|
if entity_type == 'CDokumente':
|
||||||
entity_type = entity.get('entityType', 'CDokumente')
|
entity_type = entity.get('entityType', 'CDokumente')
|
||||||
elif isinstance(payload, dict) and 'id' in payload:
|
elif isinstance(payload, dict) and 'id' in payload:
|
||||||
entity_ids.add(payload['id'])
|
entity_ids.add(payload['id'])
|
||||||
@@ -48,12 +53,15 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
'topic': 'vmh.document.update',
|
'topic': 'vmh.document.update',
|
||||||
'data': {
|
'data': {
|
||||||
'entity_id': entity_id,
|
'entity_id': entity_id,
|
||||||
'entity_type': entity_type if 'entity_type' in locals() else 'CDokumente',
|
'entity_type': entity_type,
|
||||||
'action': 'update',
|
'action': 'update',
|
||||||
'timestamp': payload[0].get('modifiedAt') if isinstance(payload, list) and payload else None
|
'timestamp': payload[0].get('modifiedAt') if isinstance(payload, list) and payload else None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Document Update Webhook verarbeitet: "
|
||||||
|
f"{len(entity_ids)} Events emittiert")
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=200,
|
status=200,
|
||||||
body={
|
body={
|
||||||
@@ -64,8 +72,11 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.logger.error(f"Fehler im Document Update Webhook: {e}")
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error("❌ FEHLER: DOCUMENT UPDATE WEBHOOK")
|
||||||
|
ctx.logger.error(f"Error: {e}")
|
||||||
ctx.logger.error(f"Payload: {request.body}")
|
ctx.logger.error(f"Payload: {request.body}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=500,
|
status=500,
|
||||||
|
|||||||
Reference in New Issue
Block a user