Skip to content

Commit

Permalink
Add error_cause property to log message. (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
iMacTia authored Nov 29, 2024
1 parent d71ca62 commit bb73489
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Metrics/BlockLength:
Exclude:
- spec/**/*.rb

Metrics/ModuleLength:
Enabled: false

Layout/LineLength:
Max: 120
Exclude:
Expand Down
24 changes: 15 additions & 9 deletions lib/sidekiq/exception_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@

# Utility that allows us to get a hash representation of an exception
module ExceptionUtils
def self.get_exception_with_cause_hash(exc, parent_backtrace = nil, max_depth_left)
backtrace = exc.backtrace || []
if parent_backtrace
common_lines = backtrace.reverse.zip(parent_backtrace.reverse).take_while { |a, b| a == b }

backtrace = backtrace[0...-common_lines.length] if common_lines.any?
end
module_function

def get_exception_with_cause_hash(exc, parent_backtrace = nil, max_depth_left: 1)
error_hash = {
'class' => exc.class.to_s,
'message' => exc.message,
'backtrace' => backtrace
'backtrace' => backtrace_for(exc, parent_backtrace)
}

if (cause = exc.cause) && max_depth_left.positive?
# Pass the current backtrace as the parent_backtrace to the cause to shorten cause's backtrace list
error_hash['cause'] = get_exception_with_cause_hash(cause, exc.backtrace, max_depth_left - 1)
error_hash['cause'] = get_exception_with_cause_hash(cause, exc.backtrace, max_depth_left: max_depth_left - 1)
end

error_hash
end

def backtrace_for(exception, parent_backtrace = nil)
backtrace = exception.backtrace || []
if parent_backtrace
common_lines = backtrace.reverse.zip(parent_backtrace.reverse).take_while { |a, b| a == b }

backtrace = backtrace[0...-common_lines.length] if common_lines.any?
end

backtrace
end
end
13 changes: 11 additions & 2 deletions lib/sidekiq/logging/shared.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ def log_job_exception(job, started_at, exc)

config = Sidekiq::Logstash.configuration
if config.log_job_exception_with_causes
payload['error'] = ExceptionUtils.get_exception_with_cause_hash(exc, config.causes_logging_max_depth)
payload['error'] = ExceptionUtils.get_exception_with_cause_hash(
exc, max_depth_left: config.causes_logging_max_depth
)
else
exc = exc.cause || exc if exc.is_a? Sidekiq::JobRetry::Handled
payload['error_message'] = exc.message
payload['error'] = exc.class
payload['error'] = exc.class.to_s
payload['error_backtrace'] = %('#{exc.backtrace.join("\n")}')
if (cause = exc.cause)
payload['error_cause'] = {
'class' => cause.class.to_s,
'message' => cause.message,
'backtrace' => ExceptionUtils.backtrace_for(cause, exc.backtrace)
}
end
end

process_payload(payload)
Expand Down
16 changes: 14 additions & 2 deletions spec/sidekiq/logstash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,31 @@ def process(worker, params = [], encrypt: false)
it 'logs the exception with job retry' do
expect { process(SpecWorker, [true]) }.to raise_error(RuntimeError)

expect(log_message['error_message']).to eq('You know nothing, Jon Snow.')
expect(log_message['error']).to eq('RuntimeError')
expect(log_message['error_message']).to eq('You know nothing, Jon Snow.')
expect(log_message['error_backtrace'].split("\n").first).to include('workers/spec_worker.rb:')
expect(log_message['error_cause']).to match(
hash_including(
'class' => 'RuntimeError',
'message' => 'Error rescuing error'
)
)
end

it 'logs the exception without job retry' do
allow(SpecWorker).to receive(:get_sidekiq_options).and_return({ 'retry' => false, 'queue' => 'default' })

expect { process(SpecWorker, [true]) }.to raise_error(RuntimeError)

expect(log_message['error_message']).to eq('You know nothing, Jon Snow.')
expect(log_message['error']).to eq('RuntimeError')
expect(log_message['error_message']).to eq('You know nothing, Jon Snow.')
expect(log_message['error_backtrace'].split("\n").first).to include('workers/spec_worker.rb:')
expect(log_message['error_cause']).to match(
hash_including(
'class' => 'RuntimeError',
'message' => 'Error rescuing error'
)
)
end

context 'log_job_exception_with_causes enabled' do
Expand Down

0 comments on commit bb73489

Please sign in to comment.