Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/logtail-rails/error_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ class ErrorEvent < Logtail::Integrations::Rack::Middleware
# We determine this when the app loads to avoid the overhead on a per request basis.
EXCEPTION_WRAPPER_TAKES_CLEANER = defined?(::ActionDispatch::ExceptionWrapper) &&
!::ActionDispatch::ExceptionWrapper.instance_methods.include?(:env)
# config.action_dispatch.log_rescued_responses (Rails 7.0+), as ActionDispatch::DebugExceptions
# reads it. This gem silences its logging and logs the exception here instead.
# config.action_dispatch.log_rescued_responses (Rails 7.0+) and
# config.action_dispatch.debug_exception_log_level (Rails 7.1+), as ActionDispatch::DebugExceptions
# reads them. This gem silences its logging and logs the exception here instead.
LOG_RESCUED_RESPONSES_KEY = "action_dispatch.log_rescued_responses".freeze
DEBUG_EXCEPTION_LOG_LEVEL_KEY = "action_dispatch.debug_exception_log_level".freeze

def call(env)
begin
Expand All @@ -38,7 +40,7 @@ def call(env)
def log_exception(env, exception)
return if !log_exception?(env, exception)

Config.instance.logger.fatal do
Config.instance.logger.add(env[DEBUG_EXCEPTION_LOG_LEVEL_KEY] || ::Logger::FATAL) do
backtrace = extract_backtrace(env, exception)
Events::Error.new(
name: exception.class.name,
Expand Down
33 changes: 32 additions & 1 deletion spec/logtail-rails/error_event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,45 @@ def method_for_action(action_name)
expect(response_statuses).to eq([404, 500])
end

it "should log the error at fatal even when config.action_dispatch.debug_exception_log_level is :error" do
it "should log the error at config.action_dispatch.debug_exception_log_level, :error with load_defaults 7.1 or later" do
skip("config.action_dispatch.debug_exception_log_level is new in Rails 7.1") unless ::Rails.application.env_config.key?("action_dispatch.debug_exception_log_level")

dispatch_rendering_exceptions("/runtime_error", "action_dispatch.debug_exception_log_level" => ::Logger::ERROR)

expect(error_rows.map { |row| [row["level"], row["message"]] }).to eq([["error", "RuntimeError (Boom!)"]])
expect(response_statuses).to eq([500])
end

it "should log every error, rescued responses too, at warn when config.action_dispatch.debug_exception_log_level is :warn" do
skip("config.action_dispatch.debug_exception_log_level is new in Rails 7.1") unless ::Rails.application.env_config.key?("action_dispatch.debug_exception_log_level")

dispatch_rendering_exceptions("/runtime_error", "action_dispatch.debug_exception_log_level" => ::Logger::WARN)
dispatch_rendering_exceptions("/record_not_found", "action_dispatch.debug_exception_log_level" => ::Logger::WARN)

expect(error_rows.map { |row| [row["level"], row["message"]] }).to eq([["warn", "RuntimeError (Boom!)"], ["warn", "ActiveRecord::RecordNotFound (Couldn't find User)"]])
expect(response_statuses).to eq([500, 404])
end

it "should log the error at fatal, Rails' default, when the app doesn't set config.action_dispatch.debug_exception_log_level" do
skip("config.action_dispatch.debug_exception_log_level is new in Rails 7.1") unless ::Rails.application.env_config.key?("action_dispatch.debug_exception_log_level")

# The spec app neither sets it nor loads the 7.1 defaults, so it has the railtie's :fatal
expect(::Rails.application.env_config["action_dispatch.debug_exception_log_level"]).to eq(::Logger::FATAL)

dispatch_rendering_exceptions("/runtime_error")

expect(error_rows.map { |row| [row["level"], row["message"]] }).to eq([["fatal", "RuntimeError (Boom!)"]])
end

it "should log every error at fatal on Rails versions without config.action_dispatch.debug_exception_log_level" do
skip("config.action_dispatch.debug_exception_log_level exists since Rails 7.1") if ::Rails.application.env_config.key?("action_dispatch.debug_exception_log_level")

dispatch_rendering_exceptions("/runtime_error")
dispatch_rendering_exceptions("/record_not_found")

expect(error_rows.map { |row| [row["level"], row["message"]] }).to eq([["fatal", "RuntimeError (Boom!)"], ["fatal", "ActiveRecord::RecordNotFound (Couldn't find User)"]])
end

# Renders exceptions as error pages like a production app, instead of raising them
def dispatch_rendering_exceptions(path, env_config = {})
show_exceptions = ::Rails.gem_version >= Gem::Version.new("7.1") ? :all : true
Expand Down
Loading