Skip to main content

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:

StepWho calls itWhat it adds
request_contextApplicationController (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_contextuser-authenticated controllers after login checkSentry user (id, email)
identities_admin_contextTeam::AuthenticatedControllercontext identities_admin (admin id, department, name); tags identity_type, department
profile_contextemployer / talent base controllers after profile lookupsee below
tag_domainconfig.before_send in config/initializers/sentry.rbtag 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.

tagexample valuehow it is set
domaincareers/job_applicationsderived 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
environmentproductionrequest_context
identity_type, departmentadmin, financeadmin requests
profile_typeOrg::Membershipemployer / talent requests
org_company_id42employer requests

Search examples in the Sentry UI: domain:careers/job_applications, org_company_id:42, profile_type:Org::Membership.