Skip to content

Commit bcfd2a5

Browse files
sentry added
1 parent a5b1245 commit bcfd2a5

4 files changed

Lines changed: 564 additions & 2 deletions

File tree

modelq/app/base.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ def get_system_info() -> Dict[str, Any]:
7777
from modelq.exceptions import TaskProcessingError, TaskTimeoutError,RetryTaskException
7878
from modelq.app.middleware import Middleware
7979
from modelq.app.redis_retry import _RedisWithRetry
80+
from modelq.app.sentry import (
81+
is_sentry_available,
82+
init_sentry,
83+
capture_task_exception,
84+
add_breadcrumb,
85+
flush_sentry,
86+
)
8087

8188
from pydantic import BaseModel, ValidationError
8289
from typing import Optional, Dict, Any, Type
@@ -120,6 +127,14 @@ def __init__(
120127
redis_retry_backoff: float = 2.0,
121128
redis_retry_jitter: float = 0.3,
122129
inactive_if_worker_boot_fail: bool = False, # Mark worker as unhealthy if before_worker_boot fails
130+
# Sentry integration (optional)
131+
sentry_dsn: Optional[str] = None,
132+
sentry_traces_sample_rate: float = 0.0,
133+
sentry_profiles_sample_rate: float = 0.0,
134+
sentry_environment: Optional[str] = None,
135+
sentry_release: Optional[str] = None,
136+
sentry_send_default_pii: bool = False,
137+
sentry_debug: bool = False,
123138
**kwargs,
124139
):
125140
if redis_client:
@@ -159,6 +174,22 @@ def __init__(
159174
self.inactive_if_worker_boot_fail = inactive_if_worker_boot_fail
160175
self.worker_healthy = True # Track worker health status
161176

177+
# Initialize Sentry if DSN is provided (optional)
178+
self.sentry_enabled = False
179+
if sentry_dsn:
180+
self.sentry_enabled = init_sentry(
181+
dsn=sentry_dsn,
182+
traces_sample_rate=sentry_traces_sample_rate,
183+
profiles_sample_rate=sentry_profiles_sample_rate,
184+
environment=sentry_environment,
185+
release=sentry_release,
186+
server_name=self.server_id,
187+
send_default_pii=sentry_send_default_pii,
188+
debug=sentry_debug,
189+
)
190+
if self.sentry_enabled:
191+
logger.info("Sentry integration enabled for ModelQ")
192+
162193
# Register this server in Redis (with an initial heartbeat)
163194
self.register_server()
164195

@@ -664,6 +695,16 @@ def worker_loop(worker_id):
664695
if task.task_name in self.allowed_tasks:
665696
try:
666697
logger.info(f"Worker {worker_id} started processing: {task.task_name}")
698+
699+
# Add Sentry breadcrumb for task processing
700+
if self.sentry_enabled:
701+
add_breadcrumb(
702+
message=f"Processing task: {task.task_name}",
703+
category="task",
704+
level="info",
705+
data={"task_id": task.task_id, "worker_id": worker_id},
706+
)
707+
667708
start_time = time.time()
668709
self.process_task(task)
669710
end_time = time.time()
@@ -704,6 +745,9 @@ def worker_loop(worker_id):
704745
)
705746
finally:
706747
self.check_middleware("before_worker_shutdown")
748+
# Flush Sentry events before worker shutdown
749+
if self.sentry_enabled:
750+
flush_sentry(timeout=2.0)
707751
self.check_middleware("after_worker_shutdown")
708752

709753
for i in range(no_of_workers):
@@ -870,6 +914,26 @@ def process_task(self, task: Task) -> None:
870914

871915
# 2) Webhook (if configured)
872916
self.post_error_to_webhook(task, e)
917+
918+
# 3) Sentry (if enabled) - capture with full traceback
919+
if self.sentry_enabled:
920+
import sys
921+
event_id = capture_task_exception(
922+
exc=e,
923+
task_id=task.task_id,
924+
task_name=task.task_name,
925+
payload=task.payload,
926+
worker_id=self.server_id,
927+
additional_context={
928+
"created_at": task.created_at,
929+
"started_at": task.started_at,
930+
"additional_params": task.additional_params,
931+
},
932+
exc_info=sys.exc_info(), # Pass full traceback with line numbers
933+
)
934+
if event_id:
935+
logger.info(f"Error reported to Sentry: {event_id}")
936+
873937
logger.error(f"Task {task.task_name} failed with error: {e}")
874938
raise TaskProcessingError(task.task_name, str(e))
875939

0 commit comments

Comments
 (0)