Skip to content

[18.0][ADD] sentry_client: browser-side Sentry SDK for the web client - #3627

Open
dnplkndll wants to merge 1 commit into
OCA:18.0from
ledoent:18.0-add-sentry_client
Open

[18.0][ADD] sentry_client: browser-side Sentry SDK for the web client#3627
dnplkndll wants to merge 1 commit into
OCA:18.0from
ledoent:18.0-add-sentry_client

Conversation

@dnplkndll

@dnplkndll dnplkndll commented May 25, 2026

Copy link
Copy Markdown
Contributor

New module: a browser-side companion to the OCA sentry Python module.
Loads the Sentry browser SDK into the Odoo web client and reports
uncaught JS errors, optional performance traces, session replays on
error, and a user-feedback widget — all toggleable per tier under
Settings → General Settings → Sentry Browser Monitoring.

Browser DSN, environment, and release are settable via the UI
(ir.config_parameter sentry_client.{browser_dsn,environment,release}).
Empty UI values fall back to the sentry_* options in odoo.conf (the
keys the server-side module reads on 18.0) so a
single-project deployment shared with the server-side sentry module
keeps working without UI clicks. Matches Sentry's recommended
one-project-per-platform setup when the values are split.

Lead PR for the module; 19.0 port at
#3622 (DRAFT,
[19.0][MIG] — cherry-pick of this commit plus three API adaptations).

Non-mechanical adaptations worth flagging

  • Single capture path on the backend. Odoo's error service routes
    every window error and unhandled rejection through the
    @web/core/error_handlers registry, so the module registers one entry
    there and leaves the SDK's own capture integrations (GlobalHandlers,
    BrowserApiErrors, TryCatch) out of the defaults whenever that
    service is in the bundle. One event per crash, capturing the wrapping
    error so LinkedErrors expands .cause in place; OWL crashes carry
    the component tree in extra.component_tree. Server-side errors
    (RPCError incl. session expiry, connection lost/aborted, request too
    large) are the server-side sentry module's job: they leave an
    odoo.rpc breadcrumb and flush the buffered session replay instead of
    producing a browser event. Returns false so Odoo's own Oops! dialog
    still renders alongside. Portal/website pages have no error service and
    keep the SDK defaults.
  • Public /sentry_client/config.json endpoint is auth="public" on
    purpose — the loader runs on portal/login pages before auth. Only
    behaviour flags and the numeric uid leave the server; email and name
    are read client-side from window.odoo.session_info (already gated by
    the session cookie). The DSN is served without any legacy :<secret>
    component, whichever source it comes from.
  • Per-user replay opt-out under My Profile → Preferences → Privacy.
    Even with Tier 2 enabled DB-wide, an opted-out user's Replay
    integration never registers, so no DOM observer runs for them.
  • Per-tab UUID + workflow tags. tab_id (sessionStorage-persisted
    UUID), workflow_id (<action_id>|<model>|<view_type>), and the
    surface tag ship on every event so downstream harvesting can split a
    user's parallel windows into distinct streams.
  • surface tag classifies the source of every browser event:
    backend (/odoo, /web), portal (/my), or frontend (public
    website). Lets Sentry queries filter cleanly when both browser and
    Python ship to the same project — environment distinguishes
    Python vs JS, surface distinguishes browser sub-streams.
  • Bundle selection. Sentry's CDN doesn't ship a
    bundle.tracing.feedback.min.js; the controller's _bundle_name
    helper falls back to bundle.tracing.replay.feedback.min.js for that
    combination — extra replay code is inert without the integration
    registered.
  • Vendored SDK under static/lib/sentry/<version>/ with a refresh
    script that SRI-verifies each bundle against Sentry's published
    hashes. Browser loads from the same origin as Odoo — air-gap safe.

Verified on the runboat preview with all four tiers + a test DSN — browser events land tagged by environment (Python vs JS) and surface (backend / portal / frontend) for clean query separation.

AI-assisted (Claude Code); every change reviewed, tested, and owned by the author.

