首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Rails 統一例外處理與錯誤通知機制:rescue_from、自定義錯誤頁面、郵件報警完整實作

     編輯:Java 相關知識

生產環境 Rails 應用需統一處理未捕獲例外:顯示友善錯誤頁面、記錄詳細日誌、發送郵件通知開發團隊。本文展示在 ApplicationController 使用 rescue_from 分類處理 ActiveRecord、路由、權限、模板等異常,並整合郵件通知與動態路由捕獲 404。

核心架構:ApplicationController 例外處理

class ApplicationController < ActionController::Base
  # 例外分類常數
  AR_ERROR_CLASSES = [
    ActiveRecord::RecordNotFound,
    ActiveRecord::StatementInvalid
  ].freeze

  ERROR_CLASSES = [
    NameError, NoMethodError, RuntimeError,
    ActionView::TemplateError,
    ActiveRecord::StaleObjectError,
    ActionController::RoutingError,
    ActionController::UnknownController,
    AbstractController::ActionNotFound,
    ActionController::MethodNotAllowed,
    ActionController::InvalidAuthenticityToken
  ].freeze

  ACCESS_DENIED_CLASSES = [CanCan::AccessDenied].freeze

  # 僅生產環境啟用統一處理 (開發環境保留詳細錯誤頁)
  if Rails.env.production?
    rescue_from *AR_ERROR_CLASSES,    with: :render_ar_error
    rescue_from *ERROR_CLASSES,       with: :render_error
    rescue_from *ACCESS_DENIED_CLASSES, with: :render_access_denied
  end

  # 捕獲未匹配路由 (需配合 routes.rb 設定)
  def raise_not_found!
    raise ActionController::RoutingError.new("No route matches #{params[:unmatched_route]}")
  end

  # === 處理器實作 ===
  def render_ar_error(exception)
    handle_exception(exception, 'ActiveRecord')
  end

  def render_error(exception)
    handle_exception(exception, 'Application')
  end

  def render_access_denied(exception)
    handle_exception(exception, 'AccessDenied')
  end

  private

  def handle_exception(exception, exception_class)
    # 1. 記錄詳細日誌 (Rails.logger 自動包含回溯)
    Rails.logger.error "[#{exception_class}] #{exception.class}: #{exception.message}"
    Rails.logger.error exception.backtrace.join("\n")

    # 2. 發送郵件通知 (非同步推薦用 ActiveJob)
    ExceptionNotifierMailer.notify(exception, exception_class, request).deliver_later

    # 3. 渲染對應錯誤頁面
    case exception_class
    when 'ActiveRecord'
      render template: 'errors/ar_error', status: :not_found
    when 'AccessDenied'
      render template: 'errors/access_denied', status: :forbidden
    else
      render template: 'errors/internal_error', status: :internal_server_error
    end
  end
end

路由配置:捕獲 404 導向統一處理

# config/routes.rb
Rails.application.routes.draw do
  # ... 正常路由 ...

  # Rails 3/4: 最後捕獲所有未匹配路由
  # Rails 3: match '*unmatched_route', to: 'application#raise_not_found!', via: :all
  # Rails 4/5/6/7:
  get '*unmatched_route', to: 'application#raise_not_found!'
  # 或 match '*unmatched_route', to: 'application#raise_not_found!', via: :all
end

郵件通知實作:ExceptionNotifierMailer

# app/mailers/exception_notifier_mailer.rb
class ExceptionNotifierMailer < ApplicationMailer
  default to: -> { Rails.application.config.exception_recipients },
          from: 'noreply@myapp.com'

  def notify(exception, exception_class, request)
    @exception = exception
    @exception_class = exception_class
    @request = request
    @timestamp = Time.current
    @host = request&.base_url
    @user = current_user # 若有認證系統

    mail(subject: "[ERROR][#{Rails.env}] #{exception_class}: #{exception.class}")
  end
end

# app/views/exception_notifier_mailer/notify.html.erb

Exception Details

Class: <%= @exception_class %> (<%= @exception.class %>)

Message: <%= @exception.message %>

Time: <%= @timestamp %>

URL: <%= @request&.original_url %>

Method: <%= @request&.request_method %>

Params: <%= @request&.params&.inspect %>

User: <%= @user&.inspect %>

Backtrace

<%= @exception.backtrace.join("\n") %>

錯誤頁面樣板


資料未找到 (404)

請求的資源不存在或已被移除。

<%= link_to '返回首頁', root_path %>

權限不足 (403)

您沒有權限存取此資源。

系統錯誤 (500)

伺服器發生錯誤,我們已收到通知並將盡快修復。

<%= link_to '返回首頁', root_path %>

進階:整合 exception_notification Gem (推薦)

手寫郵件通知易遺漏細節,推薦使用成熟 Gem:

# Gemfile
gem 'exception_notification'

# config/initializers/exception_notification.rb
Rails.application.config.middleware.use ExceptionNotification::Rack,
  email: {
    delimiter: "\n",
    sender_address: %{"Error Notifier" },
    exception_recipients: %w{dev@myapp.com ops@myapp.com},
    email_prefix: "[ERROR][#{Rails.env}] ",
    sections: %w{request session environment backtrace}
  },
  slack: {
    webhook_url: ENV['SLACK_WEBHOOK_URL'],
    channel: '#exceptions',
    additional_parameters: { icon_emoji: ':fire:' }
  }

關鍵最佳實踐總結

實踐說明
環境區分if Rails.env.production? 僅生產環境攔截,開發/測試保留詳細錯誤頁
例外分類依業務語意分群 (AR、權限、路由、通用),對應不同 HTTP 狀態碼與頁面
非同步通知deliver_later (ActiveJob) 避免阻塞回應,郵件/Slack/Webhook 並行發送
資訊豐富度郵件包含:請求 URL/參數/Session/用戶/完整回溯/環境變數
避免洩漏錯誤頁面不顯示堆疊/參數/內部結構,僅友善提示
監控整合同時送至 Sentry/Rollbar/Datadog/ELK 進行聚合分析、告警去重

常見陷阱

  • rescue_from 繼承鏈:子 Controller 可覆蓋父類處理器,順序為「最近定義優先」
  • 路由捕獲 *unmatched_route 必須置於 路由檔案最末尾
  • API 模式下應回傳 JSON 而非 HTML:render json: {error: 'Not Found'}, status: :not_found
  • 大流量時郵件風暴:需加入節流 (如 5 分鐘同類異常僅通知一次) 或改用監控平台告警

原文標題錯誤提示:原標題為「java解析xml之jdom解析xml示例分享」,但實際內容為 Rails 例外處理與錯誤通知。本文已依實際內容重寫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved