2021-09-30 23:02:18 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class ErrorTracking::ErrorEvent < ApplicationRecord
|
|
|
|
belongs_to :error, counter_cache: :events_count
|
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
# Scrub null bytes
|
|
|
|
attribute :payload, Gitlab::Database::Type::JsonPgSafe.new
|
|
|
|
|
2021-09-30 23:02:18 +05:30
|
|
|
validates :payload, json_schema: { filename: 'error_tracking_event_payload' }
|
|
|
|
|
|
|
|
validates :error, presence: true
|
2021-12-11 22:18:48 +05:30
|
|
|
validates :description, presence: true, length: { maximum: 1024 }
|
|
|
|
validates :level, length: { maximum: 255 }
|
|
|
|
validates :environment, length: { maximum: 255 }
|
2021-09-30 23:02:18 +05:30
|
|
|
validates :occurred_at, presence: true
|
2021-10-27 15:23:28 +05:30
|
|
|
|
|
|
|
def stacktrace
|
|
|
|
@stacktrace ||= build_stacktrace
|
|
|
|
end
|
|
|
|
|
|
|
|
# For compatibility with sentry integration
|
|
|
|
def to_sentry_error_event
|
|
|
|
Gitlab::ErrorTracking::ErrorEvent.new(
|
|
|
|
issue_id: error_id,
|
|
|
|
date_received: occurred_at,
|
|
|
|
stack_trace_entries: stacktrace
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
def release
|
|
|
|
payload.dig('release')
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
private
|
|
|
|
|
|
|
|
def build_stacktrace
|
|
|
|
raw_stacktrace = find_stacktrace_from_payload
|
|
|
|
|
|
|
|
return [] unless raw_stacktrace
|
|
|
|
|
|
|
|
raw_stacktrace.map do |entry|
|
|
|
|
{
|
|
|
|
'lineNo' => entry['lineno'],
|
|
|
|
'context' => build_stacktrace_context(entry),
|
|
|
|
'filename' => entry['filename'],
|
|
|
|
'function' => entry['function'],
|
|
|
|
'colNo' => 0 # we don't support colNo yet.
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def find_stacktrace_from_payload
|
|
|
|
exception_entry = payload.dig('exception')
|
|
|
|
|
|
|
|
if exception_entry
|
|
|
|
exception_values = exception_entry.dig('values')
|
|
|
|
stack_trace_entry = exception_values&.detect { |h| h['stacktrace'].present? }
|
|
|
|
stack_trace_entry&.dig('stacktrace', 'frames')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_stacktrace_context(entry)
|
|
|
|
context = []
|
|
|
|
error_line = entry['context_line']
|
|
|
|
error_line_no = entry['lineno']
|
|
|
|
pre_context = entry['pre_context']
|
|
|
|
post_context = entry['post_context']
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
context += lines_with_position(pre_context, error_line_no - pre_context.size) if pre_context
|
2021-10-27 15:23:28 +05:30
|
|
|
context += lines_with_position([error_line], error_line_no)
|
2021-12-11 22:18:48 +05:30
|
|
|
context += lines_with_position(post_context, error_line_no + 1) if post_context
|
2021-10-27 15:23:28 +05:30
|
|
|
|
|
|
|
context.reject(&:blank?)
|
|
|
|
end
|
|
|
|
|
|
|
|
def lines_with_position(lines, position)
|
|
|
|
return [] if lines.blank?
|
|
|
|
|
|
|
|
lines.map.with_index do |line, index|
|
|
|
|
next unless line
|
|
|
|
|
|
|
|
[position + index, line]
|
|
|
|
end
|
|
|
|
end
|
2021-09-30 23:02:18 +05:30
|
|
|
end
|