From 64fc0f1f1b4f1adc161a1c46640547df92b24595 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 1 Dec 2025 12:33:01 -0500 Subject: [PATCH] better error handling (#677) * swap honeybadger for sentry * better error pages --- Gemfile | 2 - Gemfile.lock | 4 - .../api/internal/magic_links_controller.rb | 2 +- app/controllers/application_controller.rb | 13 +- app/controllers/errors_controller.rb | 43 ++++++ app/controllers/sessions_controller.rb | 8 +- app/views/errors/show.html.erb | 22 +++ app/views/layouts/errors.html.erb | 25 ++++ config/application.rb | 1 + config/honeybadger.yml | 36 ----- config/initializers/autotuner.rb | 8 +- config/initializers/honeybadger.rb | 55 -------- config/routes.rb | 6 + public/400.html | 126 ------------------ public/404.html | 120 ----------------- public/406-unsupported-browser.html | 126 ------------------ public/422.html | 124 ----------------- public/500.html | 125 ----------------- 18 files changed, 117 insertions(+), 729 deletions(-) create mode 100644 app/controllers/errors_controller.rb create mode 100644 app/views/errors/show.html.erb create mode 100644 app/views/layouts/errors.html.erb delete mode 100644 config/honeybadger.yml delete mode 100644 config/initializers/honeybadger.rb delete mode 100644 public/400.html delete mode 100644 public/404.html delete mode 100644 public/406-unsupported-browser.html delete mode 100644 public/422.html delete mode 100644 public/500.html diff --git a/Gemfile b/Gemfile index 8a1ad86..ba39f40 100644 --- a/Gemfile +++ b/Gemfile @@ -37,8 +37,6 @@ gem "solid_cable" gem "stackprof" gem "sentry-ruby" gem "sentry-rails" -# Trying out sentry alternative -gem "honeybadger" gem "good_job" diff --git a/Gemfile.lock b/Gemfile.lock index dbf7b7f..321497d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -228,9 +228,6 @@ GEM groupdate (6.7.0) activesupport (>= 7.1) hashie (5.0.0) - honeybadger (6.1.3) - logger - ostruct htmlcompressor (0.4.0) http (5.3.1) addressable (~> 2.8) @@ -609,7 +606,6 @@ DEPENDENCIES flipper-ui geocoder good_job - honeybadger htmlcompressor (~> 0.4.0) http importmap-rails diff --git a/app/controllers/api/internal/magic_links_controller.rb b/app/controllers/api/internal/magic_links_controller.rb index 84bac18..661a99d 100644 --- a/app/controllers/api/internal/magic_links_controller.rb +++ b/app/controllers/api/internal/magic_links_controller.rb @@ -41,7 +41,7 @@ module Api existing: } rescue => e - Honeybadger.notify(e, context: { slack_uid: slack_uid, email: email, params: params.to_unsafe_h }) + Sentry.capture_exception(e, extra: { slack_uid: slack_uid, email: email, params: params.to_unsafe_h }) render json: { error: "internal error creating magic link" }, status: 500 end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e186373..08dfc4a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,6 @@ class ApplicationController < ActionController::Base before_action :set_paper_trail_whodunnit - before_action :honeybadger_context, if: :current_user + before_action :sentry_context, if: :current_user before_action :initialize_cache_counters before_action :try_rack_mini_profiler_enable before_action :track_request @@ -25,11 +25,14 @@ class ApplicationController < ActionController::Base @activities = PublicActivity::Activity.limit(25).order(created_at: :desc).includes(:owner, :trackable) end - def honeybadger_context - Honeybadger.context( - user_id: current_user.id, + def sentry_context + Sentry.set_user( + id: current_user.id, + username: current_user.username + ) + Sentry.set_extras( user_agent: request.user_agent, - ip_address: request.headers["CF-Connecting-IP"] || request.remote_ip, + ip_address: request.headers["CF-Connecting-IP"] || request.remote_ip ) end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb new file mode 100644 index 0000000..84c0d0e --- /dev/null +++ b/app/controllers/errors_controller.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class ErrorsController < ApplicationController + skip_before_action :verify_authenticity_token + + def bad_request + @status_code = 400 + @title = "Bad Request" + @message = "The server cannot process your request due to invalid syntax." + render_error + end + + def not_found + @status_code = 404 + @title = "Page Not Found" + @message = "The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved." + render_error + end + + def unprocessable_entity + @status_code = 422 + @title = "Unprocessable Content" + @message = "The request was well-formed but unable to be followed due to semantic errors." + render_error + end + + def internal_server_error + @status_code = 500 + @title = "Internal Server Error" + @message = "Something went wrong on our end, but we are looking into it!" + render_error + end + + private + + def render_error + render "errors/show", status: @status_code, layout: error_layout + end + + def error_layout + "errors" + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 073ca3c..156069f 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -12,8 +12,8 @@ class SessionsController < ApplicationController if params[:error].present? Rails.logger.error "Slack OAuth error: #{params[:error]}" - uuid = Honeybadger.notify("Slack OAuth error: #{params[:error]}") - redirect_to root_path, alert: "Failed to authenticate with Slack. Error ID: #{uuid}" + Sentry.capture_message("Slack OAuth error: #{params[:error]}") + redirect_to root_path, alert: "Failed to authenticate with Slack. Error ID: #{Sentry.last_event_id}" return end @@ -67,8 +67,8 @@ class SessionsController < ApplicationController if params[:error].present? Rails.logger.error "GitHub OAuth error: #{params[:error]}" - uuid = Honeybadger.notify("GitHub OAuth error: #{params[:error]}") - redirect_to my_settings_path, alert: "Failed to authenticate with GitHub. Error ID: #{uuid}" + Sentry.capture_message("GitHub OAuth error: #{params[:error]}") + redirect_to my_settings_path, alert: "Failed to authenticate with GitHub. Error ID: #{Sentry.last_event_id}" return end diff --git a/app/views/errors/show.html.erb b/app/views/errors/show.html.erb new file mode 100644 index 0000000..13c8dda --- /dev/null +++ b/app/views/errors/show.html.erb @@ -0,0 +1,22 @@ +
+
+
<%= @status_code %>
+

<%= @title %>

+
+ +
+

<%= @message %>

+
+ +
+ <%= link_to "Go Home", root_path, class: "px-6 py-3 bg-primary text-white rounded-lg font-medium hover:bg-red-600 transition-colors" %> + +
+ +

+ If this problem persists, please contact us on + <%= link_to "Slack", "https://hackclub.slack.com", class: "text-primary hover:underline", target: "_blank" %>. +

+
diff --git a/app/views/layouts/errors.html.erb b/app/views/layouts/errors.html.erb new file mode 100644 index 0000000..c2852e3 --- /dev/null +++ b/app/views/layouts/errors.html.erb @@ -0,0 +1,25 @@ + +" data-theme="dark"> + + <%= @title %> - Hackatime + + + + + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= favicon_link_tag asset_path('favicon.png'), type: 'image/png' %> + <%= stylesheet_link_tag :app %> + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %> + + + +
+ <%= yield %> +
+ + diff --git a/config/application.rb b/config/application.rb index 2f1689f..0891d40 100644 --- a/config/application.rb +++ b/config/application.rb @@ -53,5 +53,6 @@ module Harbor config.middleware.use HtmlCompressor::Rack config.middleware.use Rack::Attack + config.exceptions_app = routes end end diff --git a/config/honeybadger.yml b/config/honeybadger.yml deleted file mode 100644 index 055cd6f..0000000 --- a/config/honeybadger.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# For more options, see https://docs.honeybadger.io/lib/ruby/gem-reference/configuration - -api_key: 'hbp_IET1u9vKrC7IWjt45arAsfguTrGSFR1bSxEq' - -# The environment your app is running in. -env: "<%= Rails.env %>" - -# The absolute path to your project folder. -root: "<%= Rails.root.to_s %>" - -# Honeybadger won't report errors in these environments. -development_environments: -- test -- development -- cucumber -- production # temporary disable - -# By default, Honeybadger won't report errors in the development_environments. -# You can override this by explicitly setting report_data to true or false. -# report_data: true - -# The current Git revision of your project. Defaults to the last commit hash. -# revision: null - -# Enable verbose debug logging (useful for troubleshooting). -debug: false - -# Ignore noisy errors -exceptions: - ignore: - - GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError - -# disable insights lol -insights: - enabled: false diff --git a/config/initializers/autotuner.rb b/config/initializers/autotuner.rb index 2a99318..999cd38 100644 --- a/config/initializers/autotuner.rb +++ b/config/initializers/autotuner.rb @@ -2,4 +2,10 @@ # between 0 and 1.0 to sample on a portion of instances. Autotuner.enabled = true -# The rest is handled by the honeybadger gem. +Autotuner.reporter = proc do |report| + Sentry.capture_message( + "Autotuner Suggestion", + level: :info, + extra: { report: report.to_s } + ) +end diff --git a/config/initializers/honeybadger.rb b/config/initializers/honeybadger.rb deleted file mode 100644 index 95d0fca..0000000 --- a/config/initializers/honeybadger.rb +++ /dev/null @@ -1,55 +0,0 @@ -# Honeybadger programmatic error filtering to prevent rate limit exhaustion -Honeybadger.configure do |config| - @error_counts = Hash.new { |hash, key| hash[key] = { hourly: [], daily: [] } } - - # Rate limiting configuration - MAX_ERRORS_PER_HOUR = 10 - MAX_ERRORS_PER_DAY = 50 - - config.before_notify do |notice| - if notice.error_class == "Norairrecord::Error" && notice.error_message&.include?("HTTP 429") - return false - end - - error_index = generate_error_index notice - - should_ignore = rate_limit_exceeded? error_index - - record_error_occurrence error_index unless should_ignore - - !should_ignore - end - - private - - def generate_error_index(notice) - if notice.backtrace&.any? - first_stack_line = notice.backtrace.first - "#{notice.error_class}:#{first_stack_line}" - else - controller = notice.context[:controller] - action = notice.context[:action] - "#{notice.error_class}:#{controller}:#{action}" - end - end - - def rate_limit_exceeded?(error_index) - now = Time.current - one_hour_ago = now - 1.hour - one_day_ago = now - 1.day - - @error_counts[error_index][:hourly].reject! { |timestamp| timestamp < one_hour_ago } - @error_counts[error_index][:daily].reject! { |timestamp| timestamp < one_day_ago } - - hourly_count = @error_counts[error_index][:hourly].size - daily_count = @error_counts[error_index][:daily].size - - hourly_count >= MAX_ERRORS_PER_HOUR || daily_count >= MAX_ERRORS_PER_DAY - end - - def record_error_occurrence(error_index) - now = Time.current - @error_counts[error_index][:hourly] << now - @error_counts[error_index][:daily] << now - end -end diff --git a/config/routes.rb b/config/routes.rb index 5a5439a..65c8cd8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -205,4 +205,10 @@ Rails.application.routes.draw do # SEO routes get "/sitemap.xml", to: "sitemap#sitemap", defaults: { format: "xml" } + + # fuck ups + match "/400", to: "errors#bad_request", via: :all + match "/404", to: "errors#not_found", via: :all + match "/422", to: "errors#unprocessable_entity", via: :all + match "/500", to: "errors#internal_server_error", via: :all end diff --git a/public/400.html b/public/400.html deleted file mode 100644 index b7c2b3e..0000000 --- a/public/400.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - The server cannot process the request due to a client error (400 Bad Request) - - - - - - - - - - - - - -
-
- -
-
-

The request you sent is invalid or corrupt.
If you're the application owner check the logs for more information.

-

- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.

- Can you take a screenshot of this page and email max@hackclub.com - describing what you did to get here? Please make sure the current URL - is visible in the screenshot.

-

-

- Error ID: - (click to copy) -

-
-
- - - - diff --git a/public/404.html b/public/404.html deleted file mode 100644 index 83c1f85..0000000 --- a/public/404.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - The page you were looking for doesn't exist (404 Not found) - - - - - - - - - - - - - -
-
- -
-
-

The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.

-

- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.

- Can you take a screenshot of this page and email max@hackclub.com describing what you did to get here?

- Also please make sure the current URL is visible in the screenshot. -

-
-
- - - - diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html deleted file mode 100644 index 4100159..0000000 --- a/public/406-unsupported-browser.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - Your browser is not supported (406 Not Acceptable) - - - - - - - - - - - - - -
-
- -
-
-

Your browser is not supported.
Please upgrade to a modern browser, such as the latest version of Chrome, Firefox, Safari, or Edge.

-

- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.

- Can you take a screenshot of this page and email max@hackclub.com - describing what you did to get here? Please make sure the current URL - is visible in the screenshot.

-

-

- Error ID: - (click to copy) -

-
-
- - - - diff --git a/public/422.html b/public/422.html deleted file mode 100644 index 28cac98..0000000 --- a/public/422.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - The change you wanted was rejected (422 Unprocessable Entity) - - - - - - - - - - - - - -
-
- -
-
-

- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.

- Can you take a screenshot of this page and email max@hackclub.com describing what you did to get here?

- Also please make sure the current URL is visible in the screenshot. -

-

- Error ID: - (click to copy) -

-
-
- - - - diff --git a/public/500.html b/public/500.html deleted file mode 100644 index c85f8a2..0000000 --- a/public/500.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - We're sorry, but something went wrong (500 Internal Server Error) - - - - - - - - - - - - - -
-
- -
-
-

- Hey! Congrats! You probably just found a bug!
- This is the Hackatime v2 beta, after all.

- Can you take a screenshot of this page and email max@hackclub.com - describing what you did to get here? Please make sure the current URL - is visible in the screenshot.

-

-

- Error ID: - (click to copy) -

-
-
- - - -