exception_notification 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +2 -2
  3. data/CHANGELOG.rdoc +14 -0
  4. data/CONTRIBUTING.md +18 -0
  5. data/Gemfile +1 -1
  6. data/README.md +64 -935
  7. data/Rakefile +2 -2
  8. data/docs/notifiers/campfire.md +50 -0
  9. data/docs/notifiers/custom.md +42 -0
  10. data/docs/notifiers/datadog.md +51 -0
  11. data/docs/notifiers/email.md +195 -0
  12. data/docs/notifiers/google_chat.md +31 -0
  13. data/docs/notifiers/hipchat.md +66 -0
  14. data/docs/notifiers/irc.md +97 -0
  15. data/docs/notifiers/mattermost.md +115 -0
  16. data/docs/notifiers/slack.md +161 -0
  17. data/docs/notifiers/sns.md +37 -0
  18. data/docs/notifiers/teams.md +54 -0
  19. data/docs/notifiers/webhook.md +60 -0
  20. data/examples/sample_app.rb +54 -0
  21. data/examples/sinatra/Gemfile +6 -6
  22. data/examples/sinatra/config.ru +1 -1
  23. data/examples/sinatra/sinatra_app.rb +14 -10
  24. data/exception_notification.gemspec +27 -22
  25. data/gemfiles/rails4_0.gemfile +3 -3
  26. data/gemfiles/rails4_1.gemfile +3 -3
  27. data/gemfiles/rails4_2.gemfile +3 -3
  28. data/gemfiles/rails5_0.gemfile +3 -3
  29. data/gemfiles/rails5_1.gemfile +3 -3
  30. data/gemfiles/rails5_2.gemfile +7 -0
  31. data/gemfiles/rails6_0.gemfile +7 -0
  32. data/lib/exception_notification.rb +1 -0
  33. data/lib/exception_notification/rack.rb +8 -21
  34. data/lib/exception_notification/resque.rb +8 -10
  35. data/lib/exception_notification/sidekiq.rb +8 -12
  36. data/lib/exception_notification/version.rb +3 -0
  37. data/lib/exception_notifier.rb +20 -3
  38. data/lib/exception_notifier/base_notifier.rb +2 -3
  39. data/lib/exception_notifier/campfire_notifier.rb +12 -13
  40. data/lib/exception_notifier/datadog_notifier.rb +153 -0
  41. data/lib/exception_notifier/email_notifier.rb +64 -87
  42. data/lib/exception_notifier/google_chat_notifier.rb +25 -119
  43. data/lib/exception_notifier/hipchat_notifier.rb +11 -12
  44. data/lib/exception_notifier/irc_notifier.rb +32 -30
  45. data/lib/exception_notifier/mattermost_notifier.rb +47 -140
  46. data/lib/exception_notifier/modules/backtrace_cleaner.rb +0 -2
  47. data/lib/exception_notifier/modules/error_grouping.rb +5 -5
  48. data/lib/exception_notifier/modules/formatter.rb +118 -0
  49. data/lib/exception_notifier/notifier.rb +5 -6
  50. data/lib/exception_notifier/slack_notifier.rb +63 -40
  51. data/lib/exception_notifier/sns_notifier.rb +17 -11
  52. data/lib/exception_notifier/teams_notifier.rb +58 -44
  53. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
  54. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
  55. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
  56. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
  57. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
  58. data/lib/exception_notifier/webhook_notifier.rb +14 -11
  59. data/lib/generators/exception_notification/install_generator.rb +5 -5
  60. data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
  61. data/test/exception_notification/rack_test.rb +27 -11
  62. data/test/exception_notification/resque_test.rb +52 -0
  63. data/test/exception_notifier/campfire_notifier_test.rb +42 -42
  64. data/test/exception_notifier/datadog_notifier_test.rb +151 -0
  65. data/test/exception_notifier/email_notifier_test.rb +269 -153
  66. data/test/exception_notifier/google_chat_notifier_test.rb +154 -101
  67. data/test/exception_notifier/hipchat_notifier_test.rb +78 -81
  68. data/test/exception_notifier/irc_notifier_test.rb +34 -34
  69. data/test/exception_notifier/mattermost_notifier_test.rb +164 -67
  70. data/test/exception_notifier/modules/error_grouping_test.rb +39 -40
  71. data/test/exception_notifier/modules/formatter_test.rb +150 -0
  72. data/test/exception_notifier/sidekiq_test.rb +6 -6
  73. data/test/exception_notifier/slack_notifier_test.rb +61 -60
  74. data/test/exception_notifier/sns_notifier_test.rb +27 -32
  75. data/test/exception_notifier/teams_notifier_test.rb +23 -26
  76. data/test/exception_notifier/webhook_notifier_test.rb +48 -46
  77. data/test/exception_notifier_test.rb +41 -38
  78. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  79. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  80. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  81. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  82. data/test/test_helper.rb +11 -14
  83. metadata +136 -166
  84. data/test/dummy/.gitignore +0 -4
  85. data/test/dummy/Rakefile +0 -7
  86. data/test/dummy/app/controllers/application_controller.rb +0 -3
  87. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  88. data/test/dummy/app/helpers/application_helper.rb +0 -2
  89. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  90. data/test/dummy/app/models/post.rb +0 -2
  91. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  92. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  93. data/test/dummy/app/views/posts/new.html.erb +0 -0
  94. data/test/dummy/app/views/posts/show.html.erb +0 -0
  95. data/test/dummy/config.ru +0 -4
  96. data/test/dummy/config/application.rb +0 -42
  97. data/test/dummy/config/boot.rb +0 -6
  98. data/test/dummy/config/database.yml +0 -22
  99. data/test/dummy/config/environment.rb +0 -17
  100. data/test/dummy/config/environments/development.rb +0 -25
  101. data/test/dummy/config/environments/production.rb +0 -50
  102. data/test/dummy/config/environments/test.rb +0 -35
  103. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  104. data/test/dummy/config/initializers/inflections.rb +0 -10
  105. data/test/dummy/config/initializers/mime_types.rb +0 -5
  106. data/test/dummy/config/initializers/secret_token.rb +0 -8
  107. data/test/dummy/config/initializers/session_store.rb +0 -8
  108. data/test/dummy/config/locales/en.yml +0 -5
  109. data/test/dummy/config/routes.rb +0 -3
  110. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  111. data/test/dummy/db/schema.rb +0 -24
  112. data/test/dummy/db/seeds.rb +0 -7
  113. data/test/dummy/lib/tasks/.gitkeep +0 -0
  114. data/test/dummy/public/404.html +0 -26
  115. data/test/dummy/public/422.html +0 -26
  116. data/test/dummy/public/500.html +0 -26
  117. data/test/dummy/public/favicon.ico +0 -0
  118. data/test/dummy/public/images/rails.png +0 -0
  119. data/test/dummy/public/index.html +0 -239
  120. data/test/dummy/public/javascripts/application.js +0 -2
  121. data/test/dummy/public/javascripts/controls.js +0 -965
  122. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  123. data/test/dummy/public/javascripts/effects.js +0 -1123
  124. data/test/dummy/public/javascripts/prototype.js +0 -6001
  125. data/test/dummy/public/javascripts/rails.js +0 -191
  126. data/test/dummy/public/robots.txt +0 -5
  127. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  128. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  129. data/test/dummy/script/rails +0 -6
  130. data/test/dummy/test/functional/posts_controller_test.rb +0 -237
  131. data/test/dummy/test/test_helper.rb +0 -7
