1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
# frozen_string_literal: true
module ExceptionNotifier
class SlackNotifier < BaseNotifier
include ExceptionNotifier::BacktraceCleaner
attr_accessor :notifier
def initialize(options)
super
begin
@ignore_data_if = options[:ignore_data_if]
@backtrace_lines = options.fetch(:backtrace_lines, 10)
@additional_fields = options[:additional_fields]
webhook_url = options.fetch(:webhook_url)
@message_opts = options.fetch(:additional_parameters, {})
@color = @message_opts.delete(:color) { "danger" }
@notifier = Slack::Notifier.new webhook_url, options
rescue
@notifier = nil
end
end
def call(exception, options = {})
clean_message = exception.message.tr("`", "'")
attchs = attchs(exception, clean_message, options)
return unless valid?
args = [exception, options, clean_message, @message_opts.merge(attachments: attchs)]
send_notice(*args) do |_msg, message_opts|
message_opts[:channel] = options[:channel] if options.key?(:channel)
@notifier.ping "", message_opts
end
end
protected
def valid?
!@notifier.nil?
end
def deep_reject(hash, block)
hash.each do |k, v|
deep_reject(v, block) if v.is_a?(Hash)
hash.delete(k) if block.call(k, v)
end
end
private
def attchs(exception, clean_message, options)
text, data = information_from_options(exception.class, options)
backtrace = clean_backtrace(exception) if exception.backtrace
fields = fields(clean_message, backtrace, data)
[color: @color, text: text, fields: fields, mrkdwn_in: %w[text fields]]
end
def information_from_options(exception_class, options)
errors_count = options[:accumulated_errors_count].to_i
measure_word = if errors_count > 1
errors_count
else
/^[aeiou]/i.match?(exception_class.to_s) ? "An" : "A"
end
exception_name = "*#{measure_word}* `#{exception_class}`"
env = options[:env]
options[:headers] ||= {}
options[:headers]["Content-Type"] = "application/json"
if env.nil?
data = options[:data] || {}
text = "#{exception_name} *occured in background*\n"
else
data = (env["exception_notifier.exception_data"] || {}).merge(options[:data] || {})
kontroller = env["action_controller.instance"]
request = "#{env["REQUEST_METHOD"]} <#{env["REQUEST_URI"]}>"
text = "#{exception_name} *occurred while* `#{request}`"
text += " *was processed by* `#{kontroller.controller_name}##{kontroller.action_name}`" if kontroller
text += "\n"
end
[text, data]
end
def fields(clean_message, backtrace, data)
fields = [
{title: "Exception", value: clean_message},
{title: "Hostname", value: Socket.gethostname}
]
if backtrace
formatted_backtrace = "```#{backtrace.first(@backtrace_lines).join("\n")}```"
fields << {title: "Backtrace", value: formatted_backtrace}
end
unless data.empty?
deep_reject(data, @ignore_data_if) if @ignore_data_if.is_a?(Proc)
data_string = data.map { |k, v| "#{k}: #{v}" }.join("\n")
fields << {title: "Data", value: "```#{data_string}```"}
end
fields.concat(@additional_fields) if @additional_fields
fields
end
end
end
|