Class: EzLogsAgent::Railtie

Inherits:
Rails::Railtie
  • Object
show all
Defined in:
lib/ez_logs_agent/railtie.rb

Overview

Rails Railtie for automatic zero-config runtime integration.

This Railtie is the ONLY orchestrator in EzLogs Agent. It handles:

  • Starting/stopping the FlushScheduler

  • Auto-registering HTTP middleware

  • Auto-registering Sidekiq middleware (client + server)

  • Auto-installing ActiveJob capturer

  • Auto-installing Database capturer

All integrations are:

  • Defensive (wrapped in begin/rescue, never crash host app)

  • Configuration-gated (respect capture_http, capture_jobs, capture_database)

  • Framework-aware (check defined?(Sidekiq), defined?(ActiveRecord), etc.)

  • Idempotent (safe to boot multiple times)

  • Explicitly logged (log what’s enabled/skipped and why)

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.activejob_installedObject

Returns the value of attribute activejob_installed.



30
31
32
# File 'lib/ez_logs_agent/railtie.rb', line 30

def activejob_installed
  @activejob_installed
end

.database_capturer_installedObject

Returns the value of attribute database_capturer_installed.



30
31
32
# File 'lib/ez_logs_agent/railtie.rb', line 30

def database_capturer_installed
  @database_capturer_installed
end

.sidekiq_client_registeredObject

Returns the value of attribute sidekiq_client_registered.



30
31
32
# File 'lib/ez_logs_agent/railtie.rb', line 30

def sidekiq_client_registered
  @sidekiq_client_registered
end

.sidekiq_server_registeredObject

Returns the value of attribute sidekiq_server_registered.



30
31
32
# File 'lib/ez_logs_agent/railtie.rb', line 30

def sidekiq_server_registered
  @sidekiq_server_registered
end

Class Method Details

.install_activejob_capturervoid

This method returns an undefined value.

Install ActiveJob capturer

ActiveJob capturer handles correlation propagation and job capture for non-Sidekiq adapters (async, inline, etc.).



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
# File 'lib/ez_logs_agent/railtie.rb', line 195

def self.install_activejob_capturer
  # Check if ActiveJob is present
  unless defined?(ActiveJob)
    EzLogsAgent::Logger.debug("[Railtie] ActiveJob not detected, skipping ActiveJob capturer")
    return
  end

  # Check if job capture is enabled
  unless EzLogsAgent.configuration.capture_jobs
    # Already logged in register_sidekiq_middleware if Sidekiq present
    # Only log here if Sidekiq is NOT present
    unless defined?(Sidekiq)
      EzLogsAgent::Logger.debug("[Railtie] Job capture disabled (capture_jobs = false)")
    end
    return
  end

  return if @activejob_installed

  EzLogsAgent::Capturers::ActiveJobCapturer.install
  @activejob_installed = true
  EzLogsAgent::Logger.debug("[Railtie] ActiveJob capturer installed")
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to install ActiveJob capturer: #{e.class} - #{e.message}")
end

.install_database_capturervoid

This method returns an undefined value.

Install Database capturer

Database capturer installs ActiveRecord lifecycle callbacks (after_create, after_update, after_destroy) for all models.



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/ez_logs_agent/railtie.rb', line 227

def self.install_database_capturer
  # Check if ActiveRecord is present
  unless defined?(ActiveRecord)
    EzLogsAgent::Logger.debug("[Railtie] ActiveRecord not detected, skipping database capture")
    return
  end

  # Check if database capture is enabled
  unless EzLogsAgent.configuration.capture_database
    EzLogsAgent::Logger.debug("[Railtie] Database capture disabled (capture_database = false)")
    return
  end

  return if @database_capturer_installed

  EzLogsAgent::Capturers::DatabaseCapturer.install
  @database_capturer_installed = true
  EzLogsAgent::Logger.debug("[Railtie] Database capture installed")
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to install database capturer: #{e.class} - #{e.message}")
end

.log_configuration_summaryvoid

This method returns an undefined value.

Log configuration summary at boot time

Shows what’s enabled/disabled to help with debugging and visibility.



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/ez_logs_agent/railtie.rb', line 327

def self.log_configuration_summary
  config = EzLogsAgent.configuration

  EzLogsAgent::Logger.info("[Railtie] Configuration:")
  EzLogsAgent::Logger.info("[Railtie]   Server: #{config.server_url}")
  EzLogsAgent::Logger.info("[Railtie]   Capture HTTP: #{config.capture_http}")
  EzLogsAgent::Logger.info("[Railtie]   Capture Jobs: #{config.capture_jobs}")
  EzLogsAgent::Logger.info("[Railtie]   Capture Database: #{config.capture_database}")

  # Show optional configuration if present
  if config.actor_from_request
    EzLogsAgent::Logger.info("[Railtie]   Actor Extraction: enabled")
  end

  if config.display_name_for && config.display_name_for.any?
    EzLogsAgent::Logger.info("[Railtie]   Display Names: configured for #{config.display_name_for.keys.join(', ')}")
  end
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to log configuration summary: #{e.class} - #{e.message}")
end

.register_sidekiq_client_middlewarevoid

This method returns an undefined value.

Register Sidekiq client middleware

Client middleware runs in ALL processes (web, worker, console) and injects correlation_id into job payloads at enqueue-time.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ez_logs_agent/railtie.rb', line 148