@@ -1,6 +1,5 @@
1
1
  module ExceptionNotifier
2
2
  module BacktraceCleaner
3
-
4
3
  def clean_backtrace(exception)
5
4
  if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
6
5
  Rails.backtrace_cleaner.send(:filter, exception.backtrace)
@@ -8,6 +7,5 @@ module ExceptionNotifier
8
7
  exception.backtrace
9
8
  end
10
9
  end
11
-
12
10
  end
13
11
  end
@@ -27,7 +27,7 @@ module ExceptionNotifier
27
27
  def error_count(error_key)
28
28
  count = begin
29
29
  error_grouping_cache.read(error_key)
30
- rescue => e
30
+ rescue StandardError => e
31
31
  ExceptionNotifier.logger.warn("#{error_grouping_cache.inspect} failed to read, reason: #{e.message}. Falling back to memory cache store.")
32
32
  fallback_cache_store.read(error_key)
33
33
  end
@@ -37,7 +37,7 @@ module ExceptionNotifier
37
37
 
38
38
  def save_error_count(error_key, count)
39
39
  error_grouping_cache.write(error_key, count, expires_in: error_grouping_period)
40
- rescue => e
40
+ rescue StandardError => e
41
41
  ExceptionNotifier.logger.warn("#{error_grouping_cache.inspect} failed to write, reason: #{e.message}. Falling back to memory cache store.")
