Class: EzLogsAgent::Capturers::ActiveJobCapturer

Inherits:
Object
  • Object
show all
Defined in:
lib/ez_logs_agent/capturers/active_job_capturer.rb

Overview

ActiveJob hooks for correlation propagation and job execution capture.

This capturer provides first-class ActiveJob support with two responsibilities:

  1. **Enqueue-Time (Correlation Propagation)**:

    • Uses ‘before_enqueue` callback to inject correlation_id into job

    • Preserves causal chain: HTTP → Job → Job

  2. **Execution-Time (Job Capture)**:

    • Uses ‘around_perform` callback to capture job execution as background_job events

    • Restores correlation_id from job

    • Measures duration and captures success/failure outcome

Serialization Support

ActiveJob’s metadata hash is NOT automatically serialized. This capturer overrides ‘serialize` and `deserialize` to persist the correlation_id across job serialization (required for Async adapter and other adapters that serialize jobs).

Sidekiq Adapter Detection

If the job’s queue adapter is Sidekiq, this capturer:

  • STILL propagates correlation at enqueue-time

  • SKIPS execution capture (defers to Sidekiq server middleware)

This prevents double events when Sidekiq is the adapter.

Installation

This capturer is automatically installed by Railtie when ActiveJob is detected and ‘capture_jobs = true`.

For manual installation (if not using Rails):

EzLogsAgent::Capturers::ActiveJobCapturer.install

Class Method Summary collapse

Class Method Details

.capture_execution(job, block) ⇒ Object

Captures job execution as a background_job event.

Runs at execution-time (when job executes). Skips capture if job uses Sidekiq adapter (prevents double events).

IMPORTANT: When jobs run inline (Async adapter, perform_now, test adapter), they execute in the same thread as the caller (HTTP request or parent job). We must save and restore the previous correlation to avoid clearing the outer context’s correlation. This applies to:

  • Development with Async adapter

  • Production with perform_now calls

  • Test environments

  • Any synchronous job execution

Parameters:

  • job (ActiveJob::Base)

    The job being executed

  • block (Proc)

    The job execution block

Returns:

  • (Object)

    The result of the job execution



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/ez_logs_agent/capturers/active_job_capturer.rb', line 103

def capture_execution(job, block)
  return block.call unless EzLogsAgent.configuration.capture_jobs

  if sidekiq_adapter?(job)
    EzLogsAgent::Logger.debug("[ActiveJobCapturer] Skipping capture (Sidekiq adapter)")
    return block.call
  end

  if excluded_job_class?(job)
    EzLogsAgent::Logger.debug("[ActiveJobCapturer] Skipping capture (excluded job class: #{job.class.name})")
    return block.call
  end

  # Save the previous correlation (from HTTP middleware or parent job)
  # so we can restore it after the job completes
  previous_correlation = EzLogsAgent::Correlation.current

  # Use job's propagated correlation, fall back to current context, or generate new
  correlation_id = extract_correlation(job) || previous_correlation || EzLogsAgent::Correlation.generate
  EzLogsAgent::Correlation.current = correlation_id

  start_time = Time.now
  result = block.call
  duration_ms = ((Time.now - start_time) * 1000).to_i

  capture_success(job, correlation_id, duration_ms, start_time)
  result
rescue StandardError => error
  capture_failure(job, correlation_id, error, start_time)
  raise
ensure
  # Restore previous correlation instead of unconditionally clearing.
  # This is critical for inline jobs that run in the same thread as the caller.
  if previous_correlation
    EzLogsAgent::Correlation.current = previous_correlation
  else
    EzLogsAgent::Correlation.clear
  end
end

.installvoid

This method returns an undefined value.

Installs ActiveJob hooks for correlation propagation and job capture.

This method is idempotent and can be called multiple times safely.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ez_logs_agent/capturers/active_job_capturer.rb', line 51

def install
  return unless defined?(ActiveJob)

  install_serialization_hooks unless @serialization_installed

  ActiveJob::Base.before_enqueue do |job|
    ActiveJobCapturer.propagate_correlation(job)
  end

  ActiveJob::Base.around_perform do |job, block|
    ActiveJobCapturer.capture_execution(job, block)
  end

  EzLogsAgent::Logger.debug("[ActiveJobCapturer] Hooks installed")
rescue StandardError => e
  EzLogsAgent::Logger.error("[ActiveJobCapturer] Installation failed: #{e.class} - #{e.message}")
end

.propagate_correlation(job) ⇒ void

This method returns an undefined value.

Propagates correlation_id from current context into job.

Runs at enqueue-time (when job is scheduled). Stores correlation in ‘ezlogs_correlation_id` attribute which survives serialization/deserialization.

Parameters:

  • job (ActiveJob::Base)

    The job being enqueued



77
78
79
80
81
82
83
84
# File 'lib/ez_logs_agent/capturers/active_job_capturer.rb', line 77

def propagate_correlation(job)
  correlation_id = EzLogsAgent::Correlation.current
  return unless correlation_id && !correlation_id.empty?

  job.ezlogs_correlation_id = correlation_id
rescue StandardError => e
  EzLogsAgent::Logger.error("[ActiveJobCapturer] Correlation propagation failed: #{e.class} - #{e.message}")
end