Module: Appsignal::Hooks::ActiveJobHook::ActiveJobTraceContext
- Defined in:
- lib/appsignal/hooks/active_job.rb
Overview
Reads and writes W3C trace context on the ActiveJob enqueue/perform boundary, wire-compatible with OpenTelemetry's ActiveJob instrumentation. All of this no-ops outside collector mode.
Context rides on the job under __otel_headers, the same carrier OTel
uses. Stock serialize/deserialize only carry a fixed key set, so --
like OTel -- we patch both plus an accessor to round-trip it. The on-wire
value is run through ActiveJob's argument serializer (an array of
[key, value] pairs), matching OTel byte-for-byte so an AppSignal- and an
OTel-instrumented service read each other's jobs.
Instance Attribute Summary collapse
Instance Method Summary collapse
- #deserialize(job_data) ⇒ Object
-
#enqueue ⇒ Object
Inject on enqueue from inside a producer event, so the job carries this transaction's context and the perform later links back.
- #serialize ⇒ Object
Instance Attribute Details
#__otel_headers ⇒ Object
247 248 249 |
# File 'lib/appsignal/hooks/active_job.rb', line 247 def __otel_headers @__otel_headers ||= {} end |
Instance Method Details
#deserialize(job_data) ⇒ Object
240 241 242 243 244 245 |
# File 'lib/appsignal/hooks/active_job.rb', line 240 def deserialize(job_data) super serialized = job_data["__otel_headers"] @__otel_headers = serialized ? ::ActiveJob::Arguments.deserialize(serialized).to_h : {} end |
#enqueue ⇒ Object
Inject on enqueue from inside a producer event, so the job carries this
transaction's context and the perform later links back. Mirrors the
Sidekiq client middleware: an AppSignal event (a producer span in
collector mode), not a direct SDK span. Appsignal.instrument is a
transparent pass-through when there's no active transaction, and
inject_context no-ops outside collector mode.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/appsignal/hooks/active_job.rb', line 192 def enqueue(*, **) # When enqueue instrumentation is disabled, drop the trace context # along with the event. Without an enqueue event there is no producer # span, so the context we would write is that of whatever span is # current, such as the surrounding web request. The job that performs # later would then link back to a span that is not a producer. return super if Appsignal.config && !Appsignal.config[:enable_job_enqueue_instrumentation] # Another enqueue integration is already recording this enqueue, so # don't record it a second time. if Appsignal::Transaction.current? && Appsignal::Transaction.current.job_enqueue_events_suppressed? return super end Appsignal.instrument( "enqueue.active_job", "enqueue #{self.class.name} job", :opentelemetry_kind => :producer, :opentelemetry_scope => ["appsignal-ruby/active_job", Appsignal::VERSION] ) do Appsignal::Transaction.current.add_opentelemetry_attributes( Appsignal::OpenTelemetry::Messaging .enqueue_attributes("active_job", :destination => queue_name) ) Appsignal::OpenTelemetry.inject_context(__otel_headers) # Active Job enqueues through an adapter (Sidekiq, Resque, ...) that # has its own enqueue instrumentation. Suppress it so the enqueue is # recorded once, as this event, rather than as nested Active Job + # adapter events. if Appsignal::Transaction.current? Appsignal::Transaction.current.suppress_job_enqueue_events { super } else super end end end |