42
42
  fallback_cache_store.write(error_key, count, expires_in: error_grouping_period)
43
43
  end
@@ -46,13 +46,13 @@ module ExceptionNotifier
46
46
  message_based_key = "exception:#{Zlib.crc32("#{exception.class.name}\nmessage:#{exception.message}")}"
47
47
  accumulated_errors_count = 1
48
48
 
49
- if count = error_count(message_based_key)
49
+ if (count = error_count(message_based_key))
50
50
  accumulated_errors_count = count + 1
51
51
  save_error_count(message_based_key, accumulated_errors_count)
52
52
  else
53
53
  backtrace_based_key = "exception:#{Zlib.crc32("#{exception.class.name}\npath:#{exception.backtrace.try(:first)}")}"
54
54
 
55
- if count = error_grouping_cache.read(backtrace_based_key)
55
+ if (count = error_grouping_cache.read(backtrace_based_key))
56
56
  accumulated_errors_count = count + 1
57
57
  save_error_count(backtrace_based_key, accumulated_errors_count)
58
58
  else
@@ -74,4 +74,4 @@ module ExceptionNotifier
74
74
  end
75
75
  end
76
76
  end
77
- end
77
+ end
@@ -0,0 +1,118 @@
1
+ require 'active_support/core_ext/time'
2
+ require 'action_dispatch'
3
+
4
+ module ExceptionNotifier
5
+ class Formatter
6
+ include ExceptionNotifier::BacktraceCleaner
7
+
8
+ attr_reader :app_name
9
+
10
+ def initialize(exception, opts = {})
11
+ @exception = exception
12
+
13
+ @env = opts[:env]
14
+ @errors_count = opts[:accumulated_errors_count].to_i
15
+ @app_name = opts[:app_name] || rails_app_name
16
+ end
17
+
18
+ #
19
+ # :warning: Error occurred in production :warning:
20
+ # :warning: Error occurred :warning:
21
+ #
22
+ def title
23
+ env = Rails.env if defined?(::Rails) && ::Rails.respond_to?(:env)
24
+
25
+ if env
26
+ "⚠️ Error occurred in #{env} ⚠️"
27
+ else
28
+ '⚠️ Error occurred ⚠️'
29
+ end
30
+ end
31
+
32
+ #
33
+ # A *NoMethodError* occurred.
34
+ # 3 *NoMethodError* occurred.
35
+ # A *NoMethodError* occurred in *home#index*.
36
+ #
37
+ def subtitle
38
+ errors_text = if errors_count > 1
39
+ errors_count
40
+ else
41
+ exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A'
42
+ end
43
+
44
+ in_action = " in *#{controller_and_action}*" if controller
45
+
46
+ "#{errors_text} *#{exception.class}* occurred#{in_action}."
47
+ end
48
+
49
+ #
50
+ #
51
+ # *Request:*
52
+ # ```
53
+ # * url : https://www.example.com/
54
+ # * http_method : GET
55
+ # * ip_address : 127.0.0.1
56
+ # * parameters : {"controller"=>"home", "action"=>"index"}
57
+ # * timestamp : 2019-01-01 00:00:00 UTC
58
+ # ```
59
+ #
60
+ def request_message
61
+ request = ActionDispatch::Request.new(env) if env
62
+ return unless request
63
+
64
+ [
65
+ '```',
66
+ "* url : #{request.original_url}",
67
+ "* http_method : #{request.method}",
68
+ "* ip_address : #{request.remote_ip}",
69
+ "* parameters : #{request.filtered_parameters}",
70
+ "* timestamp : #{Time.current}",
71
+ '```'
72
+ ].join("\n")
73
+ end
74
+
75
+ #
76
+ #
77
+ # *Backtrace:*
78
+ # ```
79
+ # * app/controllers/my_controller.rb:99:in `specific_function'
80
+ # * app/controllers/my_controller.rb:70:in `specific_param'
81
+ # * app/controllers/my_controller.rb:53:in `my_controller_params'
82
+ # ```
83
+ #
84
+ def backtrace_message
85
+ backtrace = exception.backtrace ? clean_backtrace(exception) : nil
86
+
87
+ return unless backtrace
88
+
89
+ text = []
90
+
91
+ text << '```'
92
+ backtrace.first(3).each { |line| text << "* #{line}" }
93
+ text << '```'
94
+
95
+ text.join("\n")
96
+ end
97
+
98
+ #
99
+ # home#index
100
+ #
101
+ def controller_and_action
102
+ "#{controller.controller_name}##{controller.action_name}" if controller
103
+ end
104
+
105
+ private
106
+
107
+ attr_reader :exception, :env, :errors_count
108
+
109
+ def rails_app_name
110
+ return unless defined?(::Rails) && ::Rails.respond_to?(:application)
111
+ Rails.application.class.parent_name.underscore
112
+ end
113
+
114
+ def controller
115
+ env['action_controller.instance'] if env
116
+ end
117
+ end
118
+ end
@@ -2,14 +2,13 @@ require 'active_support/deprecation'
2
2
 
