diff --git a/.env.demo b/.env.demo index 6920b998..23c0c964 100644 --- a/.env.demo +++ b/.env.demo @@ -636,9 +636,26 @@ EMBEDDING_MAX_TOKENS=8000 # Profiling is only active when SENTRY_TRACES_SAMPLE_RATE > 0. Default: 0.0 (disabled). # SENTRY_PROFILES_SAMPLE_RATE=0.0 # -# Attach PII (IP addresses, user agents) to Sentry events. -# Disable (default) to stay GDPR/CCPA compliant. -# SENTRY_SEND_DEFAULT_PII=false +# Attach PII (IP addresses, user agents) to Sentry events. +# Disable (default) to stay GDPR/CCPA compliant. +# SENTRY_SEND_DEFAULT_PII=false +# +# --- Browser (JavaScript) SDK --- +# The same DSN is reused for the Sentry Browser SDK which is injected into +# every rendered page. The DSN is a *public* key and is intentionally +# embedded in client-side code. +# +# Fraction of browser navigations captured for client-side performance tracing. +# 0.0 (default) disables browser tracing; 1.0 captures every navigation. +# SENTRY_JS_TRACES_SAMPLE_RATE=0.0 +# +# Fraction of browser sessions recorded by Sentry Session Replay. +# 0.0 (default) disables session recording; 1.0 records every session. +# SENTRY_JS_REPLAY_SESSION_SAMPLE_RATE=0.0 +# +# Fraction of error sessions recorded by Sentry Session Replay. +# Defaults to 0.1 (10 %) so errors are captured with replay context. +# SENTRY_JS_REPLAY_ON_ERROR_SAMPLE_RATE=0.1 # **Mobile App – Push Notifications** # Push notifications are delivered via Expo's push notification service diff --git a/app/config.py b/app/config.py index 99c12757..cb5c207a 100644 --- a/app/config.py +++ b/app/config.py @@ -1351,6 +1351,40 @@ class Settings(BaseSettings): ), ) + # --------------------------------------------------------------------------- + # Observability – Sentry Browser JavaScript SDK (client-side) + # --------------------------------------------------------------------------- + # The same SENTRY_DSN is reused for the browser SDK. The DSN is a *public* + # key in Sentry's model and is intentionally embedded in client-side code. + # All three settings below default to 0.0 / disabled so that operators opt-in + # to the level of browser monitoring they want. + # --------------------------------------------------------------------------- + sentry_js_traces_sample_rate: float = Field( + default=0.0, + description=( + "Fraction of browser page-loads captured for client-side performance tracing " + "(0.0 – 1.0). 0.0 disables browser tracing; 1.0 captures every navigation. " + "Only active when SENTRY_DSN is set." + ), + ) + sentry_js_replay_session_sample_rate: float = Field( + default=0.0, + description=( + "Fraction of sessions recorded by Sentry Session Replay (0.0 – 1.0). " + "0.0 disables session recording; 1.0 records every session. " + "Only active when SENTRY_DSN is set." + ), + ) + sentry_js_replay_on_error_sample_rate: float = Field( + default=0.1, + description=( + "Fraction of sessions with an error that will be recorded by Sentry Session " + "Replay (0.0 – 1.0). Defaults to 0.1 (10 %) so that errors are captured " + "with replay context even when session-level recording is disabled. " + "Only active when SENTRY_DSN is set." + ), + ) + @model_validator(mode="before") @classmethod def strip_outer_quotes(cls, data: Any) -> Any: diff --git a/app/utils/settings_service.py b/app/utils/settings_service.py index 9388bf97..600597db 100644 --- a/app/utils/settings_service.py +++ b/app/utils/settings_service.py @@ -3196,6 +3196,43 @@ SETTING_METADATA = { "required": False, "restart_required": True, }, + "sentry_js_traces_sample_rate": { + "category": "Observability", + "description": ( + "Fraction of browser page-loads captured for client-side Sentry performance tracing (0.0–1.0). " + "0.0 (default) disables browser tracing; 1.0 captures every navigation. " + "Only active when SENTRY_DSN is set." + ), + "type": "float", + "sensitive": False, + "required": False, + "restart_required": True, + }, + "sentry_js_replay_session_sample_rate": { + "category": "Observability", + "description": ( + "Fraction of sessions recorded by Sentry Session Replay (0.0–1.0). " + "0.0 (default) disables session recording; 1.0 records every session. " + "Only active when SENTRY_DSN is set." + ), + "type": "float", + "sensitive": False, + "required": False, + "restart_required": True, + }, + "sentry_js_replay_on_error_sample_rate": { + "category": "Observability", + "description": ( + "Fraction of error sessions recorded by Sentry Session Replay (0.0–1.0). " + "Defaults to 0.1 (10%) so that errors are captured with replay context " + "even when session-level recording is disabled. " + "Only active when SENTRY_DSN is set." + ), + "type": "float", + "sensitive": False, + "required": False, + "restart_required": True, + }, } diff --git a/app/views/base.py b/app/views/base.py index 0c00b551..6a116733 100644 --- a/app/views/base.py +++ b/app/views/base.py @@ -96,6 +96,21 @@ def _inject_global_context(ctx: dict) -> None: ) ctx.setdefault("enable_factory_reset", getattr(settings, "enable_factory_reset", False)) + # Sentry Browser SDK config (injected into every page so the JS SDK can initialise) + # Normalize empty-string DSN to None so the {% if sentry_dsn %} template guard works correctly. + _raw_dsn = getattr(settings, "sentry_dsn", None) + ctx.setdefault("sentry_dsn", _raw_dsn if _raw_dsn else None) + ctx.setdefault("sentry_environment", getattr(settings, "sentry_environment", "production")) + ctx.setdefault("sentry_js_traces_sample_rate", getattr(settings, "sentry_js_traces_sample_rate", 0.0)) + ctx.setdefault( + "sentry_js_replay_session_sample_rate", + getattr(settings, "sentry_js_replay_session_sample_rate", 0.0), + ) + ctx.setdefault( + "sentry_js_replay_on_error_sample_rate", + getattr(settings, "sentry_js_replay_on_error_sample_rate", 0.1), + ) + req = ctx.get("request") if req is not None: # CSRF token diff --git a/docs/ConfigurationGuide.md b/docs/ConfigurationGuide.md index 0e25a3c0..5dc47650 100644 --- a/docs/ConfigurationGuide.md +++ b/docs/ConfigurationGuide.md @@ -1605,6 +1605,8 @@ No additional configuration is required — the auto-fill uses the authenticated DocuElevate integrates with [Sentry](https://sentry.io) for real-time error tracking and performance monitoring. See [SentrySetup.md](./SentrySetup.md) for a full setup guide. +### Server-side (Python SDK) + | Variable | Description | Default | |---|---|---| | `SENTRY_DSN` | Sentry DSN URL. When set, error reporting and performance tracing are enabled automatically. Leave blank to disable. | *(unset)* | @@ -1613,18 +1615,33 @@ DocuElevate integrates with [Sentry](https://sentry.io) for real-time error trac | `SENTRY_PROFILES_SAMPLE_RATE` | Fraction of profiled transactions sent to Sentry (0.0 – 1.0). Only active when traces > 0. | `0.0` | | `SENTRY_SEND_DEFAULT_PII` | Attach PII (IP addresses, user agents) to Sentry events. Disabled by default for GDPR/CCPA compliance. | `false` | +### Browser SDK (JavaScript) + +The Sentry Browser SDK is loaded automatically on every rendered page when `SENTRY_DSN` is set. The same DSN is used for both server and browser — the DSN is a *public* key in Sentry's security model and is intentionally embedded in client-side code. + +| Variable | Description | Default | +|---|---|---| +| `SENTRY_JS_TRACES_SAMPLE_RATE` | Fraction of browser page-loads captured for client-side performance tracing (0.0 – 1.0). | `0.0` | +| `SENTRY_JS_REPLAY_SESSION_SAMPLE_RATE` | Fraction of sessions recorded by [Sentry Session Replay](https://docs.sentry.io/product/session-replay/) (0.0 – 1.0). | `0.0` | +| `SENTRY_JS_REPLAY_ON_ERROR_SAMPLE_RATE` | Fraction of error sessions captured with session replay context (0.0 – 1.0). | `0.1` | + ```bash -# Minimal example +# Minimal example (server + browser) SENTRY_DSN=https://@o.ingest.sentry.io/ SENTRY_ENVIRONMENT=production -# Optional tuning +# Optional server-side tuning SENTRY_TRACES_SAMPLE_RATE=0.1 SENTRY_PROFILES_SAMPLE_RATE=0.0 SENTRY_SEND_DEFAULT_PII=false + +# Optional browser-side tuning +SENTRY_JS_TRACES_SAMPLE_RATE=0.1 +SENTRY_JS_REPLAY_SESSION_SAMPLE_RATE=0.0 +SENTRY_JS_REPLAY_ON_ERROR_SAMPLE_RATE=0.1 ``` -> **Note:** Sentry is completely opt-in — if `SENTRY_DSN` is not set, the SDK is never initialised and no data leaves your infrastructure. +> **Note:** Sentry is completely opt-in — if `SENTRY_DSN` is not set, neither SDK is initialised and no data leaves your infrastructure. ## Duplicate Document Detection diff --git a/docs/SentrySetup.md b/docs/SentrySetup.md index 357345ac..45433c02 100644 --- a/docs/SentrySetup.md +++ b/docs/SentrySetup.md @@ -2,13 +2,13 @@ DocuElevate ships with first-class support for [Sentry](https://sentry.io) — an open-source observability platform that provides real-time error tracking and performance monitoring. -When a **Sentry DSN** is configured, every unhandled exception in the FastAPI web process and Celery worker is automatically captured and sent to your Sentry project. Performance transactions (request traces, database queries, background task durations) are also recorded, giving you end-to-end visibility into your deployment. +When a **Sentry DSN** is configured, every unhandled exception in the FastAPI web process and Celery worker is automatically captured and sent to your Sentry project. The **Sentry Browser SDK** is also injected into every rendered page, capturing client-side JavaScript errors, browser performance transactions, and (optionally) session replays. Together these give you full-stack, end-to-end visibility into your deployment. --- ## Quick Start -1. **Create a Sentry project** at (or your self-hosted Sentry instance). Choose the **Python** platform. +1. **Create a Sentry project** at (or your self-hosted Sentry instance). Choose the **Python** platform (the same project and DSN are used for both the server and browser SDKs). 2. Copy the **DSN** from *Project → Settings → Client Keys (DSN)*. It looks like: ``` https://@o.ingest.sentry.io/ @@ -17,12 +17,14 @@ When a **Sentry DSN** is configured, every unhandled exception in the FastAPI we ```bash SENTRY_DSN=https://@o.ingest.sentry.io/ ``` -4. Restart DocuElevate. Sentry initialises automatically on startup — you will see a log line confirming activation. +4. Restart DocuElevate. Sentry initialises automatically on startup — you will see a log line confirming activation. The Sentry Browser SDK ` + + {% endif %}