@dnplkndll
dnplkndll marked this pull request as ready for review May 25, 2026 20:08
@dnplkndll
dnplkndll force-pushed the 18.0-add-sentry_client branch from 91d7713 to 0d828a8 Compare May 25, 2026 20:18
@dnplkndll

dnplkndll commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

@LoisRForgeFlow @victoralmau

I think you guys had some contributions on sentry. how does this look? I was actually going to look into if there might be some way to use the data on an instances use to build roles and skills for agents or just keeping process documentation up to date. I created a drop in seer app that runs the lower cost 3.5-flash to keep costs down on error fixes as well. need more use to verify.

@dnplkndll dnplkndll mentioned this pull request May 28, 2026
@dnplkndll dnplkndll changed the title [18.0][ADD] sentry_client: tiered browser SDK with vendoring, profiling, replay opt-out, OWL boundary [18.0][ADD] sentry_client: browser-side Sentry SDK for the web client May 29, 2026
@dnplkndll
dnplkndll force-pushed the 18.0-add-sentry_client branch from 0d828a8 to 3a41da8 Compare June 12, 2026 01:47

@ChristophAbenthungCibex ChristophAbenthungCibex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dnplkndll Thx for this feature. This is really useful. Overall it looks pretty good.
Minor remarks:

  • The fallback to the config file won't work like this in v18. According to the readme of the sentry module it is configured in the options sections (https://github.com/OCA/server-tools/tree/18.0/sentry#example-odoo-configuration). As far as I am aware the sentry section got added at v19.
  • The "Report a bug" systray icon is display incorrect/weird. Furthermore, I'm not sure that the bug is the best icon choice because the debug mode systray icon is exactly the same. Maybe there is a better option? If not fine by me because most user won't be affected.
Image

@dnplkndll

Copy link
Copy Markdown
Contributor Author

@dnplkndll Thx for this feature. This is really useful. Overall it looks pretty good. Minor remarks:

good points. I noticed the same odd positioning on the icon and will try to find a better one to differentiate. I will check the 19.0 issue. I did start it there then thought the ADD on 18. would be better at the time.

@ChristophAbenthungCibex ChristophAbenthungCibex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx for the changes. LGTM 👍

@isikerkan

Copy link
Copy Markdown

@dnplkndll Wow, I was building the same thing on my side to contribute to OCA and didn't check the open PRs first. Nice module, very complete as a separate addon, I hope it gets merged soon.