3
3
  module ExceptionNotifier
4
4
  class Notifier
5
-
6
- def self.exception_notification(env, exception, options={})
7
- ActiveSupport::Deprecation.warn "Please use ExceptionNotifier.notify_exception(exception, options.merge(:env => env))."
8
- ExceptionNotifier.registered_exception_notifier(:email).create_email(exception, options.merge(:env => env))
5
+ def self.exception_notification(env, exception, options = {})
6
+ ActiveSupport::Deprecation.warn 'Please use ExceptionNotifier.notify_exception(exception, options.merge(env: env)).'
7
+ ExceptionNotifier.registered_exception_notifier(:email).create_email(exception, options.merge(env: env))
9
8
  end
10
9
 
11
- def self.background_exception_notification(exception, options={})
12
- ActiveSupport::Deprecation.warn "Please use ExceptionNotifier.notify_exception(exception, options)."
10
+ def self.background_exception_notification(exception, options = {})
11
+ ActiveSupport::Deprecation.warn 'Please use ExceptionNotifier.notify_exception(exception, options).'
13
12
  ExceptionNotifier.registered_exception_notifier(:email).create_email(exception, options)
14
13
  end
15
14
  end
@@ -15,21 +15,65 @@ module ExceptionNotifier
15
15
  @message_opts = options.fetch(:additional_parameters, {})
16
16
  @color = @message_opts.delete(:color) { 'danger' }
17
17
  @notifier = Slack::Notifier.new webhook_url, options
18
- rescue
18
+ rescue StandardError
19
19
  @notifier = nil
20
20
  end
21
21
  end
22
22
 
