Class: EzLogsAgent::Capturers::DatabaseCapturer

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

Overview

Captures database operations via ActiveRecord model lifecycle callbacks.

This capturer:

  • Installs after_create, after_update, after_destroy callbacks on ActiveRecord::Base

  • Captures model class, record id, and operation type

  • Extracts resource_ids from the model instance

  • For updates, extracts curated business-relevant change context

  • Preserves correlation_id from current context

  • Never crashes the host application (fail-open)

  • Respects capture_database configuration flag

What This Capturer Does NOT Do

  • Parse SQL queries

  • Dump full attribute diffs

  • Include sensitive data

  • Guess actors

  • Act as an audit log

Event Shape

Produces events with:

  • source_type: :database_callback

  • source_data: { model_class: “User”, operation: “create|update|destroy” }

  • outcome: :success

  • correlation_id: EzLogsAgent::Correlation.current (if present)

  • resource_ids: [{ resource_type: “User”, resource_id: “123” }]

  • context: { changes: [{ attribute: “status”, from: “pending”, to: “shipped” }, …] } (updates only, if meaningful)

Constant Summary collapse

IGNORED_ATTRIBUTES =

Attributes to always ignore when detecting business changes

%w[
  id
  created_at
  updated_at
  lock_version
  encrypted_password
  reset_password_token
  reset_password_sent_at
  remember_created_at
  confirmation_token
  confirmed_at
  confirmation_sent_at
  unconfirmed_email
  unlock_token
  locked_at
  sign_in_count
  current_sign_in_at
  last_sign_in_at
  current_sign_in_ip
  last_sign_in_ip
].freeze
SENSITIVE_PATTERNS =

Patterns for sensitive data to ignore

%w[
  password
  token
  secret
  api_key
  credit_card
  ssn
  social_security
  encrypted
].freeze

Class Method Summary collapse

Class Method Details

.handle_create(model) ⇒ void

This method returns an undefined value.

Handles after_create callback

Parameters:

  • model (ActiveRecord::Base)

    The created model instance



110
111
112
113
114
115
116
117
118
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 110

def handle_create(model)
  return unless capture_enabled?

  context = extract_initial_attributes(model) || {}
  context[:display_name] = resolve_display_name(model)
  capture_event(model, "create", context: context.presence)
rescue StandardError => e
  EzLogsAgent::Logger.error("[DatabaseCapturer] handle_create failed: #{e.class} - #{e.message}")
end

.handle_destroy(model) ⇒ void

This method returns an undefined value.

Handles after_destroy callback

Parameters:

  • model (ActiveRecord::Base)

    The destroyed model instance



138
139
140
141
142
143
144
145
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 138

def handle_destroy(model)
  return unless capture_enabled?

  context = { display_name: resolve_display_name(model) }
  capture_event(model, "destroy", context: context.presence)
rescue StandardError => e
  EzLogsAgent::Logger.error("[DatabaseCapturer] handle_destroy failed: #{e.class} - #{e.message}")
end

.handle_update(model) ⇒ void

This method returns an undefined value.

Handles after_update callback

Parameters:

  • model (ActiveRecord::Base)

    The updated model instance



124
125
126
127
128
129
130
131
132
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 124

def handle_update(model)
  return unless capture_enabled?

  context = extract_change_context(model) || {}
  context[:display_name] = resolve_display_name(model)
  capture_event(model, "update", context: context.presence)
rescue StandardError => e
  EzLogsAgent::Logger.error("[DatabaseCapturer] handle_update failed: #{e.class} - #{e.message}")
end

.installvoid

This method returns an undefined value.

Installs ActiveRecord lifecycle callbacks for database capture.

This method is idempotent and can be called multiple times safely. Only installs if ActiveRecord is present.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 86

def install
  return unless defined?(ActiveRecord::Base)
  return if @installed

  # Only register callbacks once per Ruby process
  unless @callbacks_registered
    ActiveRecord::Base.class_eval do
      after_create { |model| EzLogsAgent::Capturers::DatabaseCapturer.handle_create(model) }
      after_update { |model| EzLogsAgent::Capturers::DatabaseCapturer.handle_update(model) }
      after_destroy { |model| EzLogsAgent::Capturers::DatabaseCapturer.handle_destroy(model) }
    end
    @callbacks_registered = true
  end

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