Class: EzLogsAgent::Capturers::DatabaseCapturer
- Inherits:
-
Object
- Object
- EzLogsAgent::Capturers::DatabaseCapturer
- 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.
The first source of truth is ‘record.class.encrypted_attributes` (Rails 7+ `encrypts :foo` declaration) — see encrypted_attribute?. If the host app encrypted it, we never capture it.
This list is the secondary defense: column names that frequently carry sensitive material even when the host app didn’t declare ‘encrypts` (legacy code, manual hashing, externally-generated material). Matching is substring + case-insensitive.
%w[ password token secret api_key credit_card ssn social_security encrypted private_key public_key signing_key pem cipher nonce salt digest signature hmac ].freeze
Class Method Summary collapse
-
.handle_create(model) ⇒ void
Handles after_create callback.
-
.handle_destroy(model) ⇒ void
Handles after_destroy callback.
-
.handle_update(model) ⇒ void
Handles after_update callback.
-
.install ⇒ void
Installs ActiveRecord lifecycle callbacks for database capture.
Class Method Details
.handle_create(model) ⇒ void
This method returns an undefined value.
Handles after_create callback
129 130 131 132 133 134 135 136 137 |
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 129 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.}") end |
.handle_destroy(model) ⇒ void
This method returns an undefined value.
Handles after_destroy callback
157 158 159 160 161 162 163 164 |
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 157 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.}") end |
.handle_update(model) ⇒ void
This method returns an undefined value.
Handles after_update callback
143 144 145 146 147 148 149 150 151 |
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 143 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.}") end |
.install ⇒ void
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.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ez_logs_agent/capturers/database_capturer.rb', line 105 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.}") end |