def self.register_sidekiq_client_middleware
  return if @sidekiq_client_registered

  Sidekiq.configure_client do |config|
    config.client_middleware do |chain|
      chain.add EzLogsAgent::Capturers::JobCapturer::ClientMiddleware
    end
  end

  @sidekiq_client_registered = true
  EzLogsAgent::Logger.debug("[Railtie] Sidekiq client middleware registered")
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to register Sidekiq client middleware: #{e.class} - #{e.message}")
end

.register_sidekiq_middlewarevoid

This method returns an undefined value.

Register Sidekiq client and server middleware



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ez_logs_agent/railtie.rb', line 120

def self.register_sidekiq_middleware
  # Check if Sidekiq is present
  unless defined?(Sidekiq)
    EzLogsAgent::Logger.debug("[Railtie] Sidekiq not detected, skipping job capture middleware")
    return
  end

  # Check if job capture is enabled
  unless EzLogsAgent.configuration.capture_jobs
    EzLogsAgent::Logger.debug("[Railtie] Job capture disabled (capture_jobs = false)")
    return
  end

  # Register client middleware (correlation propagation)
  register_sidekiq_client_middleware

  # Register server middleware (job execution capture)
  register_sidekiq_server_middleware
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to register Sidekiq middleware: #{e.class} - #{e.message}")
end

.register_sidekiq_server_middlewarevoid

This method returns an undefined value.

Register Sidekiq server middleware

Server middleware runs ONLY in Sidekiq worker processes and captures job execution as background_job events.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/ez_logs_agent/railtie.rb', line 169

def self.register_sidekiq_server_middleware
  return if @sidekiq_server_registered

  Sidekiq.configure_server do |config|
    # Also register client middleware in server process (for job-enqueues-job scenarios)
    config.client_middleware do |chain|
      chain.add EzLogsAgent::Capturers::JobCapturer::ClientMiddleware
    end

    config.server_middleware do |chain|
      chain.add EzLogsAgent::Capturers::JobCapturer::ServerMiddleware
    end
  end

  @sidekiq_server_registered = true
  EzLogsAgent::Logger.debug("[Railtie] Sidekiq server middleware registered")
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to register Sidekiq server middleware: #{e.class} - #{e.message}")
end

.reset_registration_state!Object

Reset registration state (for testing)



34
35
36
37
38
39
# File 'lib/ez_logs_agent/railtie.rb', line 34

def reset_registration_state!
  @sidekiq_client_registered = false
  @sidekiq_server_registered = false
  @activejob_installed = false
  @database_capturer_installed = false
end

.setup_flush_schedulervoid

This method returns an undefined value.

Setup FlushScheduler with fork-aware initialization

Background threads don’t survive fork(). In forking servers like Puma cluster mode or Unicorn, we must start FlushScheduler AFTER the fork in each worker process.

Strategy:

  1. Always register ActiveSupport::ForkTracker hook (handles all forking cases)

  2. Also start immediately (for non-forking servers or single-process mode)

  3. FlushScheduler.start is idempotent, so calling it multiple times is safe



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/ez_logs_agent/railtie.rb', line 261

def self.setup_flush_scheduler
  # Register fork hook for Puma cluster mode, Unicorn, etc.
  # ActiveSupport::ForkTracker (Rails 6.1+) is the most reliable way
  if defined?(ActiveSupport::ForkTracker)
    ActiveSupport::ForkTracker.after_fork do
      start_flush_scheduler
    end
    EzLogsAgent::Logger.debug("[Railtie] Registered ActiveSupport::ForkTracker hook for FlushScheduler")
  end

  # Always start FlushScheduler now (for non-forking servers or master process)
  # In forking servers, this runs in master (will be killed on fork)
  # then ForkTracker restarts it in each worker
  start_flush_scheduler
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to setup FlushScheduler: #{e.class} - #{e.message}")
end

.start_flush_schedulervoid

This method returns an undefined value.

Start the FlushScheduler



282
283
284
285
286
287
# File 'lib/ez_logs_agent/railtie.rb', line 282

def self.start_flush_scheduler
  EzLogsAgent::FlushScheduler.start
  EzLogsAgent::Logger.debug("[Railtie] FlushScheduler started (PID: #{Process.pid})")
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to start FlushScheduler: #{e.class} - #{e.message}")
end

.validate_configurationBoolean

Validate configuration at boot time

Logs errors and warnings from configuration validation. If configuration is invalid, logs errors and returns false to skip initialization. If configuration has warnings, logs them but continues.

Returns:

  • (Boolean)

    true if valid or has only warnings, false if invalid



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/ez_logs_agent/railtie.rb', line 296

def self.validate_configuration
  result = EzLogsAgent::ConfigurationValidator.validate(EzLogsAgent.configuration)

  # Log errors
  if result.errors.any?
    EzLogsAgent::Logger.error("[Railtie] Configuration validation failed:")
    result.errors.each do |error|
      EzLogsAgent::Logger.error("[Railtie]   - #{error}")
    end
    EzLogsAgent::Logger.error("[Railtie] Agent initialization skipped. Please fix configuration errors.")
    return false
  end

  # Log warnings
  if result.warnings.any?
    result.warnings.each do |warning|
      EzLogsAgent::Logger.warn("[Railtie]   ⚠ #{warning}")
    end
  end

  true
rescue StandardError => e
  EzLogsAgent::Logger.error("[Railtie] Failed to validate configuration: #{e.class} - #{e.message}")
  false
end