Sending events to sentry
Setting up Sentry so it can receive errors is not enough. Every event (capture_message or capture_exception) must carry enough context to answer: who was acting, on which request, in which part of the system?
All enrichment lives in one module: app/third_parties/sentry_event_enricher.rb. Controllers call it progressively — each layer of the request adds what it knows:
| Step | Who calls it | What it adds |
|---|---|---|
request_context | ApplicationController (before_action :sentry_request_context) | contexts request_meta (uuid, ip, user agent, path, method, format — from the Rack request), request_params, request_headers; tag environment |
identities_user_context | user-authenticated controllers after login check | Sentry user (id, email) |
identities_admin_context | Team::AuthenticatedController | context identities_admin (admin id, department, name); tags identity_type, department |
profile_context | employer / talent base controllers after profile lookup | see below |
tag_domain | config.before_send in config/initializers/sentry.rb | tag domain (see Tags) |
Structured Context
The profile context
After a profile is authorized, profile_context(CurrentRequest) records who is acting. For an employer request:
# app/third_parties/sentry_event_enricher.rb (org branch)
scope.set_user(
id: current_request.identities_user.id,
email: current_request.identities_user.email,
actor_class: current_request.org_membership.class.name,
actor_id: current_request.org_membership.id
)
scope.set_context('user_profile', {
profile_type: current_request.org_membership.class.name,
profile_id: current_request.org_membership.id,
org_company_id: current_request.org_company.id
})
scope.set_tags(
profile_type: current_request.org_membership.class.name,
org_company_id: current_request.org_company.id
)
The talent branch does the same shape with a talent_profile context (profile_type, profile_id) and a profile_type tag.
Note: CurrentRequest holds exactly five attributes — identities_user, identities_admin, org_membership, org_company, talent_profile. Request facts (uuid, ip, user agent) are NOT on CurrentRequest; they come from the Rack request in request_context, before authentication.
Request params
The parameters of the failing request reach Sentry through the request_params context, set by request_context in the before_action — automatically, for every request.
Managers also pass the endpoint request object into the error class when raising:
# app/domains/careers/job_applications/candidates_index_manager.rb
if validator.invalid?
raise ::Errors::ValidationError.new(
title: 'There was an error when getting your job applications',
messages: validator.errors.messages,
request: JSON.parse(request.to_json(), symbolize_names: true)
)
end
The request: value is stored on the error object for local debugging. It does not reach Sentry today — whether to wire it in (or stop passing it) is an open decision: jod-app/roadmap#67.
Error level
config.before_send (config/initializers/sentry.rb) downgrades Errors::ValidationError events to :info — a failed validation is a user mistake, not a system fault. It also drops events whose error has skip_sentry set.
Tags
Tags make errors searchable in the Sentry UI.
| tag | example value | how it is set |
|---|---|---|
domain | careers/job_applications | derived from the exception backtrace: the first frame under app/domains/ gives the directory path; fallback Unknown. It tags where the error was raised, not the endpoint's namespace |
environment | production | request_context |
identity_type, department | admin, finance | admin requests |
profile_type | Org::Membership | employer / talent requests |
org_company_id | 42 | employer requests |
Search examples in the Sentry UI: domain:careers/job_applications, org_company_id:42, profile_type:Org::Membership.