Module: CloseYourIt::Rails::ActiveJobExtension
- Defined in:
- lib/closeyourit/rails/active_job_extension.rb
Overview
Incluso in ActiveJob::Base (via railtie on_load(:active_job)): cattura gli errori dei job
(oggi persi) con il contesto del job. La logica vive in .monitor/.report_discarded per essere
testabile senza ActiveSupport/ActiveJob.
CYRB-19: around_perform gira DENTRO perform_now, mentre retry_on/discard_on (rescue_from)
sono valutati FUORI, dopo i callback. Catturare nell'around_perform significa quindi segnalare
OGNI tentativo — anche quelli che verranno ritentati con successo — perché il reporter vede
l'errore prima che il retry possa zittirlo, e ogni tentativo solleva una nuova istanza (la
deduplica interna, per-istanza, non interviene). Su ActiveJob 7.1+ deleghiamo la segnalazione ad
after_discard, che Rails invoca al fallimento definitivo (retry_on esauriti o eccezione non
gestita), mai sui tentativi che retry_on ritenta → una sola occorrenza per job.
Limiti noti (per costruzione di ActiveJob, non del client):
after_discardNON scatta per gli errori intercettati da unrescue_fromcustom: sono gestiti dall'app, quindi non li segnaliamo più come "non gestiti" (prima lo facevamo, impropriamente).- I retry a livello di ADAPTER (es. Sidekiq) SENZA
retry_onrisollevano l'errore non gestito:after_discardscatta a ogni esecuzione, quindi lì la de-duplicazione per tentativo non si applica (comportamento invariato rispetto a prima). Sulle versioni prive diafter_discardrestiamo al fallback legacy (cattura nell'around_perform).
Constant Summary collapse
- STASHED_SCOPE_IVAR =
Scope arricchito durante il
performtramandato adafter_discard. Legato all'ISTANZA del job (ogni retry ne crea una nuova, deserializzata) → vive esattamente quanto serve, niente bleed tra job o thread; il reset di fineperformlo sgancia solo dallo storage, non muta l'oggetto. :@__closeyourit_stashed_scope
Class Method Summary collapse
- .apply_job_scope(job) ⇒ Object
- .included(base) ⇒ Object
-
.monitor(job) ⇒ Object
Esegue il job arricchendo lo scope con tag/context; resetta lo scope a fine job (no bleed tra job sullo stesso thread).
-
.report_discarded(job, exception) ⇒ Object
Aggancio
after_discard(ActiveJob 7.1+): il job è definitivamente fallito, quindi segnaliamo l'errore UNA sola volta (handled:false). -
.report_on_discard?(job) ⇒ Boolean
true quando ActiveJob espone
after_discard(7.1+): la segnalazione è delegata lì, cosìmonitornon cattura i tentativi intermedi.
Class Method Details
.apply_job_scope(job) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/closeyourit/rails/active_job_extension.rb', line 87 def self.apply_job_scope(job) CloseYourIt.set_tag("job.class", job.class.name) CloseYourIt.set_tag("job.queue", job.queue_name) if job.respond_to?(:queue_name) context = {} context["job_id"] = job.job_id if job.respond_to?(:job_id) context["executions"] = job.executions if job.respond_to?(:executions) CloseYourIt.set_context("active_job", context) unless context.empty? # Un trace_id stabile per esecuzione (job_id è unico per run ActiveJob): log ed errore dello # stesso job condividono il trace_id → correlazione log↔errore anche fuori dal ciclo richiesta. CloseYourIt::Scope.current.trace_id = job.job_id if job.respond_to?(:job_id) end |
.included(base) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/closeyourit/rails/active_job_extension.rb', line 30 def self.included(base) base.around_perform do |job, block| CloseYourIt::Rails::ActiveJobExtension.monitor(job) { block.call } end return unless base.respond_to?(:after_discard) base.after_discard do |job, exception| CloseYourIt::Rails::ActiveJobExtension.report_discarded(job, exception) end end |
.monitor(job) ⇒ Object
Esegue il job arricchendo lo scope con tag/context; resetta lo scope a fine job (no bleed tra
job sullo stesso thread). Su ActiveJob 7.1+ NON cattura l'errore (lo fa after_discard solo al
fallimento definitivo): qui tramanda lo scope al job e ri-solleva. Sul fallback legacy cattura.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/closeyourit/rails/active_job_extension.rb', line 45 def self.monitor(job) return yield unless CloseYourIt.configuration.report_active_job_errors begin apply_job_scope(job) yield rescue Exception => e # rubocop:disable Lint/RescueException if report_on_discard?(job) job.instance_variable_set(STASHED_SCOPE_IVAR, CloseYourIt::Scope.current) else CloseYourIt.capture_exception(e, handled: false) end raise ensure CloseYourIt::Scope.reset! end end |
.report_discarded(job, exception) ⇒ Object
Aggancio after_discard (ActiveJob 7.1+): il job è definitivamente fallito, quindi segnaliamo
l'errore UNA sola volta (handled:false). Riprendiamo lo scope arricchito durante il perform
(tramandato da monitor) così il report conserva breadcrumb/tag/contesti raccolti nel job;
apply_job_scope rinfresca i campi standard con executions finale senza perdere i custom.
67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/closeyourit/rails/active_job_extension.rb', line 67 def self.report_discarded(job, exception) return unless CloseYourIt.configuration.report_active_job_errors begin stashed = job.instance_variable_get(STASHED_SCOPE_IVAR) CloseYourIt::Scope.current = stashed if stashed apply_job_scope(job) CloseYourIt.capture_exception(exception, handled: false) ensure job.remove_instance_variable(STASHED_SCOPE_IVAR) if job.instance_variable_defined?(STASHED_SCOPE_IVAR) CloseYourIt::Scope.reset! end end |
.report_on_discard?(job) ⇒ Boolean
true quando ActiveJob espone after_discard (7.1+): la segnalazione è delegata lì, così
monitor non cattura i tentativi intermedi. false → fallback legacy (cattura nell'around_perform).
83 84 85 |
# File 'lib/closeyourit/rails/active_job_extension.rb', line 83 def self.report_on_discard?(job) job.class.respond_to?(:after_discard) end |