From 696d67ea7c0ec00f905e22ab512b7f10e7ec8347 Mon Sep 17 00:00:00 2001 From: Giovanni Cangiani Date: Wed, 9 Sep 2026 17:57:53 +0200 Subject: [PATCH 1/3] fix: context field compatibility between model and db schema --- app/models/solid_errors/occurrence.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 4f034e0..fb6b694 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -5,6 +5,8 @@ class Occurrence < Record after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? } after_create_commit :clear_resolved_errors, if: :should_clear_resolved_errors? + serialize :context, coder: JSON unless connection.adapter_name.downcase.include?("postgres") + # The parsed exception backtrace. Lines in this backtrace that are from installed gems # have the base path for gem installs replaced by "[GEM_ROOT]", while those in the project # have "[PROJECT_ROOT]". From a32c94cf7915b11cf6dec782c1d2bbce8d5e451b Mon Sep 17 00:00:00 2001 From: Giovanni Cangiani Date: Wed, 9 Sep 2026 17:57:53 +0200 Subject: [PATCH 2/3] fix: context field compatibility between model and db schema --- app/models/solid_errors/occurrence.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 4f034e0..295ff94 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -5,6 +5,10 @@ class Occurrence < Record after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? } after_create_commit :clear_resolved_errors, if: :should_clear_resolved_errors? + unless type_for_attribute("context").is_a?(ActiveRecord::Type::Json) + serialize :context, coder: JSON + end + # The parsed exception backtrace. Lines in this backtrace that are from installed gems # have the base path for gem installs replaced by "[GEM_ROOT]", while those in the project # have "[PROJECT_ROOT]". From 666a1938b166d820261c28cd011a01af01c4e74d Mon Sep 17 00:00:00 2001 From: Giovanni Cangiani Date: Thu, 10 Sep 2026 17:17:28 +0200 Subject: [PATCH 3/3] feature: user defined ignored_errors this includes a slight refactoring of the way the IGNORED_ERRORS array is built. The original code were allowing for class objects to be passed. The feature is not used because it can actually lead to exception when the class is not defined. Instead of a loop lookup, I think it is faster to rely on the keys of an hash. --- README.md | 2 +- lib/solid_errors.rb | 5 +++++ lib/solid_errors/subscriber.rb | 9 ++------ test/models/solid_errors/occurrence_test.rb | 23 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 874a177..52a36ce 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ You can configure Solid Errors via the Rails configuration object, under the `so * `email_subject_prefix` - Prefix added to the subject line for email notifications. See [Email notifications](#email-notifications) for more information. * `base_controller_class` - Specify a different controller as the base class for the Solid Errors controller. See [Authentication](#authentication) for more information. * `destroy_after` - If set, Solid Errors will periodically destroy resolved records that are older than the value specified. See [Automatically destroying old records](#automatically-destroying-old-records) for more information. +* `ignored_errors` - An array of error class names (strings) that will not be saved in addition to the default list hardcoded in `lib/solid_errors/subscriber.rb` ### Database Configuration @@ -376,4 +377,3 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct Everyone interacting in the SolidErrors project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fractaledmind/solid_errors/blob/main/CODE_OF_CONDUCT.md). - diff --git a/lib/solid_errors.rb b/lib/solid_errors.rb index c2a3788..dee56aa 100644 --- a/lib/solid_errors.rb +++ b/lib/solid_errors.rb @@ -15,6 +15,7 @@ module SolidErrors mattr_accessor :email_to mattr_accessor :email_subject_prefix mattr_accessor :destroy_after + mattr_writer :ignored_errors class << self # use method instead of attr_accessor to ensure @@ -29,6 +30,10 @@ def password @password ||= ENV["SOLIDERRORS_PASSWORD"] || @@password end + def ignored_errors + @ignored_errors ||= (@@ignored_errors || []).to_h{|k| [k,true]} + end + def send_emails? send_emails && email_to.present? end diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index 2063250..c8e8b0c 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -19,11 +19,10 @@ class Subscriber "CGI::Session::CookieStore::TamperedWithCookie", "Mongoid::Errors::DocumentNotFound", "Sinatra::NotFound", - "Sidekiq::JobRetry::Skip"].map(&:freeze).freeze + "Sidekiq::JobRetry::Skip"].to_h{|v| [v.freeze,true]}.freeze def report(error, handled:, severity:, context:, source: nil) return if ignore_by_class?(error.class.name) - error_attributes = { exception_class: error.class.name, message: s(error.message), @@ -49,11 +48,7 @@ def s(data) end def ignore_by_class?(error_class_name) - IGNORED_ERRORS.any? do |ignored_class| - ignored_class_name = ignored_class.respond_to?(:name) ? ignored_class.name : ignored_class - - ignored_class_name == error_class_name - end + SolidErrors.ignored_errors.key?(error_class_name) || IGNORED_ERRORS.key?(error_class_name) end end end diff --git a/test/models/solid_errors/occurrence_test.rb b/test/models/solid_errors/occurrence_test.rb index c994e7d..6cab4b5 100644 --- a/test/models/solid_errors/occurrence_test.rb +++ b/test/models/solid_errors/occurrence_test.rb @@ -39,6 +39,28 @@ def teardown end end + test "an ignored error should not be saved while an acceptable one is saved" do + SolidErrors.ignored_errors = ["RuntimeError"] + # An hardcoded ignored error should be discarded + assert_difference -> { SolidErrors::Error.count }, 0 do + assert_difference -> { SolidErrors::Occurrence.count }, 0 do + Rails.error.report( ActionController::RoutingError.new('argh')) + end + end + # A user ignored error should be discarded + assert_difference -> { SolidErrors::Error.count }, 0 do + assert_difference -> { SolidErrors::Occurrence.count }, 0 do + Rails.error.report( RuntimeError.new("argh") ) + end + end + # A valid error should be recorded + assert_difference -> { SolidErrors::Error.count }, +1 do + assert_difference -> { SolidErrors::Occurrence.count }, +1 do + Rails.error.report(StandardError.new("argh")) + end + end + end + private def simulate_99_old_exceptions(status) @@ -46,4 +68,5 @@ def simulate_99_old_exceptions(status) SolidErrors::Error.update_all(resolved_at: Time.current) if status == :resolved SolidErrors::Occurrence.last.update!(id: 99, created_at: 1.day.ago) end + end