From ffd97c39551ec4033e4005e0ea35e517944af317 Mon Sep 17 00:00:00 2001 From: aradhyacp Date: Tue, 30 Dec 2025 17:34:44 +0530 Subject: [PATCH 1/5] Add Sentry configuration to .env file --- .env | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env b/.env index 7d7e694..4116ee9 100644 --- a/.env +++ b/.env @@ -8,3 +8,8 @@ RQ_DASHBOARD_REDIS_URL=redis://redis:6379/0 POSTGRES_DB=aperisolve POSTGRES_USER=aperiuser POSTGRES_PASSWORD=aperipass +SENTRY_DSN=https://your-dsn@sentry.io/project-id +SENTRY_TRACES_SAMPLE_RATE=0.1 +SENTRY_PROFILES_SAMPLE_RATE=0.1 +SENTRY_RELEASE=1.0.0 +ENVIRONMENT=production \ No newline at end of file From b7d25522f11a20d31eaf4d11d9d18fa625f383f7 Mon Sep 17 00:00:00 2001 From: aradhyacp Date: Tue, 30 Dec 2025 17:35:32 +0530 Subject: [PATCH 2/5] Integrate Sentry for error tracking and performance monitoring --- aperisolve/app.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/aperisolve/app.py b/aperisolve/app.py index 75359d2..5b0ad4d 100644 --- a/aperisolve/app.py +++ b/aperisolve/app.py @@ -8,6 +8,11 @@ from datetime import datetime, timezone from pathlib import Path from typing import Any, Optional +import sentry_sdk +from sentry_sdk.integrations.flask import FlaskIntegration +from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration +from sentry_sdk.integrations.rq import RqIntegration + from flask import Flask, Response, abort, jsonify, render_template, request, send_file from redis import Redis from rq import Queue @@ -16,6 +21,32 @@ from .cleanup import cleanup_old_entries from .config import IMAGE_EXTENSIONS, RESULT_FOLDER, WORKER_FILES from .models import Image, Submission, db, init_db +SENTRY_DSN = os.environ.get("SENTRY_DSN") +ENVIRONMENT = os.environ.get("ENVIRONMENT", "development") + +if SENTRY_DSN: + sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=[ + FlaskIntegration(), + SqlalchemyIntegration(), + RqIntegration(), + ], + # Set traces_sample_rate to 1.0 to capture 100% of transactions for performance monitoring. + # Adjust this value in production (e.g., 0.1 for 10%) + traces_sample_rate=float(os.environ.get("SENTRY_TRACES_SAMPLE_RATE", "0.1")), + # Set profiles_sample_rate to 1.0 to profile 100% of sampled transactions. + profiles_sample_rate=float(os.environ.get("SENTRY_PROFILES_SAMPLE_RATE", "0.1")), + # Environment tag (production, staging, development) + environment=ENVIRONMENT, + # Release version (use git commit hash or version number) + release=os.environ.get("SENTRY_RELEASE", "1.0.0"), + # Send default PII (user IPs, etc.) - set to False for privacy + send_default_pii=False, + # Enable automatic breadcrumb tracking + enable_tracing=True, + ) + app: Flask = Flask(__name__) app.json.sort_keys = False # type: ignore app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI") @@ -35,6 +66,11 @@ queue = Queue(connection=redis_conn) @app.errorhandler(413) def too_large(_: Any) -> tuple[Response, int]: """Error Handler for Max File Size.""" + sentry_sdk.set_context("upload", { + "content_length": request.content_length, + "max_allowed": app.config["MAX_CONTENT_LENGTH"], + }) + sentry_sdk.capture_message("Upload failed: 413 Payload Too Large", level="warning") return jsonify({"error": "Image size exceeded"}), 413 @@ -43,6 +79,7 @@ def not_found(_: Any) -> str: """Error Handler for 404 not found.""" return render_template("error.html", message="Resource not found", statuscode=404) +# Sentry automatically captures 500 errors @app.route("/") def index() -> str: From 149d80fb473851010f0397f52d385f0f81edbdbe Mon Sep 17 00:00:00 2001 From: aradhyacp Date: Tue, 30 Dec 2025 17:35:44 +0530 Subject: [PATCH 3/5] Enhance Sentry integration in workers for improved error tracking and performance monitoring --- aperisolve/workers.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/aperisolve/workers.py b/aperisolve/workers.py index c5be8c7..d577fa2 100644 --- a/aperisolve/workers.py +++ b/aperisolve/workers.py @@ -1,9 +1,31 @@ """Asynchronous worker for analyzing image submissions.""" +import os import threading from pathlib import Path from typing import Any +import sentry_sdk +from sentry_sdk.integrations.rq import RqIntegration +from sentry_sdk.integrations.threading import ThreadingIntegration +from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration + +# Initialize Sentry for workers (separate process from Flask app) +SENTRY_DSN = os.environ.get("SENTRY_DSN") +if SENTRY_DSN: + sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=[ + RqIntegration(), + ThreadingIntegration(propagate_hub=True), # Capture exceptions in threads + SqlalchemyIntegration(), + ], + traces_sample_rate=float(os.environ.get("SENTRY_TRACES_SAMPLE_RATE", "0.1")), + environment=os.environ.get("ENVIRONMENT", "development"), + release=os.environ.get("SENTRY_RELEASE", "1.0.0"), + send_default_pii=False, + ) + from .analyzers.binwalk import analyze_binwalk from .analyzers.decomposer import analyze_decomposer from .analyzers.exiftool import analyze_exiftool @@ -51,6 +73,10 @@ def analyze_image(submission_hash: str) -> None: image = Image.query.get_or_404(submission.image_hash) # type: ignore if not submission or not image: # No submission found + sentry_sdk.capture_message( + f"Submission or image not found: {submission_hash}", + level="warning" + ) return submission.status = "running" # type: ignore @@ -69,6 +95,7 @@ def analyze_image(submission_hash: str) -> None: analyzer_func(*args) except Exception as e: print(f"Error in {analyzer_func.__name__}: {e}") + sentry_sdk.capture_exception(e) analyzers = [ (analyze_binwalk, img_path, result_path), @@ -100,7 +127,10 @@ def analyze_image(submission_hash: str) -> None: thread.join() submission.status = "completed" - except Exception: + except Exception as e: + sentry_sdk.capture_exception(e) submission.status = "error" finally: db.session.commit() # pylint: disable=no-member + # Flush Sentry events - wait up to 5 seconds for events to be sent + sentry_sdk.flush(timeout=5) From 9d693b89125badb3b4faaf8e989b7887513f7ed0 Mon Sep 17 00:00:00 2001 From: aradhyacp Date: Tue, 30 Dec 2025 17:35:50 +0530 Subject: [PATCH 4/5] Add Sentry SDK to requirements for enhanced error tracking --- aperisolve/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aperisolve/requirements.txt b/aperisolve/requirements.txt index 14446ca..f7ee61b 100644 --- a/aperisolve/requirements.txt +++ b/aperisolve/requirements.txt @@ -7,4 +7,5 @@ psycopg2-binary types-Flask-SQLAlchemy sqlalchemy-stubs Pillow -gunicorn \ No newline at end of file +gunicorn +sentry-sdk[flask,sqlalchemy,rq] \ No newline at end of file From ff61763e577d717f1dcc36483a30abdbf90dc51a Mon Sep 17 00:00:00 2001 From: aradhyacp Date: Tue, 30 Dec 2025 17:39:18 +0530 Subject: [PATCH 5/5] Add Threading integration to Sentry for improved error tracking --- aperisolve/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aperisolve/app.py b/aperisolve/app.py index 5b0ad4d..663ae07 100644 --- a/aperisolve/app.py +++ b/aperisolve/app.py @@ -12,6 +12,7 @@ import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sentry_sdk.integrations.rq import RqIntegration +from sentry_sdk.integrations.threading import ThreadingIntegration from flask import Flask, Response, abort, jsonify, render_template, request, send_file from redis import Redis @@ -31,6 +32,7 @@ if SENTRY_DSN: FlaskIntegration(), SqlalchemyIntegration(), RqIntegration(), + ThreadingIntegration(propagate_hub=True), ], # Set traces_sample_rate to 1.0 to capture 100% of transactions for performance monitoring. # Adjust this value in production (e.g., 0.1 for 10%)