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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
# frozen_string_literal: true
require "action_dispatch"
module ExceptionNotifier
class DatadogNotifier < BaseNotifier
attr_reader :client,
:default_options
def initialize(options)
super
@client = options.fetch(:client)
@default_options = options
end
def call(exception, options = {})
client.emit_event(
datadog_event(exception, options)
)
end
def datadog_event(exception, options = {})
DatadogExceptionEvent.new(
exception,
options.reverse_merge(default_options)
).event
end
class DatadogExceptionEvent
include ExceptionNotifier::BacktraceCleaner
MAX_TITLE_LENGTH = 120
MAX_VALUE_LENGTH = 300
MAX_BACKTRACE_SIZE = 3
ALERT_TYPE = "error"
attr_reader :exception,
:options
def initialize(exception, options)
@exception = exception
@options = options
end
def request
@request ||= ActionDispatch::Request.new(options[:env]) if options[:env]
end
def controller
@controller ||= options[:env] && options[:env]["action_controller.instance"]
end
def backtrace
@backtrace ||= exception.backtrace ? clean_backtrace(exception) : []
end
def tags
options[:tags] || []
end
def title_prefix
options[:title_prefix] || ""
end
def event
title = formatted_title
body = formatted_body
Dogapi::Event.new(
body,
msg_title: title,
alert_type: ALERT_TYPE,
tags: tags,
aggregation_key: [title]
)
end
def formatted_title
title =
"#{title_prefix}#{controller_subtitle} (#{exception.class}) #{exception.message.inspect}"
truncate(title, MAX_TITLE_LENGTH)
end
def formatted_body
text = []
text << "%%%"
text << formatted_request if request
text << formatted_session if request
text << formatted_backtrace
text << "%%%"
text.join("\n")
end
def formatted_key_value(key, value)
"**#{key}:** #{value}"
end
def formatted_request
text = []
text << "### **Request**"
text << formatted_key_value("URL", request.url)
text << formatted_key_value("HTTP Method", request.request_method)
text << formatted_key_value("IP Address", request.remote_ip)
text << formatted_key_value("Parameters", request.filtered_parameters.inspect)
text << formatted_key_value("Timestamp", Time.current)
text << formatted_key_value("Server", Socket.gethostname)
text << formatted_key_value("Rails root", Rails.root) if defined?(Rails) && Rails.respond_to?(:root)
text << formatted_key_value("Process", $PROCESS_ID)
text << "___"
text.join("\n")
end
def formatted_session
text = []
text << "### **Session**"
text << formatted_key_value("Data", request.session.to_hash)
text << "___"
text.join("\n")
end
def formatted_backtrace
size = [backtrace.size, MAX_BACKTRACE_SIZE].min
text = []
text << "### **Backtrace**"
text << "````"
size.times { |i| text << backtrace[i] }
text << "````"
text << "___"
text.join("\n")
end
def truncate(string, max)
(string.length > max) ? "#{string[0...max]}..." : string
end
def inspect_object(object)
case object
when Hash, Array
truncate(object.inspect, MAX_VALUE_LENGTH)
else
object.to_s
end
end
private
def controller_subtitle
"#{controller.controller_name} #{controller.action_name}" if controller
end
end
end
end
|