Merge pull request #121 from aradhyacp/feature/sentry

Feature/sentry
This commit is contained in:
Zeecka
2025-12-30 18:58:11 +01:00
committed by GitHub
4 changed files with 77 additions and 2 deletions
+5
View File
@@ -8,3 +8,8 @@ RQ_DASHBOARD_REDIS_URL=redis://redis:6379/0
POSTGRES_DB=aperisolve POSTGRES_DB=aperisolve
POSTGRES_USER=aperiuser POSTGRES_USER=aperiuser
POSTGRES_PASSWORD=aperipass 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
+39
View File
@@ -9,6 +9,12 @@ from pathlib import Path
from shutil import rmtree from shutil import rmtree
from typing import Any, Optional 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 sentry_sdk.integrations.threading import ThreadingIntegration
from flask import Flask, Response, abort, jsonify, render_template, request, send_file from flask import Flask, Response, abort, jsonify, render_template, request, send_file
from redis import Redis from redis import Redis
from rq import Queue from rq import Queue
@@ -17,6 +23,33 @@ from .cleanup import cleanup_old_entries
from .config import IMAGE_EXTENSIONS, RESULT_FOLDER, WORKER_FILES from .config import IMAGE_EXTENSIONS, RESULT_FOLDER, WORKER_FILES
from .models import Image, Submission, db, init_db 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(),
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%)
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: Flask = Flask(__name__)
app.json.sort_keys = False # type: ignore app.json.sort_keys = False # type: ignore
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI") app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI")
@@ -36,6 +69,11 @@ queue = Queue(connection=redis_conn)
@app.errorhandler(413) @app.errorhandler(413)
def too_large(_: Any) -> tuple[Response, int]: def too_large(_: Any) -> tuple[Response, int]:
"""Error Handler for Max File Size.""" """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 return jsonify({"error": "Image size exceeded"}), 413
@@ -44,6 +82,7 @@ def not_found(_: Any) -> str:
"""Error Handler for 404 not found.""" """Error Handler for 404 not found."""
return render_template("error.html", message="Resource not found", statuscode=404) return render_template("error.html", message="Resource not found", statuscode=404)
# Sentry automatically captures 500 errors
@app.route("/") @app.route("/")
def index() -> str: def index() -> str:
+1
View File
@@ -8,3 +8,4 @@ types-Flask-SQLAlchemy
sqlalchemy-stubs sqlalchemy-stubs
Pillow Pillow
gunicorn gunicorn
sentry-sdk[flask,sqlalchemy,rq]
+31 -1
View File
@@ -1,9 +1,31 @@
"""Asynchronous worker for analyzing image submissions.""" """Asynchronous worker for analyzing image submissions."""
import os
import threading import threading
from pathlib import Path from pathlib import Path
from typing import Any 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.binwalk import analyze_binwalk
from .analyzers.decomposer import analyze_decomposer from .analyzers.decomposer import analyze_decomposer
from .analyzers.exiftool import analyze_exiftool 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 image = Image.query.get_or_404(submission.image_hash) # type: ignore
if not submission or not image: # No submission found if not submission or not image: # No submission found
sentry_sdk.capture_message(
f"Submission or image not found: {submission_hash}",
level="warning"
)
return return
submission.status = "running" # type: ignore submission.status = "running" # type: ignore
@@ -69,6 +95,7 @@ def analyze_image(submission_hash: str) -> None:
analyzer_func(*args) analyzer_func(*args)
except Exception as e: except Exception as e:
print(f"Error in {analyzer_func.__name__}: {e}") print(f"Error in {analyzer_func.__name__}: {e}")
sentry_sdk.capture_exception(e)
analyzers = [ analyzers = [
(analyze_binwalk, img_path, result_path), (analyze_binwalk, img_path, result_path),
@@ -100,7 +127,10 @@ def analyze_image(submission_hash: str) -> None:
thread.join() thread.join()
submission.status = "completed" submission.status = "completed"
except Exception: except Exception as e:
sentry_sdk.capture_exception(e)
submission.status = "error" submission.status = "error"
finally: finally:
db.session.commit() # pylint: disable=no-member 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)