Two questions on the design, to understand it better, not to argue. You best practice to keep the projects seperate is good and I missed that on my part to read up:

  1. Why a separate web-only addon instead of extending the existing sentry module? I put mine inside sentry (fork: https://github.com/isikerkan/server-tools/tree/18.0/sentry) with a sentry_mode = python | javascript option in odoo.conf: python keeps the module a pure backend integration, javascript additionally injects the browser side, so multi-database servers behave consistently. I see the upside of your approach (standalone, no Python sentry_sdk dependency, own settings UI, vendored bundle for air-gapped setups), so I'm curious whether the split was mainly about the standalone use case. In my case a split of both into two projects would produce error messages on both projects (redundant reporting).
  2. In a shared Python project the Replays tab only appears after switching the project platform to JavaScript. Was that one of the reasons for recommending a separate browser project?

Differences in my implementation that might be useful for yours:

  • I use Sentry's Loader Script (js.sentry-cdn.com/.min.js) instead of a vendored bundle, so SDK version, sampling and replay config are managed in the Sentry UI. I see the SDK source URL can already point at the public CDN bundles. Would a Loader Script source fit your design as a third variant?
  • Backend errors never trigger a replay with on-error sampling, because Odoo shows them in the RPC error dialog without throwing in the browser. I added an opt-in listener on rpcBus RPC:RESPONSE that flushes the replay buffer when a server error comes back. The backend event and the replay are linked through the propagated trace. I'd be glad to contribute that as a follow-up PR on sentry and sentry_client once it's merged.
  • Your roadmap mentions OpenTelemetry hooks for server-side trace correlation. It worked for me without. BrowserTracing already sends sentry-trace/baggage on same-origin XHR and the Python SDK picks it up with propagate_traces. My APM work for sentry covers the server side, so the two modules link end to end as they are.

I installed the branch next to the server-side sentry module (Python mode, shared project) and triggered errors:

  • 5 backend exceptions produced 5 Python events plus 5 browser events (UncaughtPromiseError > RPC_ERROR: Odoo Server Error), so every backend error shows up twice.
  • 1 plain JS throw produced 2 browser events: Error from the global handler and UncaughtClientError from the error_handlers hook. The beforeSend dedupe only catches the unhandledrejection case.

Returning early in sentryHandler for RPCError / ConnectionLostError / SessionExpiredError (those are the server's job, maybe leave a breadcrumb), and marking the error for the onerror path as well, would fix both while keeping the OWL context capture.

Two smaller things from the same test:

  • /sentry_client/config.json returns the odoo.conf sentry_dsn as is. A server DSN with the legacy key:secret@ part therefore becomes public. Stripping the password component (or refusing the fallback when one is present) would be safer.
  • The odoo.groups tag carries the full list of group names, over 600 characters for an admin user, above Sentry's 200-character tag limit, and it is personal data on every event.

I'll keep testing against my project and drop my browser code from the fork if it holds up. Looking forward to your feedback.

@dnplkndll

Copy link
Copy Markdown
Contributor Author

@dnplkndll Wow, I was building the same thing on my side to contribute to OCA and didn't check the open PRs first. Nice module, very complete as a separate addon, I hope it gets merged soon.

if you want to record a review I think we need 2.
Thanks for the detailed feedback. I should have all the bugs. But I am now on 19.0 so will port and then install on my daily use instance to verify everything. on the design then systems are really very different as the python does not have access to what is happening in the UI. so replays and all that need to run in the JS side. I run 2 sentry types I think you have to? because they have different codebases. I will have review again as it has been quite a while since I needed this for any troubleshooting thankfully!

@dnplkndll
dnplkndll force-pushed the 18.0-add-sentry_client branch from f750ec6 to 7c5d4f9 Compare September 8, 2026 21:05
…nfig

Backport of the 19.0 module (OCA#3622) to 18.0. Identical
feature set; only the API surfaces that changed between series are
adapted:

- `user.all_group_ids` (19.0) → `user.groups_id` (18.0). 19.0 split the
  groups field into a stored direct collection plus a computed
  transitive closure; on 18.0 `groups_id` already returns the full set.
- `group.privilege_id.category_id` (19.0) → `group.category_id` (18.0).
  19.0 introduced the privilege layer between groups and categories;
  on 18.0 groups carry the category directly.
- Config-file fallback reads the top-level `sentry_*` options from
  odoo.conf's `[options]` section — the layout the server-side `sentry`
  module actually uses on 18.0 (the dedicated `[sentry]` section only
  exists from 19.0).
- Manifest version `18.0.1.0.0`.

Everything else — Connection UI block (Browser DSN / Environment /
Release) with odoo.conf fallback, four tier toggles with sample-rate
knobs, vendored Sentry SDK under `static/lib`, the `error_handlers`
registry entry as the single backend capture path, per-tab UUID +
`workflow_id` + `surface` tags, per-user replay opt-out, and the public
`/sentry_client/config.json` endpoint — ports verbatim.

The OWL `error_handlers` registry, settings `<block>`/`<setting>`
pattern, and asset bundle keys are identical between 18.0 and 19.0.

The systray feedback launcher is an `o_nav_entry` button with
`fa-bullhorn` (Sentry's own feedback iconography) — deliberately not
`fa-bug`, which is the debug-mode systray icon beside it.

Assisted-by: Claude Fable 5
Assisted-by: Claude Opus 5
@dnplkndll
dnplkndll force-pushed the 18.0-add-sentry_client branch from 7c5d4f9 to bebad7d Compare September 9, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants