diff --git a/emperor.ini b/emperor.ini
index c5c3810a1..3eeac3a53 100644
--- a/emperor.ini
+++ b/emperor.ini
@@ -3,6 +3,7 @@ strict-mode = true
protocol = uwsgi
master = true
enable-threads = true
+lazy-apps = true
emperor = ./vassals/*/*.ini
# See https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#passing-configuration-parameters-to-all-vassals
@@ -13,3 +14,5 @@ emperor-on-demand-extension = .socket
fastrouter = 127.0.0.1:8001
fastrouter-subscription-server = /tmp/sock2
vacuum = true
+
+log-format = %(host) - %(addr) - %(user) [%(ltime)] "%(method) %(uri) %(proto)" %(status) %(size) "%(referer)" "%(uagent)"
diff --git a/entrypoint.sh b/entrypoint.sh
index bcc1b78d3..a519b31d5 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -29,5 +29,5 @@ flask db upgrade
flask initdb
-exec uwsgi --http :5000 --workers 1 --threads 2 --wsgi-file subscribie.wsgi --touch-chain-reload subscribie.wsgi --chdir /usr/src/app/subscribie/
+exec uwsgi --http :5000 --workers 1 --threads 2 --py-call-uwsgi-fork-hooks --wsgi-file subscribie.wsgi --touch-chain-reload subscribie.wsgi --chdir /usr/src/app/subscribie/
diff --git a/pyproject.toml b/pyproject.toml
index 72bd07b57..f3dfecf61 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,6 +39,7 @@ dependencies = [
"scikit-learn",
"black",
"strictyaml",
+ "sentry-sdk[flask]>=2.14.0",
]
readme = "README.md"
requires-python = ">= 3.12"
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 3d15330bc..3615bbdb0 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -22,9 +22,11 @@ black==24.4.2
blinker==1.8.2
# via flask
# via flask-mail
+ # via sentry-sdk
# via subscribie
certifi==2024.2.2
# via requests
+ # via sentry-sdk
cffi==1.16.0
# via cryptography
charset-normalizer==3.3.2
@@ -50,6 +52,7 @@ flask==3.0.3
# via flask-reuploaded
# via flask-sqlalchemy
# via flask-wtf
+ # via sentry-sdk
# via subscribie
flask-babel==4.0.0
# via subscribie
@@ -96,6 +99,7 @@ mako==1.3.5
markupsafe==2.1.5
# via jinja2
# via mako
+ # via sentry-sdk
# via werkzeug
# via wtforms
mypy-extensions==1.0.0
@@ -148,6 +152,8 @@ scikit-learn==1.5.0
scipy==1.13.1
# via scikit-learn
# via subscribie
+sentry-sdk==2.14.0
+ # via subscribie
six==1.16.0
# via python-dateutil
smmap==5.0.1
@@ -172,6 +178,7 @@ tzdata==2024.1
# via pandas
urllib3==2.2.1
# via requests
+ # via sentry-sdk
werkzeug==3.0.3
# via flask
wheel==0.43.0
diff --git a/requirements.lock b/requirements.lock
index 3d15330bc..3615bbdb0 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -22,9 +22,11 @@ black==24.4.2
blinker==1.8.2
# via flask
# via flask-mail
+ # via sentry-sdk
# via subscribie
certifi==2024.2.2
# via requests
+ # via sentry-sdk
cffi==1.16.0
# via cryptography
charset-normalizer==3.3.2
@@ -50,6 +52,7 @@ flask==3.0.3
# via flask-reuploaded
# via flask-sqlalchemy
# via flask-wtf
+ # via sentry-sdk
# via subscribie
flask-babel==4.0.0
# via subscribie
@@ -96,6 +99,7 @@ mako==1.3.5
markupsafe==2.1.5
# via jinja2
# via mako
+ # via sentry-sdk
# via werkzeug
# via wtforms
mypy-extensions==1.0.0
@@ -148,6 +152,8 @@ scikit-learn==1.5.0
scipy==1.13.1
# via scikit-learn
# via subscribie
+sentry-sdk==2.14.0
+ # via subscribie
six==1.16.0
# via python-dateutil
smmap==5.0.1
@@ -172,6 +178,7 @@ tzdata==2024.1
# via pandas
urllib3==2.2.1
# via requests
+ # via sentry-sdk
werkzeug==3.0.3
# via flask
wheel==0.43.0
diff --git a/settings.yaml.example b/settings.yaml.example
index 2f418867f..d353ad9db 100644
--- a/settings.yaml.example
+++ b/settings.yaml.example
@@ -6,6 +6,10 @@ FLASK_ENV: development
# SESSION_COOKIE_HTTPONLY: True
# SESSION_COOKIE_SAMESITE: None
+# Sentry_sdk_dsn
+SENTRY_SDK_DSN: "https://changeme@changeme.ingest.de.sentry.io/123456789"
+SENTRY_SDK_SESSION_REPLAY_ID: "changeme"
+
# Software as a service (SAAS)
SAAS_URL: https://subscribie.co.uk/
# SAAS_API_KEY is to allow subscribie platform to send authenticated
diff --git a/subscribie/__init__.py b/subscribie/__init__.py
index 51d8ac647..e2b3cb57b 100644
--- a/subscribie/__init__.py
+++ b/subscribie/__init__.py
@@ -7,6 +7,7 @@
:copyright: (c) 2018 by Karma Computing Ltd
"""
+import sentry_sdk
from subscribie.settings import settings
from .logger import logger # noqa: F401
import logging
@@ -25,6 +26,17 @@
request,
abort,
)
+
+sentry_sdk.init(
+ dsn=settings.get("SENTRY_SDK_DSN"),
+ # Set traces_sample_rate to 1.0 to capture 100%
+ # of transactions for tracing.
+ traces_sample_rate=1.0,
+ # Set profiles_sample_rate to 1.0 to profile 100%
+ # of sampled transactions.
+ # We recommend adjusting this value in production.
+ profiles_sample_rate=1.0,
+)
from flask_babel import Babel, _
from subscribie.email import EmailMessageQueue
from .Template import load_theme
@@ -378,4 +390,14 @@ def alert_subscriber_update_choices(subscriber: Person):
def test_language():
return _("Hello")
+ @app.route("/error")
+ def raise_error():
+ 1 / 0 # raises an error
+ return ""
+
+ @app.route("/ui-error")
+ def raise_ui_error():
+ """Raises UI error"""
+ return render_template("errors/ui-error.html"), 200
+
return app
diff --git a/subscribie/blueprints/admin/templates/admin/layout.html b/subscribie/blueprints/admin/templates/admin/layout.html
index ce890ee8f..42eccf2e4 100644
--- a/subscribie/blueprints/admin/templates/admin/layout.html
+++ b/subscribie/blueprints/admin/templates/admin/layout.html
@@ -8,6 +8,7 @@
+
{% if integration.google_tag_manager_container_id
and integration.google_tag_manager_active
diff --git a/subscribie/settings.py b/subscribie/settings.py
index 5870a8f2c..512bf788a 100644
--- a/subscribie/settings.py
+++ b/subscribie/settings.py
@@ -1,4 +1,5 @@
from strictyaml import load, Map, Email, Str, Url, Int, Bool, Regex, CommaSeparated
+import os
# Load application settings according to schema
@@ -7,6 +8,8 @@
schema = Map(
{
"FLASK_ENV": Str(),
+ "SENTRY_SDK_DSN": Str(),
+ "SENTRY_SDK_SESSION_REPLAY_ID": Str(),
"SAAS_URL": Url(),
"SAAS_API_KEY": Str(),
"SAAS_ACTIVATE_ACCOUNT_PATH": Str(),
@@ -57,6 +60,10 @@ def load_settings():
with open("settings.yaml") as fp:
settings_string = fp.read()
settings = load(settings_string, schema)
+ for key in schema._required_keys:
+ if key in os.environ:
+ print(f"Overriding setting {key} with environ value: {os.getenv(key)}")
+ settings[key] = os.getenv(key)
return settings
diff --git a/subscribie/themes/theme-jesmond/jesmond/errors/ui-error.html b/subscribie/themes/theme-jesmond/jesmond/errors/ui-error.html
new file mode 100644
index 000000000..15859d5ca
--- /dev/null
+++ b/subscribie/themes/theme-jesmond/jesmond/errors/ui-error.html
@@ -0,0 +1,10 @@
+{% extends "layout.html" %}
+
+{% block body %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/subscribie/themes/theme-jesmond/jesmond/layout.html b/subscribie/themes/theme-jesmond/jesmond/layout.html
index 2437ce134..32cfe9554 100644
--- a/subscribie/themes/theme-jesmond/jesmond/layout.html
+++ b/subscribie/themes/theme-jesmond/jesmond/layout.html
@@ -7,6 +7,7 @@
+
{% if custom_code != "None" %}
diff --git a/subscribie/views.py b/subscribie/views.py
index eafa883f5..e08aae2ad 100644
--- a/subscribie/views.py
+++ b/subscribie/views.py
@@ -150,12 +150,14 @@ def check_if_inside_iframe():
@bp.app_context_processor
def inject_template_globals():
+ from subscribie.settings import settings as internal_settings
company = Company.query.first()
integration = Integration.query.first()
plans = Plan.query.filter_by(archived=0)
pages = Page.query.all()
settings = Setting.query.first()
custom_code = settings.custom_code
+ SENTRY_SDK_SESSION_REPLAY_ID = internal_settings.get("SENTRY_SDK_SESSION_REPLAY_ID")
geo_currency_symbol = get_geo_currency_symbol()
default_currency_symbol = get_shop_default_currency_symbol()
currency_format = currencyFormat
@@ -166,6 +168,7 @@ def inject_template_globals():
plans=plans,
pages=pages,
custom_code=Markup(custom_code),
+ SENTRY_SDK_SESSION_REPLAY_ID=SENTRY_SDK_SESSION_REPLAY_ID,
geo_currency_symbol=geo_currency_symbol,
get_geo_currency_code=get_geo_currency_code,
default_currency_symbol=default_currency_symbol,