23
- def call(exception, options={})
23
+ def call(exception, options = {})
24
+ clean_message = exception.message.tr('`', "'")
25
+ attchs = attchs(exception, clean_message, options)
26
+
27
+ return unless valid?
28
+
29
+ args = [exception, options, clean_message, @message_opts.merge(attachments: attchs)]
30
+ send_notice(*args) do |_msg, message_opts|
31
+ message_opts[:channel] = options[:channel] if options.key?(:channel)
32
+
33
+ @notifier.ping '', message_opts
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def valid?
40
+ !@notifier.nil?
41
+ end
42
+
43
+ def deep_reject(hash, block)
44
+ hash.each do |k, v|
45
+ deep_reject(v, block) if v.is_a?(Hash)
46
+
47
+ hash.delete(k) if block.call(k, v)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def attchs(exception, clean_message, options)
54
+ text, data = information_from_options(exception.class, options)
55
+ backtrace = clean_backtrace(exception) if exception.backtrace
56
+ fields = fields(clean_message, backtrace, data)
57
+
58
+ [color: @color, text: text, fields: fields, mrkdwn_in: %w[text fields]]
59
+ end
60
+
61
+ def information_from_options(exception_class, options)
24
62
  errors_count = options[:accumulated_errors_count].to_i
25
- measure_word = errors_count > 1 ? errors_count : (exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A')
26
- exception_name = "*#{measure_word}* `#{exception.class.to_s}`"
27
63
 
28
- if options[:env].nil?
64
+ measure_word = if errors_count > 1
65
+ errors_count
66
+ else
67
+ exception_class.to_s =~ /^[aeiou]/i ? 'An' : 'A'
68
+ end
69
+
70
+ exception_name = "*#{measure_word}* `#{exception_class}`"
71
+ env = options[:env]
72
+
73
+ if env.nil?
29
74
  data = options[:data] || {}
30
75
  text = "#{exception_name} *occured in background*\n"
31
76
  else
32
- env = options[:env]
33
77
  data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
34
78
 
35
79
  kontroller = env['action_controller.instance']
@@ -39,50 +83,29 @@ module ExceptionNotifier
39
83
  text += "\n"
40
84
  end
41
85
 
42
- clean_message = exception.message.gsub("`", "'")
43
- fields = [ { title: 'Exception', value: clean_message } ]
86
+ [text, data]
87
+ end
44
88
 
45
- fields.push({ title: 'Hostname', value: Socket.gethostname })
89
+ def fields(clean_message, backtrace, data)
90
+ fields = [
91
+ { title: 'Exception', value: clean_message },
92
+ { title: 'Hostname', value: Socket.gethostname }
93
+ ]
46
94
 
47
- if exception.backtrace
48
- formatted_backtrace = "```#{exception.backtrace.first(@backtrace_lines).join("\n")}```"
49
- fields.push({ title: 'Backtrace', value: formatted_backtrace })
95
+ if backtrace
96
+ formatted_backtrace = "```#{backtrace.first(@backtrace_lines).join("\n")}```"
97
+ fields << { title: 'Backtrace', value: formatted_backtrace }
50
98
  end
51
99
 
52
100
  unless data.empty?
53
101
  deep_reject(data, @ignore_data_if) if @ignore_data_if.is_a?(Proc)
54
- data_string = data.map{|k,v| "#{k}: #{v}"}.join("\n")
55
- fields.push({ title: 'Data', value: "```#{data_string}```" })
102
+ data_string = data.map { |k, v| "#{k}: #{v}" }.join("\n")
103
+ fields << { title: 'Data', value: "```#{data_string}```" }
56
104
  end
57
105
 
58
106
  fields.concat(@additional_fields) if @additional_fields
59
107
 
60
- attchs = [color: @color, text: text, fields: fields, mrkdwn_in: %w(text fields)]
61
-
62
- if valid?
63
- send_notice(exception, options, clean_message, @message_opts.merge(attachments: attchs)) do |msg, message_opts|
64
- @notifier.ping '', message_opts
65
- end
66
- end
67
- end
68
-
69
- protected
70
-
71
- def valid?
72
- !@notifier.nil?
73
- end
74
-
75
- def deep_reject(hash, block)
76
- hash.each do |k, v|
77
- if v.is_a?(Hash)
78
- deep_reject(v, block)
79
- end
80
-
81
- if block.call(k, v)
82
- hash.delete(k)
83
- end
84
- end
108
+ fields
85
109
  end
86
-
87
110
  end
88
111
  end
@@ -3,9 +3,9 @@ module ExceptionNotifier
3
3
  def initialize(options)
4
4
  super
5
5
 
6
- raise ArgumentError.new "You must provide 'region' option" unless options[:region]
7
- raise ArgumentError.new "You must provide 'access_key_id' option" unless options[:access_key_id]
8
- raise ArgumentError.new "You must provide 'secret_access_key' option" unless options[:secret_access_key]
6
+ raise ArgumentError, "You must provide 'region' option" unless options[:region]
7
+ raise ArgumentError, "You must provide 'access_key_id' option" unless options[:access_key_id]
8
+ raise ArgumentError, "You must provide 'secret_access_key' option" unless options[:secret_access_key]
9
9
 
10
10
  @notifier = Aws::SNS::Client.new(
11
11
  region: options[:region],
@@ -35,8 +35,8 @@ module ExceptionNotifier
35
35
  def build_subject(exception, options)
36
36
  subject = "#{options[:sns_prefix]} - "
37
37
  subject << accumulated_exception_name(exception, options)
38
- subject << " occurred"
39
- subject.length > 120 ? subject[0...120] + "..." : subject
38
+ subject << ' occurred'
39
+ subject.length > 120 ? subject[0...120] + '...' : subject
40
40
  end
41
41
 
42
42
  def build_message(exception, options)
@@ -57,16 +57,22 @@ module ExceptionNotifier
57
57
  text += "Exception: #{exception.message}\n"
58
58
  text += "Hostname: #{Socket.gethostname}\n"
59
59
 
60
- if exception.backtrace
61
- formatted_backtrace = "#{exception.backtrace.first(options[:backtrace_lines]).join("\n")}"
62
- text += "Backtrace:\n#{formatted_backtrace}\n"
63
- end
60
+ return unless exception.backtrace
61
+
62
+ formatted_backtrace = exception.backtrace.first(options[:backtrace_lines]).join("\n").to_s
63
+ text + "Backtrace:\n#{formatted_backtrace}\n"
64
64
  end
65
65
 
66
66
  def accumulated_exception_name(exception, options)
67
67
  errors_count = options[:accumulated_errors_count].to_i
68
- measure_word = errors_count > 1 ? errors_count : (exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A')
69
- "#{measure_word} #{exception.class.to_s}"
68
+
69
+ measure_word = if errors_count > 1
70
+ errors_count
71
+ else
72
+ exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A'
73
+ end
74
+
75
+ "#{measure_word} #{exception.class}"
70
76
  end
71
77
 
72
78
  def default_options
@@ -1,13 +1,13 @@
1
1
  require 'action_dispatch'
2
2
  require 'active_support/core_ext/time'
3
+ require 'json'
3
4
 
4
5
  module ExceptionNotifier
5
6
  class TeamsNotifier < BaseNotifier
6
7
  include ExceptionNotifier::BacktraceCleaner
7
8
 
8
9
  class MissingController
9
- def method_missing(*args, &block)
10
- end
10
+ def method_missing(*args, &block); end
11
11
  end
12
12
 
13
13
  attr_accessor :httparty
@@ -18,21 +18,23 @@ module ExceptionNotifier
18
18
  @httparty = HTTParty
19
19
  end
20
20
 
21
- def call(exception, options={})
21
+ def call(exception, options = {})
22
22
  @options = options.merge(@default_options)
23
23
  @exception = exception
24
24
  @backtrace = exception.backtrace ? clean_backtrace(exception) : nil
25
25
 
26
26
  @env = @options.delete(:env)
27
27
 
28
- @application_name = @options.delete(:app_name) || Rails.application.class.parent_name.underscore
28
+ @application_name = @options.delete(:app_name) || rails_app_name
29
29
  @gitlab_url = @options.delete(:git_url)
30
30
  @jira_url = @options.delete(:jira_url)
31
31
 
32
32
  @webhook_url = @options.delete(:webhook_url)
33
- raise ArgumentError.new "You must provide 'webhook_url' parameter." unless @webhook_url
33
+ raise ArgumentError, "You must provide 'webhook_url' parameter." unless @webhook_url
34
34
 
35
- unless @env.nil?
35
+ if @env.nil?
36
+ @controller = @request_items = nil
37
+ else
36
38
  @controller = @env['action_controller.instance'] || MissingController.new
37
39
 
38
40
  request = ActionDispatch::Request.new(@env)
@@ -43,19 +45,17 @@ module ExceptionNotifier
43
45
  parameters: request.filtered_parameters,
44
46
  timestamp: Time.current }
45
47
 
46
- if request.session["warden.user.user.key"]
47
- current_user = User.find(request.session["warden.user.user.key"][0][0])
48
- @request_items.merge!({ current_user: { id: current_user.id, email: current_user.email } })
48
+ if request.session['warden.user.user.key']
49
+ current_user = User.find(request.session['warden.user.user.key'][0][0])
50
+ @request_items[:current_user] = { id: current_user.id, email: current_user.email }
49
51
  end
50
- else
51
- @controller = @request_items = nil
52
52
  end
53
53
 
54
54
  payload = message_text
55
55
 
56
56
  @options[:body] = payload.to_json
57
57
  @options[:headers] ||= {}
58
- @options[:headers].merge!({ 'Content-Type' => 'application/json' })
58
+ @options[:headers]['Content-Type'] = 'application/json'
59
59
  @options[:debug_output] = $stdout
60
60
 
61
61
  @httparty.post(@webhook_url, @options)
@@ -67,17 +67,17 @@ module ExceptionNotifier
67
67
  errors_count = @options[:accumulated_errors_count].to_i
68
68
 
69
69
  text = {
70
- "@type" => "MessageCard",
71
- "@context" => "http://schema.org/extensions",
72
- "summary" => "#{@application_name} Exception Alert",
73
- "title" => "⚠️ Exception Occurred in #{Rails.env} ⚠️",
74
- "sections" => [
70
+ '@type' => 'MessageCard',
71
+ '@context' => 'http://schema.org/extensions',
72
+ 'summary' => "#{@application_name} Exception Alert",
73
+ 'title' => "⚠️ Exception Occurred in #{env_name} ⚠️",
74
+ 'sections' => [
75
75
  {
76
- "activityTitle" => "#{errors_count > 1 ? errors_count : 'A'} *#{@exception.class}* occurred" + if @controller then " in *#{controller_and_method}*." else "." end,
77
- "activitySubtitle" => "#{@exception.message}"
76
+ 'activityTitle' => "#{errors_count > 1 ? errors_count : 'A'} *#{@exception.class}* occurred" + (@controller ? " in *#{controller_and_method}*." : '.'),
77
+ 'activitySubtitle' => @exception.message.to_s
78
78
  }
79
79
  ],
80
- "potentialAction" => []
80
+ 'potentialAction' => []
81
81
  }
82
82
 
83
83
  text['sections'].push details
@@ -90,8 +90,8 @@ module ExceptionNotifier
90
90
 
91
91
  def details
92
92
  details = {
93
- "title" => "Details",
94
- "facts" => []
93
+ 'title' => 'Details',
94
+ 'facts' => []
95
95
  }
96
96
 
97
97
  details['facts'].push message_request unless @request_items.nil?
@@ -102,59 +102,59 @@ module ExceptionNotifier
102
102
 
103
103
  def message_request
104
104
  {
105
- "name" => "Request",
106
- "value" => "#{hash_presentation(@request_items)}\n "
105
+ 'name' => 'Request',
106
+ 'value' => "#{hash_presentation(@request_items)}\n "
107
107
  }
108
108
  end
109
109
 
110
110
  def message_backtrace(size = 3)
111
111
  text = []
112
112
  size = @backtrace.size < size ? @backtrace.size : size
113
- text << "```"
114
- size.times { |i| text << "* " + @backtrace[i] }
115
- text << "```"
113
+ text << '```'
114
+ size.times { |i| text << '* ' + @backtrace[i] }
115
+ text << '```'
116
116
 
117
117
  {
118
- "name" => "Backtrace",
119
- "value" => "#{text.join(" \n")}"
118
+ 'name' => 'Backtrace',
119
+ 'value' => text.join(" \n").to_s
120
120
  }
121
121
  end
122
122
 
123
123
  def gitlab_view_link
124
124
  {
125
- "@type" => "ViewAction",
126
- "name" => "🦊 View in GitLab",
127
- "target" => [
125
+ '@type' => 'ViewAction',
126
+ 'name' => "\u{1F98A} View in GitLab",
127
+ 'target' => [
128
128
  "#{@gitlab_url}/#{@application_name}"
129
129
  ]
130
130
  }
131
131
  end
132
132
 
133
133
  def gitlab_issue_link
134
- link = [@gitlab_url, @application_name, "issues", "new"].join("/")
134
+ link = [@gitlab_url, @application_name, 'issues', 'new'].join('/')
135
135
  params = {
136
- "issue[title]" => ["[BUG] Error 500 :",
136
+ 'issue[title]' => ['[BUG] Error 500 :',
137
137
  controller_and_method,
138
138
  "(#{@exception.class})",
139
- @exception.message].compact.join(" ")
139
+ @exception.message].compact.join(' ')
140
140
  }.to_query
141
141
 
142
142
  {
143
- "@type" => "ViewAction",
144
- "name" => "🦊 Create Issue in GitLab",
145
- "target" => [
143
+ '@type' => 'ViewAction',
144
+ 'name' => "\u{1F98A} Create Issue in GitLab",
145
+ 'target' => [
146
146
  "#{link}/?#{params}"
147
- ]
147
+ ]
148
148
  }
149
149
  end
150
150
 
151
151
  def jira_issue_link
152
152
  {
153
- "@type" => "ViewAction",
154
- "name" => "🐞 Create Issue in Jira",
155
- "target" => [
153
+ '@type' => 'ViewAction',
154
+ 'name' => '🐞 Create Issue in Jira',
155
+ 'target' => [
156
156
  "#{@jira_url}/secure/CreateIssue!default.jspa"
157
- ]
157
+ ]
158
158
  }
159
159
  end
160
160
 
@@ -162,7 +162,7 @@ module ExceptionNotifier
162
162
  if @controller
163
163
  "#{@controller.controller_name}##{@controller.action_name}"
164
164
  else
165
- ""
165
+ ''
166
166
  end
167
167
  end
168
168
 
@@ -175,5 +175,19 @@ module ExceptionNotifier
175
175
 
176
176
  text.join(" \n")
177
177
  end
178
+
179
+ def rails_app_name
180
+ return unless defined?(Rails) && Rails.respond_to?(:application)
181
+
182
+ if ::Gem::Version.new(Rails.version) >= ::Gem::Version.new('6.0')
183
+ Rails.application.class.module_parent_name.underscore
184
+ else
185
+ Rails.application.class.parent_name.underscore
186
+ end
187
+ end
188
+
189
+ def env_name
190
+ Rails.env if defined?(Rails) && Rails.respond_to?(:env)
191
+ end
178
192
  end
179
193
  end