From 4b27794cc59f8b88f0f2e99a0a438e450cb46788 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 17 Jul 2024 12:26:51 -0600 Subject: [PATCH 01/33] Add one_email_per_occurrence option and migration task Introduce the one_email_per_occurrence configuration option to limit email notifications to one per occurrence. Updated README and error controller to support the new feature, and added Rake task to handle migrations. --- .gitignore | 1 + README.md | 3 +++ .../solid_errors/errors_controller.rb | 2 +- app/models/solid_errors/occurrence.rb | 22 +++++++++++++------ lib/solid_errors.rb | 7 ++++++ ...38_add_prev_resolved_at_to_solid_errors.rb | 5 +++++ lib/tasks/solid_errors_tasks.rake | 18 +++++++++++++++ 7 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb create mode 100644 lib/tasks/solid_errors_tasks.rake diff --git a/.gitignore b/.gitignore index 927a7dc..60afa49 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /tmp/ .DS_Store test/dummy/log/*.log +.idea \ No newline at end of file diff --git a/README.md b/README.md index 8332ad4..7eae12c 100644 --- a/README.md +++ b/README.md @@ -237,11 +237,14 @@ Second, you can set the values via the configuration object: config.solid_errors.send_emails = true config.solid_errors.email_from = "errors@myapp.com" config.solid_errors.email_to = "devs@myapp.com" +# Tell Solid Errors whether or not to limit the total emails per occurrence. Defaults to false. config.solid_errors.email_subject_prefix = "[#{Rails.application.name}][#{Rails.env}]" ``` If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification whenever an error occurs. If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications. +If you have set `send_emails` to `true` and have set `one_email_per_occurrence` to `true`, Solid Errors will only send an email notification after the first occurrence of a new error or the first reoccurrence of a previously resolved error. If you have not set `one_email_per_occurrence` to `true`, Solid Errors will send an email notification for each occurrence of an error. + ### Examples There are only two screens in the dashboard. diff --git a/app/controllers/solid_errors/errors_controller.rb b/app/controllers/solid_errors/errors_controller.rb index 8703cf3..3631214 100644 --- a/app/controllers/solid_errors/errors_controller.rb +++ b/app/controllers/solid_errors/errors_controller.rb @@ -46,7 +46,7 @@ def destroy # Only allow a list of trusted parameters through. def error_params - params.require(:error).permit(:resolved_at) + params.require(:error).permit(:resolved_at, :prev_resolved_at) end def set_error diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 2c815b5..5dda2ca 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -2,7 +2,7 @@ module SolidErrors class Occurrence < Record belongs_to :error, class_name: "SolidErrors::Error" - after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? } + after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? && under_limit? } # 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 @@ -16,12 +16,20 @@ def parsed_backtrace private - def parse_backtrace(backtrace) - Backtrace.parse(backtrace) - end + def parse_backtrace(backtrace) + Backtrace.parse(backtrace) + end - def send_email - ErrorMailer.error_occurred(self).deliver_later - end + def send_email + ErrorMailer.error_occurred(self).deliver_later + end + + def under_limit? + return true if error.occurrences.count == 1 || !SolidErrors.one_email_per_occurrence + + error.occurrences.where("created_at > ?", error.prev_resolved_at).count == 1 + rescue StandardError + true + end end end diff --git a/lib/solid_errors.rb b/lib/solid_errors.rb index e0716a8..da78cd0 100644 --- a/lib/solid_errors.rb +++ b/lib/solid_errors.rb @@ -10,6 +10,7 @@ module SolidErrors mattr_writer :username mattr_writer :password mattr_writer :send_emails + mattr_writer :one_email_per_occurrence mattr_writer :email_from mattr_writer :email_to mattr_writer :email_subject_prefix @@ -31,6 +32,12 @@ def send_emails? @send_emails ||= ENV["SOLIDERRORS_SEND_EMAILS"] || @@send_emails || false end + # only send one email per occurrence while the issue is Unresolved + # if the issue was resolved and then reoccurs, another email will be sent + def one_email_per_occurrence + @one_email_per_occurrence ||= ENV["SOLIDERRORS_ONE_EMAIL_PER_OCCURRENCE"] || @@one_email_per_occurrence || false + end + def email_from @email_from ||= ENV["SOLIDERRORS_EMAIL_FROM"] || @@email_from || "solid_errors@noreply.com" end diff --git a/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb b/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb new file mode 100644 index 0000000..5b0a197 --- /dev/null +++ b/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb @@ -0,0 +1,5 @@ +class AddPrevResolvedAtToSolidErrors < ActiveRecord::Migration[6.1] + def change + add_column :solid_errors, :prev_resolved_at, :timestamp + end +end diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake new file mode 100644 index 0000000..2214eb9 --- /dev/null +++ b/lib/tasks/solid_errors_tasks.rake @@ -0,0 +1,18 @@ +namespace :solid_errors do + desc "Copy and run migrations from Solid Errors" + task :install_migrations => :environment do + source = File.expand_path('../../solid_errors/db/migrate', __FILE__) + destination = File.join(Rails.root, 'db', 'migrate') + + Dir.glob("#{source}/*.rb").each do |migration| + filename = File.basename(migration) + unless File.exist?(File.join(destination, filename)) + puts "Copying #{filename} from Solid Errors to Rails application" + FileUtils.cp(migration, destination) + end + end + + puts "Running migrations from Solid Errors" + Rake::Task['db:migrate'].invoke + end +end \ No newline at end of file From b11b32f5d6eddc25a7ab7436834adaf45fba6ede Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 17 Jul 2024 16:35:35 -0600 Subject: [PATCH 02/33] standard.rb fixes --- app/models/solid_errors/occurrence.rb | 24 ++++++++++++------------ lib/tasks/solid_errors_tasks.rake | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 5dda2ca..d45aeb3 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -16,20 +16,20 @@ def parsed_backtrace private - def parse_backtrace(backtrace) - Backtrace.parse(backtrace) - end + def parse_backtrace(backtrace) + Backtrace.parse(backtrace) + end - def send_email - ErrorMailer.error_occurred(self).deliver_later - end + def send_email + ErrorMailer.error_occurred(self).deliver_later + end - def under_limit? - return true if error.occurrences.count == 1 || !SolidErrors.one_email_per_occurrence + def under_limit? + return true if error.occurrences.count == 1 || !SolidErrors.one_email_per_occurrence - error.occurrences.where("created_at > ?", error.prev_resolved_at).count == 1 - rescue StandardError - true - end + error.occurrences.where("created_at > ?", error.prev_resolved_at).count == 1 + rescue + true + end end end diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 2214eb9..b4e36b3 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -1,8 +1,8 @@ namespace :solid_errors do desc "Copy and run migrations from Solid Errors" - task :install_migrations => :environment do - source = File.expand_path('../../solid_errors/db/migrate', __FILE__) - destination = File.join(Rails.root, 'db', 'migrate') + task install_migrations: :environment do + source = File.expand_path("../../solid_errors/db/migrate", __FILE__) + destination = File.join(Rails.root, "db", "migrate") Dir.glob("#{source}/*.rb").each do |migration| filename = File.basename(migration) @@ -13,6 +13,6 @@ namespace :solid_errors do end puts "Running migrations from Solid Errors" - Rake::Task['db:migrate'].invoke + Rake::Task["db:migrate"].invoke end -end \ No newline at end of file +end From d466265037a0e332430f43d591ffa5d50e04d4de Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 10:56:06 -0600 Subject: [PATCH 03/33] Adjustments from feedback on PR - Converted prev_resolved_at to a datetime from a timestamp in migration - Added a method `should_send_email?` to determine if an email goes instead of a string of conditionals - Cleaned up guard clauses - Improved the datetime comparison in an AR query --- app/models/solid_errors/occurrence.rb | 13 ++++++++----- ...16154238_add_prev_resolved_at_to_solid_errors.rb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index d45aeb3..d9b4d88 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -2,7 +2,7 @@ module SolidErrors class Occurrence < Record belongs_to :error, class_name: "SolidErrors::Error" - after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? && under_limit? } + after_create_commit :send_email, if: :should_send_email? # 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 @@ -24,12 +24,15 @@ def send_email ErrorMailer.error_occurred(self).deliver_later end + def should_send_email? + SolidErrors.send_emails? && SolidErrors.email_to.present? && under_limit? + end + def under_limit? - return true if error.occurrences.count == 1 || !SolidErrors.one_email_per_occurrence + return true if error.occurrences.count == 1 + return true if !SolidErrors.one_email_per_occurrence - error.occurrences.where("created_at > ?", error.prev_resolved_at).count == 1 - rescue - true + error.occurrences.where(created_at: error.prev_resolved_at..).one? end end end diff --git a/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb b/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb index 5b0a197..a889bf8 100644 --- a/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb +++ b/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb @@ -1,5 +1,5 @@ class AddPrevResolvedAtToSolidErrors < ActiveRecord::Migration[6.1] def change - add_column :solid_errors, :prev_resolved_at, :timestamp + add_column :solid_errors, :prev_resolved_at, :datetime end end From bba0eb1dc957e265bb4f0051643ab020c034e05c Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 12:16:20 -0600 Subject: [PATCH 04/33] Refactor solid_errors_tasks.rake to remove hardcoded migrations The refactor involved removing a segment which forced a specific migration to run in favor of Rails' mechanism of alerting when migrations are pending. The prior method was too rigid and hard-coded. Now, the system still copies the migration over but doesn't automatically run it. --- lib/tasks/solid_errors_tasks.rake | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index b4e36b3..ce5acdc 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -11,8 +11,15 @@ namespace :solid_errors do FileUtils.cp(migration, destination) end end - - puts "Running migrations from Solid Errors" - Rake::Task["db:migrate"].invoke + # this could be a way to "force" a migration to run in the future but + # the more thought into this, it seems too rigid and hard-coded + # Plus, after we copy the migration over, Rails will alert a user + # to run migrations/there are pending migrations. + # Set version for our specific migration + # ENV["VERSION"] = "20240716154238" + # Run just our specific migration file + # Rake::Task["db:migrate:up"].invoke + # Now clear that! + # ENV["VERSION"] = nil end end From 8d9505db767420540f51a655743d0dbf391376d1 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 12:25:15 -0600 Subject: [PATCH 05/33] Rework 'prev_resolved_at' from error_params The 'prev_resolved_at' parameter has been removed from the error_params in the errors_controller, and subsequently from the error buttons in the views. Meanwhile, in the subscriber, 'prev_resolved_at' is now updated with the current 'resolved_at' before this is set to nil, maintaining the history of resolution times. --- app/controllers/solid_errors/errors_controller.rb | 2 +- lib/solid_errors/subscriber.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/solid_errors/errors_controller.rb b/app/controllers/solid_errors/errors_controller.rb index 3631214..8703cf3 100644 --- a/app/controllers/solid_errors/errors_controller.rb +++ b/app/controllers/solid_errors/errors_controller.rb @@ -46,7 +46,7 @@ def destroy # Only allow a list of trusted parameters through. def error_params - params.require(:error).permit(:resolved_at, :prev_resolved_at) + params.require(:error).permit(:resolved_at) end def set_error diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index 05f6e46..04f27a5 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -32,7 +32,7 @@ def report(error, handled:, severity:, context:, source: nil) } fingerprint = Digest::SHA256.hexdigest(error_attributes.values.join) if (record = SolidErrors::Error.find_by(fingerprint: fingerprint)) - record.update!(resolved_at: nil, updated_at: Time.now) + record.update!(prev_resolved_at: record.resolved_at, resolved_at: nil, updated_at: Time.now) else record = SolidErrors::Error.create!(error_attributes.merge(fingerprint: fingerprint)) end From 429d2acfc2bd0fde4871063ce0ea4b8e0f9d6fc7 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 14:46:27 -0600 Subject: [PATCH 06/33] Refactor email sending condition in SolidErrors Simplified the condition check whether to send an email in SolidErrors::Occurrence. The method should_send_email? now has guard clauses if SolidErrors does not send emails or if no email recipient is set. Additionally, the instance method one_email_per_occurrence is renamed to one_email_per_occurrence? to follow the naming convention like `send_emails?`. --- app/models/solid_errors/occurrence.rb | 8 +++----- lib/solid_errors.rb | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index d9b4d88..2e41112 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -25,12 +25,10 @@ def send_email end def should_send_email? - SolidErrors.send_emails? && SolidErrors.email_to.present? && under_limit? - end - - def under_limit? + return false unless SolidErrors.send_emails? + return false unless SolidErrors.email_to.present? return true if error.occurrences.count == 1 - return true if !SolidErrors.one_email_per_occurrence + return true unless SolidErrors.one_email_per_occurrence? error.occurrences.where(created_at: error.prev_resolved_at..).one? end diff --git a/lib/solid_errors.rb b/lib/solid_errors.rb index da78cd0..5295a0b 100644 --- a/lib/solid_errors.rb +++ b/lib/solid_errors.rb @@ -34,7 +34,7 @@ def send_emails? # only send one email per occurrence while the issue is Unresolved # if the issue was resolved and then reoccurs, another email will be sent - def one_email_per_occurrence + def one_email_per_occurrence? @one_email_per_occurrence ||= ENV["SOLIDERRORS_ONE_EMAIL_PER_OCCURRENCE"] || @@one_email_per_occurrence || false end From 1391b73cb57689bd6ee1addd8e726f0a76f75d57 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 15:32:40 -0600 Subject: [PATCH 07/33] Remove one_email_per_occurrence config option from SolidErrors The config option that limits the email notifications to one email per error occurrence in the SolidErrors has been removed and, instead, that approach is used automatically (sending one per occurrence). Corrections were also made to the post-install message in the gemspec file to remove the ``*and* runs` from the note since we are not auto-running migrations. --- README.md | 2 -- app/models/solid_errors/occurrence.rb | 1 - lib/solid_errors.rb | 7 ------- lib/tasks/solid_errors_tasks.rake | 10 ---------- 4 files changed, 20 deletions(-) diff --git a/README.md b/README.md index 7eae12c..999db36 100644 --- a/README.md +++ b/README.md @@ -243,8 +243,6 @@ config.solid_errors.email_subject_prefix = "[#{Rails.application.name}][#{Rails. If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification whenever an error occurs. If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications. -If you have set `send_emails` to `true` and have set `one_email_per_occurrence` to `true`, Solid Errors will only send an email notification after the first occurrence of a new error or the first reoccurrence of a previously resolved error. If you have not set `one_email_per_occurrence` to `true`, Solid Errors will send an email notification for each occurrence of an error. - ### Examples There are only two screens in the dashboard. diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 2e41112..b1bfe3e 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -28,7 +28,6 @@ def should_send_email? return false unless SolidErrors.send_emails? return false unless SolidErrors.email_to.present? return true if error.occurrences.count == 1 - return true unless SolidErrors.one_email_per_occurrence? error.occurrences.where(created_at: error.prev_resolved_at..).one? end diff --git a/lib/solid_errors.rb b/lib/solid_errors.rb index 5295a0b..e0716a8 100644 --- a/lib/solid_errors.rb +++ b/lib/solid_errors.rb @@ -10,7 +10,6 @@ module SolidErrors mattr_writer :username mattr_writer :password mattr_writer :send_emails - mattr_writer :one_email_per_occurrence mattr_writer :email_from mattr_writer :email_to mattr_writer :email_subject_prefix @@ -32,12 +31,6 @@ def send_emails? @send_emails ||= ENV["SOLIDERRORS_SEND_EMAILS"] || @@send_emails || false end - # only send one email per occurrence while the issue is Unresolved - # if the issue was resolved and then reoccurs, another email will be sent - def one_email_per_occurrence? - @one_email_per_occurrence ||= ENV["SOLIDERRORS_ONE_EMAIL_PER_OCCURRENCE"] || @@one_email_per_occurrence || false - end - def email_from @email_from ||= ENV["SOLIDERRORS_EMAIL_FROM"] || @@email_from || "solid_errors@noreply.com" end diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index ce5acdc..321f1b7 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -11,15 +11,5 @@ namespace :solid_errors do FileUtils.cp(migration, destination) end end - # this could be a way to "force" a migration to run in the future but - # the more thought into this, it seems too rigid and hard-coded - # Plus, after we copy the migration over, Rails will alert a user - # to run migrations/there are pending migrations. - # Set version for our specific migration - # ENV["VERSION"] = "20240716154238" - # Run just our specific migration file - # Rake::Task["db:migrate:up"].invoke - # Now clear that! - # ENV["VERSION"] = nil end end From 6ce78bac8506c9e4494a715f8bebeac465b48650 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 15:59:05 -0600 Subject: [PATCH 08/33] Better README update for explaining new behavior --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 999db36..c5e41eb 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,9 @@ config.solid_errors.email_to = "devs@myapp.com" config.solid_errors.email_subject_prefix = "[#{Rails.application.name}][#{Rails.env}]" ``` -If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification whenever an error occurs. If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications. +If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification when an error first occurs. Subsequence occurrences of the error will not trigger additional emails to be sent. However, if an error is resolved and then reoccurs, an email will be sent, again, on the first reoccurrence of the error. + +If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications. ### Examples From 03d6cecd62e50fb05f656727b71c89cc036533f4 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 16:41:35 -0600 Subject: [PATCH 09/33] Make email sending condition consistent style; update migration copying process In the 'Occurrence' model, the condition checking if an error has only one occurrence has been simplified by replacing 'count == 1' with 'one?'. In the rake task, the file copying process from Solid Errors to Rails application has been altered so that instead of copying the file directly, it's named with a timestamp before copying to avoid potential naming conflicts. The migration file name was also simplified by removing the timestamp so that we can dynamically add one. --- app/models/solid_errors/occurrence.rb | 2 +- ...ors.rb => add_prev_resolved_at_to_solid_errors.rb} | 0 lib/tasks/solid_errors_tasks.rake | 11 +++++++---- 3 files changed, 8 insertions(+), 5 deletions(-) rename lib/solid_errors/db/migrate/{20240716154238_add_prev_resolved_at_to_solid_errors.rb => add_prev_resolved_at_to_solid_errors.rb} (100%) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index b1bfe3e..ccc1e0b 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -27,7 +27,7 @@ def send_email def should_send_email? return false unless SolidErrors.send_emails? return false unless SolidErrors.email_to.present? - return true if error.occurrences.count == 1 + return true if error.occurrences.one? error.occurrences.where(created_at: error.prev_resolved_at..).one? end diff --git a/lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb b/lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb similarity index 100% rename from lib/solid_errors/db/migrate/20240716154238_add_prev_resolved_at_to_solid_errors.rb rename to lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 321f1b7..0acc03d 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -5,10 +5,13 @@ namespace :solid_errors do destination = File.join(Rails.root, "db", "migrate") Dir.glob("#{source}/*.rb").each do |migration| - filename = File.basename(migration) - unless File.exist?(File.join(destination, filename)) - puts "Copying #{filename} from Solid Errors to Rails application" - FileUtils.cp(migration, destination) + original_filename = File.basename(migration) + timestamp = Time.now.strftime('%Y%m%d%H%M%S_') + filename_with_timestamp = "#{timestamp}#{original_filename}" + + unless File.exist?(File.join(destination, filename_with_timestamp)) + puts "Copying #{original_filename} to #{filename_with_timestamp} from Solid Errors to Rails application" + FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) end end end From 4882100e8ebbcf6c18dcc27532eae48f80e1c88c Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 17:09:04 -0600 Subject: [PATCH 10/33] Update file existence check in solid_errors_tasks This patch replaces the original file existence check so that it now checks if there are any files with the same original filename (no timestamp) in the entire directory structure under the destination path. This avoids unnecessary file copy operations. --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 0acc03d..387fa74 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -9,7 +9,7 @@ namespace :solid_errors do timestamp = Time.now.strftime('%Y%m%d%H%M%S_') filename_with_timestamp = "#{timestamp}#{original_filename}" - unless File.exist?(File.join(destination, filename_with_timestamp)) + if Dir.glob("#{destination}/**/*#{original_filename}*").empty? puts "Copying #{original_filename} to #{filename_with_timestamp} from Solid Errors to Rails application" FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) end From 9d22f4be4d4b2c1d46c903951ff9d9f31882995e Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 18 Jul 2024 22:39:29 -0600 Subject: [PATCH 11/33] Simplify file search in solid_errors_tasks rake task Don't need nested directory in glob --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 387fa74..7a004eb 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -9,7 +9,7 @@ namespace :solid_errors do timestamp = Time.now.strftime('%Y%m%d%H%M%S_') filename_with_timestamp = "#{timestamp}#{original_filename}" - if Dir.glob("#{destination}/**/*#{original_filename}*").empty? + if Dir.glob("#{destination}/*#{original_filename}*").empty? puts "Copying #{original_filename} to #{filename_with_timestamp} from Solid Errors to Rails application" FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) end From d9e7ee810921d81910a9bf457b84fdb4745c08ca Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Fri, 19 Jul 2024 11:14:48 -0600 Subject: [PATCH 12/33] Update README.md Co-authored-by: Stephen Margheim --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5e41eb..7510342 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ config.solid_errors.email_to = "devs@myapp.com" config.solid_errors.email_subject_prefix = "[#{Rails.application.name}][#{Rails.env}]" ``` -If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification when an error first occurs. Subsequence occurrences of the error will not trigger additional emails to be sent. However, if an error is resolved and then reoccurs, an email will be sent, again, on the first reoccurrence of the error. +If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification when an error first occurs. Subsequent occurrences of the error will not trigger additional emails to be sent; however, if an error is resolved and then reoccurs, an email will be sent, again, on the first reoccurrence of the error. If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications. From 2f76f691118ebb183eb6939aaa9f569a18146f70 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Fri, 19 Jul 2024 11:15:20 -0600 Subject: [PATCH 13/33] Update lib/tasks/solid_errors_tasks.rake Co-authored-by: Stephen Margheim --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 7a004eb..7c5183a 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -2,7 +2,7 @@ namespace :solid_errors do desc "Copy and run migrations from Solid Errors" task install_migrations: :environment do source = File.expand_path("../../solid_errors/db/migrate", __FILE__) - destination = File.join(Rails.root, "db", "migrate") + destination = Rails.root.join('db', 'migrate') Dir.glob("#{source}/*.rb").each do |migration| original_filename = File.basename(migration) From 2214f49775feb5bf54ab7a18220cabd6d6f6814c Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Fri, 19 Jul 2024 11:15:33 -0600 Subject: [PATCH 14/33] Update lib/tasks/solid_errors_tasks.rake Co-authored-by: Stephen Margheim --- lib/tasks/solid_errors_tasks.rake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 7c5183a..b0eb1d9 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -4,8 +4,7 @@ namespace :solid_errors do source = File.expand_path("../../solid_errors/db/migrate", __FILE__) destination = Rails.root.join('db', 'migrate') - Dir.glob("#{source}/*.rb").each do |migration| - original_filename = File.basename(migration) + Dir.children(source).each do |migration| timestamp = Time.now.strftime('%Y%m%d%H%M%S_') filename_with_timestamp = "#{timestamp}#{original_filename}" From 5bc0fdd8f1090752c734b5e1730aa6bd4d7459d1 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Fri, 19 Jul 2024 11:26:26 -0600 Subject: [PATCH 15/33] Update README and solid_errors tasks Added instructions to README file for running migration installer after updating the gem. Also, refactored 'solid_errors_tasks.rake' by simplifying the filename generation process during migration. These changes facilitate smoother gem updates and migrations. --- README.md | 5 +++++ lib/tasks/solid_errors_tasks.rake | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7510342..04c1330 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,11 @@ authenticate :user, -> (user) { user.admin? } do end ``` +After updating the gem, run the migration installer so that any new migrations are copied over: +```bash +$ rails solid_errors:install_migrations +``` + > [!NOTE] > Be sure to [secure the dashboard](#authentication) in production. diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index b0eb1d9..0925e87 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -6,10 +6,10 @@ namespace :solid_errors do Dir.children(source).each do |migration| timestamp = Time.now.strftime('%Y%m%d%H%M%S_') - filename_with_timestamp = "#{timestamp}#{original_filename}" + filename_with_timestamp = "#{timestamp}_#{migration}" - if Dir.glob("#{destination}/*#{original_filename}*").empty? - puts "Copying #{original_filename} to #{filename_with_timestamp} from Solid Errors to Rails application" + if Dir.glob("#{destination}/*#{original_filename}").empty? + puts "Copying #{migration} to #{filename_with_timestamp} from Solid Errors to Rails application" FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) end end From b912495bc6d9584678d382a6c4e794c3273ac593 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Fri, 19 Jul 2024 11:27:28 -0600 Subject: [PATCH 16/33] Forgot to remove an underscore --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 0925e87..37b230a 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -5,7 +5,7 @@ namespace :solid_errors do destination = Rails.root.join('db', 'migrate') Dir.children(source).each do |migration| - timestamp = Time.now.strftime('%Y%m%d%H%M%S_') + timestamp = Time.now.strftime('%Y%m%d%H%M%S') filename_with_timestamp = "#{timestamp}_#{migration}" if Dir.glob("#{destination}/*#{original_filename}").empty? From 461fe22ed0419ff2a8ceb2019f393489ebf06da2 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 22 Jul 2024 14:58:23 -0600 Subject: [PATCH 17/33] bumping version so I can test migration copying From 8c5415bae8f21a30bd93c56b57a378d1faf208e5 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 22 Jul 2024 15:03:45 -0600 Subject: [PATCH 18/33] a little more tweaking to test migration copy --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 37b230a..63c9603 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -8,7 +8,7 @@ namespace :solid_errors do timestamp = Time.now.strftime('%Y%m%d%H%M%S') filename_with_timestamp = "#{timestamp}_#{migration}" - if Dir.glob("#{destination}/*#{original_filename}").empty? + if Dir.glob("#{destination}/*#{migration}").empty? puts "Copying #{migration} to #{filename_with_timestamp} from Solid Errors to Rails application" FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) end From ae541006f98573c5de2e4dd60d3f2f4d10d2f9a7 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 22 Jul 2024 15:07:28 -0600 Subject: [PATCH 19/33] another test run --- lib/tasks/solid_errors_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake index 63c9603..46ad79e 100644 --- a/lib/tasks/solid_errors_tasks.rake +++ b/lib/tasks/solid_errors_tasks.rake @@ -10,7 +10,7 @@ namespace :solid_errors do if Dir.glob("#{destination}/*#{migration}").empty? puts "Copying #{migration} to #{filename_with_timestamp} from Solid Errors to Rails application" - FileUtils.cp(migration, File.join(destination, filename_with_timestamp)) + FileUtils.cp(File.join(source, migration), File.join(destination, filename_with_timestamp)) end end end From 0b80dd8c8b08b552c37e4c355a1e8142bb835ae8 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Tue, 23 Jul 2024 07:25:03 -0600 Subject: [PATCH 20/33] version bump 0.4.3.003 This version (x.003) has changes that ensure the migration is copied to the containing application. From 1bba415baa810e143824afaebc82288d6403d4d7 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 2 Dec 2024 13:36:42 -0700 Subject: [PATCH 21/33] Add full backtrace option and refine error handling. Introduced the `full_backtrace` configuration option for more granular control over logging error details. Removed unused migration files and rake tasks to simplify the codebase. Enhanced error handling by adding a backtrace cleaner to filter out irrelevant lines from logs. --- .../solid_errors/install/install_generator.rb | 1 + .../install/templates/db/errors_schema.rb | 1 + lib/solid_errors.rb | 7 +++ .../add_prev_resolved_at_to_solid_errors.rb | 5 --- lib/solid_errors/subscriber.rb | 44 ++++++++++--------- lib/tasks/solid_errors_tasks.rake | 17 ------- 6 files changed, 33 insertions(+), 42 deletions(-) delete mode 100644 lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb delete mode 100644 lib/tasks/solid_errors_tasks.rake diff --git a/lib/generators/solid_errors/install/install_generator.rb b/lib/generators/solid_errors/install/install_generator.rb index 862cd2c..8cb25b2 100644 --- a/lib/generators/solid_errors/install/install_generator.rb +++ b/lib/generators/solid_errors/install/install_generator.rb @@ -21,6 +21,7 @@ def configure_solid_errors '\1config.solid_errors.send_emails = true', '\1config.solid_errors.email_from = ""', '\1config.solid_errors.email_to = ""', + '\1config.solid_errors.full_backtrace = false', '\1config.solid_errors.username = Rails.application.credentials.dig(:solid_errors, :username)', '\1config.solid_errors.password = Rails.application.credentials.dig(:solid_errors, :password)' ].join("\n") diff --git a/lib/generators/solid_errors/install/templates/db/errors_schema.rb b/lib/generators/solid_errors/install/templates/db/errors_schema.rb index 1e60891..1b1dc29 100644 --- a/lib/generators/solid_errors/install/templates/db/errors_schema.rb +++ b/lib/generators/solid_errors/install/templates/db/errors_schema.rb @@ -7,6 +7,7 @@ t.text "severity", null: false t.text "source" t.datetime "resolved_at" + t.datetime "prev_resolved_at" t.string "fingerprint", limit: 64, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/lib/solid_errors.rb b/lib/solid_errors.rb index e0716a8..78fb934 100644 --- a/lib/solid_errors.rb +++ b/lib/solid_errors.rb @@ -7,6 +7,7 @@ module SolidErrors mattr_accessor :connects_to + mattr_accessor :full_backtrace mattr_writer :username mattr_writer :password mattr_writer :send_emails @@ -15,6 +16,12 @@ module SolidErrors mattr_writer :email_subject_prefix class << self + # use method instead of attr_accessor to ensure + # this works if variable set after SolidErrors is loaded + def full_backtrace? + @full_backtrace ||= ENV["SOLIDERRORS_FULL_BACKTRACE"] || @@full_backtrace || false + end + # use method instead of attr_accessor to ensure # this works if variable set after SolidErrors is loaded def username diff --git a/lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb b/lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb deleted file mode 100644 index a889bf8..0000000 --- a/lib/solid_errors/db/migrate/add_prev_resolved_at_to_solid_errors.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddPrevResolvedAtToSolidErrors < ActiveRecord::Migration[6.1] - def change - add_column :solid_errors, :prev_resolved_at, :datetime - end -end diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index 04f27a5..80885c2 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -1,25 +1,25 @@ module SolidErrors class Subscriber IGNORED_ERRORS = ["ActionController::RoutingError", - "AbstractController::ActionNotFound", - "ActionController::MethodNotAllowed", - "ActionController::UnknownHttpMethod", - "ActionController::NotImplemented", - "ActionController::UnknownFormat", - "ActionController::InvalidAuthenticityToken", - "ActionController::InvalidCrossOriginRequest", - "ActionDispatch::Http::Parameters::ParseError", - "ActionController::BadRequest", - "ActionController::ParameterMissing", - "ActiveRecord::RecordNotFound", - "ActionController::UnknownAction", - "ActionDispatch::Http::MimeNegotiation::InvalidType", - "Rack::QueryParser::ParameterTypeError", - "Rack::QueryParser::InvalidParameterError", - "CGI::Session::CookieStore::TamperedWithCookie", - "Mongoid::Errors::DocumentNotFound", - "Sinatra::NotFound", - "Sidekiq::JobRetry::Skip"].map(&:freeze).freeze + "AbstractController::ActionNotFound", + "ActionController::MethodNotAllowed", + "ActionController::UnknownHttpMethod", + "ActionController::NotImplemented", + "ActionController::UnknownFormat", + "ActionController::InvalidAuthenticityToken", + "ActionController::InvalidCrossOriginRequest", + "ActionDispatch::Http::Parameters::ParseError", + "ActionController::BadRequest", + "ActionController::ParameterMissing", + "ActiveRecord::RecordNotFound", + "ActionController::UnknownAction", + "ActionDispatch::Http::MimeNegotiation::InvalidType", + "Rack::QueryParser::ParameterTypeError", + "Rack::QueryParser::InvalidParameterError", + "CGI::Session::CookieStore::TamperedWithCookie", + "Mongoid::Errors::DocumentNotFound", + "Sinatra::NotFound", + "Sidekiq::JobRetry::Skip"].map(&:freeze).freeze def report(error, handled:, severity:, context:, source: nil) return if ignore_by_class?(error.class.name) @@ -37,9 +37,13 @@ def report(error, handled:, severity:, context:, source: nil) record = SolidErrors::Error.create!(error_attributes.merge(fingerprint: fingerprint)) end + backtrace_cleaner = ActiveSupport::BacktraceCleaner.new + backtrace_cleaner.add_silencer { |line| /puma|rubygems|gems/.match?(line) } + backtrace = SolidErrors.full_backtrace? ? backtrace_cleaner.clean(error.backtrace) : error.backtrace + SolidErrors::Occurrence.create( error_id: record.id, - backtrace: error.backtrace.join("\n"), + backtrace: backtrace.join("\n"), context: s(context) ) end diff --git a/lib/tasks/solid_errors_tasks.rake b/lib/tasks/solid_errors_tasks.rake deleted file mode 100644 index 46ad79e..0000000 --- a/lib/tasks/solid_errors_tasks.rake +++ /dev/null @@ -1,17 +0,0 @@ -namespace :solid_errors do - desc "Copy and run migrations from Solid Errors" - task install_migrations: :environment do - source = File.expand_path("../../solid_errors/db/migrate", __FILE__) - destination = Rails.root.join('db', 'migrate') - - Dir.children(source).each do |migration| - timestamp = Time.now.strftime('%Y%m%d%H%M%S') - filename_with_timestamp = "#{timestamp}_#{migration}" - - if Dir.glob("#{destination}/*#{migration}").empty? - puts "Copying #{migration} to #{filename_with_timestamp} from Solid Errors to Rails application" - FileUtils.cp(File.join(source, migration), File.join(destination, filename_with_timestamp)) - end - end - end -end From 59db8e1c4e3bd8a1d249951974ab59283d91ded2 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 2 Dec 2024 13:39:18 -0700 Subject: [PATCH 22/33] Bumping version --- lib/solid_errors/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index 5079125..0fb8883 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.6.1" + VERSION = "0.7.0" end From 28f610abab8e91150fc9c1434f5d059f03eea5bf Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Mon, 2 Dec 2024 13:42:58 -0700 Subject: [PATCH 23/33] Upgrade solid_errors gem and fix backtrace logic. Updated solid_errors gem from version 0.6.1 to 0.7.0 in Gemfile.lock. Adjusted the backtrace logic in subscriber.rb to correctly apply the backtrace cleaner only when needed. --- Gemfile.lock | 2 +- lib/solid_errors/subscriber.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9a997c5..3c4ef02 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.6.1) + solid_errors (0.7.0) actionmailer (>= 7.0) actionpack (>= 7.0) actionview (>= 7.0) diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index 80885c2..65634eb 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -39,7 +39,7 @@ def report(error, handled:, severity:, context:, source: nil) backtrace_cleaner = ActiveSupport::BacktraceCleaner.new backtrace_cleaner.add_silencer { |line| /puma|rubygems|gems/.match?(line) } - backtrace = SolidErrors.full_backtrace? ? backtrace_cleaner.clean(error.backtrace) : error.backtrace + backtrace = SolidErrors.full_backtrace? ? error.backtrace : backtrace_cleaner.clean(error.backtrace) SolidErrors::Occurrence.create( error_id: record.id, From fdf890649e57aed51b90c7b8b47f1aabd9f8a4ae Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Thu, 27 Mar 2025 09:14:12 -0600 Subject: [PATCH 24/33] Update backtrace regex, Ruby versions, and gem dependencies Refine backtrace regex to support both single and double quotes. Add Ruby 3.4.0 to CI workflow for testing. Upgrade Nokogiri and SQLite3 to newer versions to ensure compatibility and performance improvements. --- .github/workflows/main.yml | 1 + Gemfile.lock | 8 ++++---- app/models/solid_errors/backtrace_line.rb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 20e273a..bfafd10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,6 +17,7 @@ jobs: - '3.1.4' - '3.2.3' - '3.3.0' + - '3.4.0' steps: - uses: actions/checkout@v4 - name: Set up Ruby diff --git a/Gemfile.lock b/Gemfile.lock index 3c4ef02..45c7818 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,9 +97,9 @@ GEM timeout net-smtp (0.5.0) net-protocol - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.18.6-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.18.6-x86_64-linux-gnu) racc (~> 1.4) parallel (1.26.3) parser (3.3.4.2) @@ -158,8 +158,8 @@ GEM rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) securerandom (0.3.1) - sqlite3 (2.0.4-arm64-darwin) - sqlite3 (2.0.4-x86_64-linux-gnu) + sqlite3 (2.6.0-arm64-darwin) + sqlite3 (2.6.0-x86_64-linux-gnu) standard (1.40.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) diff --git a/app/models/solid_errors/backtrace_line.rb b/app/models/solid_errors/backtrace_line.rb index 8ac75c5..8d8194b 100644 --- a/app/models/solid_errors/backtrace_line.rb +++ b/app/models/solid_errors/backtrace_line.rb @@ -2,7 +2,7 @@ module SolidErrors # adapted from: https://github.com/honeybadger-io/honeybadger-ruby/blob/master/lib/honeybadger/backtrace.rb class BacktraceLine # Backtrace line regexp (optionally allowing leading X: for windows support). - INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$} + INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in [`']([^']+)')?$} STRING_EMPTY = "".freeze GEM_ROOT = "[GEM_ROOT]".freeze PROJECT_ROOT = "[PROJECT_ROOT]".freeze From 5d0faf39eb16900417f0d4ffd419639c545c3d3b Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Tue, 10 Jun 2025 11:21:40 -0600 Subject: [PATCH 25/33] Update Gemfile.lock to remove actionmailer dependency The actionmailer dependency was removed from solid_errors in the Gemfile.lock. This change ensures compatibility and reflects the updated requirements of the gem. Corrected an incorrect choice in a merge conflict --- Gemfile.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4233e6f..2282776 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,6 @@ PATH remote: . specs: solid_errors (0.6.1) - actionmailer (>= 7.0) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) From 40e4603e5ff2ee711f6b8c93f3f8fbab16a76378 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Sun, 22 Jun 2025 10:52:41 -0600 Subject: [PATCH 26/33] Fix incorrect variable usage in backtrace handling Corrected the use of the `error.backtrace` variable to `backtrace` in `lib/solid_errors/subscriber.rb` to ensure proper handling of backtrace data. --- lib/solid_errors/subscriber.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index 4607eb8..a216106 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -43,7 +43,7 @@ def report(error, handled:, severity:, context:, source: nil) SolidErrors::Occurrence.create( error_id: record.id, - backtrace: error.backtrace&.join("\n"), + backtrace: backtrace&.join("\n"), context: s(context) ) end From 4d80f4e799eb08e40dbc142e7489ed3e4fe0146a Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 18:51:49 -0600 Subject: [PATCH 27/33] Bump version to 0.7.1, refine email handling and optimize resolved error updates. - Added `should_send_email?` method for improved email sending logic. - Removed `prev_resolved_at` column from the database schema. - Simplified error occurrence updates by dropping `prev_resolved_at` usage. - Ensured `resolved_at` is reset when sending email notifications. --- app/models/solid_errors/occurrence.rb | 7 ++++++- .../solid_errors/install/templates/db/errors_schema.rb | 1 - lib/solid_errors/subscriber.rb | 2 +- lib/solid_errors/version.rb | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 4f034e0..e38f575 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -2,7 +2,7 @@ module SolidErrors class Occurrence < Record belongs_to :error, class_name: "SolidErrors::Error" - after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? } + after_create_commit :send_email, if: :should_send_email? after_create_commit :clear_resolved_errors, if: :should_clear_resolved_errors? # The parsed exception backtrace. Lines in this backtrace that are from installed gems @@ -23,6 +23,7 @@ def parse_backtrace(backtrace) def send_email ErrorMailer.error_occurred(self).deliver_later + self.update_column(:resolved_at, nil) end def clear_resolved_errors @@ -45,5 +46,9 @@ def should_clear_resolved_errors? true end + + def should_send_email? + SolidErrors.send_emails? && SolidErrors.email_to.present? && error.occurrences.where.not(resolved_at: nil).any? + end end end diff --git a/lib/generators/solid_errors/install/templates/db/errors_schema.rb b/lib/generators/solid_errors/install/templates/db/errors_schema.rb index 1b1dc29..1e60891 100644 --- a/lib/generators/solid_errors/install/templates/db/errors_schema.rb +++ b/lib/generators/solid_errors/install/templates/db/errors_schema.rb @@ -7,7 +7,6 @@ t.text "severity", null: false t.text "source" t.datetime "resolved_at" - t.datetime "prev_resolved_at" t.string "fingerprint", limit: 64, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/lib/solid_errors/subscriber.rb b/lib/solid_errors/subscriber.rb index a216106..2be7e5b 100644 --- a/lib/solid_errors/subscriber.rb +++ b/lib/solid_errors/subscriber.rb @@ -32,7 +32,7 @@ def report(error, handled:, severity:, context:, source: nil) } fingerprint = Digest::SHA256.hexdigest(error_attributes.values.join) if (record = SolidErrors::Error.find_by(fingerprint: fingerprint)) - record.update!(prev_resolved_at: record.resolved_at, resolved_at: nil, updated_at: Time.now) + record.update!(resolved_at: nil, updated_at: Time.now) else record = SolidErrors::Error.create!(error_attributes.merge(fingerprint: fingerprint)) end diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index 0fb8883..12515a6 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.0" + VERSION = "0.7.1" end From 6c28ace35c850039766e89e22aa3a867aee00738 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 19:03:32 -0600 Subject: [PATCH 28/33] Update `should_send_email?` logic and bump `solid_errors` to 0.7.2 - Refined `should_send_email?` to check `resolved_at` directly for accuracy. - Updated Gemfile.lock to use `solid_errors` version 0.7.2. --- Gemfile.lock | 2 +- app/models/solid_errors/occurrence.rb | 2 +- lib/solid_errors/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e4aa820..f0e5c77 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.7.0) + solid_errors (0.7.1) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index e38f575..21a704c 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -48,7 +48,7 @@ def should_clear_resolved_errors? end def should_send_email? - SolidErrors.send_emails? && SolidErrors.email_to.present? && error.occurrences.where.not(resolved_at: nil).any? + SolidErrors.send_emails? && SolidErrors.email_to.present? && self.resolved_at.nil? end end end diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index 12515a6..599da8d 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.1" + VERSION = "0.7.2" end From b4be15a9a710c63362c056d7548f83878c033113 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 19:04:53 -0600 Subject: [PATCH 29/33] Update `should_send_email?` logic and bump `solid_errors` to 0.7.3 - Adjusted `should_send_email?` to reference `error.resolved_at` for consistency. - Updated Gemfile.lock to use `solid_errors` version 0.7.3. - Incremented gem version to 0.7.3. --- Gemfile.lock | 2 +- app/models/solid_errors/occurrence.rb | 2 +- lib/solid_errors/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f0e5c77..45c96ef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.7.1) + solid_errors (0.7.2) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 21a704c..c8686d4 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -48,7 +48,7 @@ def should_clear_resolved_errors? end def should_send_email? - SolidErrors.send_emails? && SolidErrors.email_to.present? && self.resolved_at.nil? + SolidErrors.send_emails? && SolidErrors.email_to.present? && error.resolved_at.nil? end end end diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index 599da8d..cd8b533 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.2" + VERSION = "0.7.3" end From 870b713d78eeb46d990e0348234269ef4d63f72f Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 19:07:07 -0600 Subject: [PATCH 30/33] Update `should_send_email?` logic and bump `solid_errors` to 0.7.4 - Refined `should_send_email?` to use `error.resolved?` for clarity. - Updated Gemfile.lock to use `solid_errors` version 0.7.4. - Incremented gem version to 0.7.4. --- Gemfile.lock | 2 +- app/models/solid_errors/occurrence.rb | 2 +- lib/solid_errors/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 45c96ef..d5b1eef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.7.2) + solid_errors (0.7.3) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index c8686d4..024f71d 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -48,7 +48,7 @@ def should_clear_resolved_errors? end def should_send_email? - SolidErrors.send_emails? && SolidErrors.email_to.present? && error.resolved_at.nil? + SolidErrors.send_emails? && SolidErrors.email_to.present? && !error.resolved? end end end diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index cd8b533..546e7d8 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.3" + VERSION = "0.7.4" end From 2f634fc7818a76eb7bf321627c06872cd14b36e0 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 19:08:48 -0600 Subject: [PATCH 31/33] Update `send_email` logic and bump `solid_errors` to 0.7.5 - Fixed incorrect reference of `self` in `send_email`, replacing it with `error` for consistency. - Updated Gemfile.lock to use `solid_errors` version 0.7.5. - Incremented gem version to 0.7.5. --- Gemfile.lock | 2 +- app/models/solid_errors/occurrence.rb | 2 +- lib/solid_errors/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d5b1eef..442c1d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.7.3) + solid_errors (0.7.4) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index 024f71d..ec30ebf 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -23,7 +23,7 @@ def parse_backtrace(backtrace) def send_email ErrorMailer.error_occurred(self).deliver_later - self.update_column(:resolved_at, nil) + error.update_column(:resolved_at, nil) end def clear_resolved_errors diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index 546e7d8..bf0b2e8 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.4" + VERSION = "0.7.5" end From 7f8005cd99eb41c6a703d8860759100e7281d950 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Wed, 9 Jul 2025 19:11:27 -0600 Subject: [PATCH 32/33] Bump `solid_errors` to 0.7.5 in Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 442c1d3..8079d3e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_errors (0.7.4) + solid_errors (0.7.5) actionpack (>= 7.0) actionview (>= 7.0) activerecord (>= 7.0) From 83d4b6c137773b9d701d35e619f13e03e24795e9 Mon Sep 17 00:00:00 2001 From: Craig Kaminsky Date: Tue, 12 Aug 2025 13:27:17 -0600 Subject: [PATCH 33/33] Update `send_email` and `should_send_email?` logic, add `prev_resolved_at`, and bump `solid_errors` to 0.8.0 - Refined `should_send_email?` to better handle resolved errors and new occurrences. - Removed `resolved_at` reset logic in `send_email` for simplification. - Introduced `prev_resolved_at` column to track previous resolution timestamps. - Incremented gem version to 0.8.0. --- app/models/solid_errors/occurrence.rb | 14 +++++++++++--- .../install/templates/db/errors_schema.rb | 2 ++ lib/solid_errors/version.rb | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/solid_errors/occurrence.rb b/app/models/solid_errors/occurrence.rb index ec30ebf..1c9bc0a 100644 --- a/app/models/solid_errors/occurrence.rb +++ b/app/models/solid_errors/occurrence.rb @@ -2,7 +2,7 @@ module SolidErrors class Occurrence < Record belongs_to :error, class_name: "SolidErrors::Error" - after_create_commit :send_email, if: :should_send_email? + after_create :send_email, if: :should_send_email? after_create_commit :clear_resolved_errors, if: :should_clear_resolved_errors? # The parsed exception backtrace. Lines in this backtrace that are from installed gems @@ -23,7 +23,6 @@ def parse_backtrace(backtrace) def send_email ErrorMailer.error_occurred(self).deliver_later - error.update_column(:resolved_at, nil) end def clear_resolved_errors @@ -48,7 +47,16 @@ def should_clear_resolved_errors? end def should_send_email? - SolidErrors.send_emails? && SolidErrors.email_to.present? && !error.resolved? + return false unless SolidErrors.send_emails? && SolidErrors.email_to.present? + + # Check if resolved_at changed from a datetime to nil (resolved error reoccurred) + resolved_at_changes = error.previous_changes['resolved_at'] + resolved_error_reoccurred = resolved_at_changes&.first.present? && resolved_at_changes&.last.nil? + + # Check if this is the first occurrence of a brand new error + first_occurrence = error.occurrences.count == 1 + + resolved_error_reoccurred || first_occurrence end end end diff --git a/lib/generators/solid_errors/install/templates/db/errors_schema.rb b/lib/generators/solid_errors/install/templates/db/errors_schema.rb index 1e60891..055c5d6 100644 --- a/lib/generators/solid_errors/install/templates/db/errors_schema.rb +++ b/lib/generators/solid_errors/install/templates/db/errors_schema.rb @@ -7,11 +7,13 @@ t.text "severity", null: false t.text "source" t.datetime "resolved_at" + t.datetime "prev_resolved_at" t.string "fingerprint", limit: 64, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["fingerprint"], name: "index_solid_errors_on_fingerprint", unique: true t.index ["resolved_at"], name: "index_solid_errors_on_resolved_at" + t.index ["prev_resolved_at"], name: "index_solid_errors_on_prev_resolved_at" end create_table "solid_errors_occurrences", force: :cascade do |t| diff --git a/lib/solid_errors/version.rb b/lib/solid_errors/version.rb index bf0b2e8..2e9d29e 100644 --- a/lib/solid_errors/version.rb +++ b/lib/solid_errors/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SolidErrors - VERSION = "0.7.5" + VERSION = "0.8.0" end