exception_notification 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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