airbrakeV4rails5 4.3.8

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1716 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +6 -0
  5. data/INSTALL +20 -0
  6. data/LICENSE +61 -0
  7. data/README.md +148 -0
  8. data/README_FOR_HEROKU_ADDON.md +102 -0
  9. data/Rakefile +179 -0
  10. data/TESTED_AGAINST +7 -0
  11. data/airbrake.gemspec +41 -0
  12. data/bin/airbrake +12 -0
  13. data/features/metal.feature +34 -0
  14. data/features/rack.feature +60 -0
  15. data/features/rails.feature +324 -0
  16. data/features/rake.feature +33 -0
  17. data/features/sinatra.feature +126 -0
  18. data/features/step_definitions/file_steps.rb +14 -0
  19. data/features/step_definitions/rack_steps.rb +27 -0
  20. data/features/step_definitions/rails_application_steps.rb +267 -0
  21. data/features/step_definitions/rake_steps.rb +22 -0
  22. data/features/support/airbrake_shim.rb.template +11 -0
  23. data/features/support/aruba.rb +5 -0
  24. data/features/support/env.rb +39 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +156 -0
  27. data/features/support/rake/Rakefile +77 -0
  28. data/features/user_informer.feature +57 -0
  29. data/generators/airbrake/airbrake_generator.rb +94 -0
  30. data/generators/airbrake/lib/insert_commands.rb +34 -0
  31. data/generators/airbrake/lib/rake_commands.rb +24 -0
  32. data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
  33. data/generators/airbrake/templates/capistrano_hook.rb +6 -0
  34. data/generators/airbrake/templates/initializer.rb +4 -0
  35. data/install.rb +1 -0
  36. data/lib/airbrake.rb +191 -0
  37. data/lib/airbrake/backtrace.rb +103 -0
  38. data/lib/airbrake/capistrano.rb +103 -0
  39. data/lib/airbrake/capistrano3.rb +3 -0
  40. data/lib/airbrake/cli/client.rb +76 -0
  41. data/lib/airbrake/cli/options.rb +45 -0
  42. data/lib/airbrake/cli/printer.rb +33 -0
  43. data/lib/airbrake/cli/project.rb +17 -0
  44. data/lib/airbrake/cli/project_factory.rb +33 -0
  45. data/lib/airbrake/cli/runner.rb +49 -0
  46. data/lib/airbrake/cli/validator.rb +8 -0
  47. data/lib/airbrake/configuration.rb +366 -0
  48. data/lib/airbrake/jobs/send_job.rb +7 -0
  49. data/lib/airbrake/notice.rb +411 -0
  50. data/lib/airbrake/rack.rb +64 -0
  51. data/lib/airbrake/rails.rb +45 -0
  52. data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
  53. data/lib/airbrake/rails/controller_methods.rb +146 -0
  54. data/lib/airbrake/rails/error_lookup.rb +35 -0
  55. data/lib/airbrake/rails/middleware.rb +63 -0
  56. data/lib/airbrake/rails3_tasks.rb +126 -0
  57. data/lib/airbrake/railtie.rb +44 -0
  58. data/lib/airbrake/rake_handler.rb +75 -0
  59. data/lib/airbrake/response.rb +29 -0
  60. data/lib/airbrake/sender.rb +213 -0
  61. data/lib/airbrake/shared_tasks.rb +59 -0
  62. data/lib/airbrake/sidekiq.rb +8 -0
  63. data/lib/airbrake/sinatra.rb +40 -0
  64. data/lib/airbrake/tasks.rb +81 -0
  65. data/lib/airbrake/tasks/airbrake.cap +28 -0
  66. data/lib/airbrake/user_informer.rb +36 -0
  67. data/lib/airbrake/utils/params_cleaner.rb +141 -0
  68. data/lib/airbrake/utils/rack_filters.rb +45 -0
  69. data/lib/airbrake/version.rb +3 -0
  70. data/lib/airbrake_tasks.rb +62 -0
  71. data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
  72. data/lib/templates/rescue.erb +91 -0
  73. data/rails/init.rb +1 -0
  74. data/resources/README.md +34 -0
  75. data/resources/airbrake_2_4.xsd +89 -0
  76. data/resources/airbrake_3_0.json +52 -0
  77. data/resources/ca-bundle.crt +3376 -0
  78. data/script/integration_test.rb +35 -0
  79. data/test/airbrake_tasks_test.rb +161 -0
  80. data/test/backtrace_test.rb +215 -0
  81. data/test/capistrano_test.rb +44 -0
  82. data/test/configuration_test.rb +303 -0
  83. data/test/controller_methods_test.rb +230 -0
  84. data/test/helper.rb +233 -0
  85. data/test/integration.rb +13 -0
  86. data/test/integration/catcher_test.rb +371 -0
  87. data/test/logger_test.rb +79 -0
  88. data/test/notice_test.rb +494 -0
  89. data/test/notifier_test.rb +288 -0
  90. data/test/params_cleaner_test.rb +204 -0
  91. data/test/rack_test.rb +62 -0
  92. data/test/rails_initializer_test.rb +36 -0
  93. data/test/recursion_test.rb +10 -0
  94. data/test/response_test.rb +18 -0
  95. data/test/sender_test.rb +335 -0
  96. data/test/support/response_shim.xml +4 -0
  97. data/test/user_informer_test.rb +29 -0
  98. metadata +469 -0
@@ -0,0 +1,34 @@
1
+ # Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
2
+
3
+ Rails::Generator::Commands::Base.class_eval do
4
+ def file_contains?(relative_destination, line)
5
+ File.read(destination_path(relative_destination)).include?(line)
6
+ end
7
+ end
8
+
9
+ Rails::Generator::Commands::Create.class_eval do
10
+ def append_to(file, line)
11
+ logger.insert "#{line} appended to #{file}"
12
+ unless options[:pretend] || file_contains?(file, line)
13
+ File.open(file, "a") do |file|
14
+ file.puts
15
+ file.puts line
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Rails::Generator::Commands::Destroy.class_eval do
22
+ def append_to(file, line)
23
+ logger.remove "#{line} removed from #{file}"
24
+ unless options[:pretend]
25
+ gsub_file file, "\n#{line}", ''
26
+ end
27
+ end
28
+ end
29
+
30
+ Rails::Generator::Commands::List.class_eval do
31
+ def append_to(file, line)
32
+ logger.insert "#{line} appended to #{file}"
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ Rails::Generator::Commands::Create.class_eval do
2
+ def rake(cmd, opts = {})
3
+ logger.rake "rake #{cmd}"
4
+ unless system("rake #{cmd}")
5
+ logger.rake "#{cmd} failed. Rolling back"
6
+ command(:destroy).invoke!
7
+ end
8
+ end
9
+ end
10
+
11
+ Rails::Generator::Commands::Destroy.class_eval do
12
+ def rake(cmd, opts = {})
13
+ unless opts[:generate_only]
14
+ logger.rake "rake #{cmd}"
15
+ system "rake #{cmd}"
16
+ end
17
+ end
18
+ end
19
+
20
+ Rails::Generator::Commands::List.class_eval do
21
+ def rake(cmd, opts = {})
22
+ logger.rake "rake #{cmd}"
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # Don't load anything when running the gems:* tasks.
2
+ # Otherwise, airbrake will be considered a framework gem.
3
+ # https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629
4
+ unless ARGV.any? {|a| a =~ /^gems/}
5
+
6
+ Dir[File.join(Rails.root, 'vendor', 'gems', 'airbrake-*')].each do |vendored_notifier|
7
+ $: << File.join(vendored_notifier, 'lib')
8
+ end
9
+
10
+ begin
11
+ require 'airbrake/tasks'
12
+ rescue LoadError => exception
13
+ namespace :airbrake do
14
+ %w(deploy test log_stdout).each do |task_name|
15
+ desc "Missing dependency for airbrake:#{task_name}"
16
+ task task_name do
17
+ $stderr.puts "Failed to run airbrake:#{task_name} because of missing dependency."
18
+ $stderr.puts "You probably need to run `rake gems:install` to install the airbrake gem"
19
+ abort exception.inspect
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,6 @@
1
+
2
+ Dir[File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'airbrake-*')].each do |vendored_notifier|
3
+ $: << File.join(vendored_notifier, 'lib')
4
+ end
5
+
6
+ require 'airbrake/capistrano'
@@ -0,0 +1,4 @@
1
+ <% if ::Rails::VERSION::MAJOR < 3 && ::Rails::VERSION::MINOR < 2 -%>
2
+ require 'airbrake/rails'
3
+ <% end -%>
4
+ <%= configuration_output %>
@@ -0,0 +1 @@
1
+ puts IO.read(File.join(File.dirname(__FILE__), 'INSTALL'))
@@ -0,0 +1,191 @@
1
+ begin
2
+ require "girl_friday"
3
+ rescue LoadError
4
+ end
5
+
6
+ begin
7
+ require "sucker_punch"
8
+ rescue LoadError
9
+ end
10
+
11
+ require 'net/http'
12
+ require 'net/https'
13
+ require 'rubygems'
14
+ require 'logger'
15
+
16
+ require 'airbrake/version'
17
+ require 'airbrake/jobs/send_job'
18
+ require 'airbrake/utils/rack_filters'
19
+ require 'airbrake/utils/params_cleaner'
20
+ require 'airbrake/configuration'
21
+ require 'airbrake/notice'
22
+ require 'airbrake/sender'
23
+ require 'airbrake/response'
24
+ require 'airbrake/backtrace'
25
+ require 'airbrake/rack'
26
+ require 'airbrake/sinatra'
27
+ require 'airbrake/user_informer'
28
+
29
+ begin
30
+ require 'airbrake/sidekiq'
31
+ rescue LoadError
32
+ end
33
+
34
+ require 'airbrake/railtie' if defined?(Rails::Railtie)
35
+
36
+ module Airbrake
37
+ API_VERSION = "2.4"
38
+ LOG_PREFIX = "** [Airbrake] "
39
+
40
+ class << self
41
+ # The sender object is responsible for delivering formatted data to the Airbrake server.
42
+ # Must respond to #send_to_airbrake. See Airbrake::Sender.
43
+ attr_accessor :sender
44
+
45
+ # A Airbrake configuration object. Must act like a hash and return sensible
46
+ # values for all Airbrake configuration options. See Airbrake::Configuration.
47
+ attr_writer :configuration
48
+
49
+ # Tell the log that the Notifier is good to go
50
+ def report_ready
51
+ write_verbose_log("Notifier #{VERSION} ready to catch errors")
52
+ end
53
+
54
+ # Prints out the environment info to the log for debugging help
55
+ def report_environment_info
56
+ write_verbose_log("Environment Info: #{environment_info}")
57
+ end
58
+
59
+ # Prints out the response body from Airbrake for debugging help
60
+ def report_response_body(response)
61
+ write_verbose_log("Response from Airbrake: \n#{Response.pretty_format(response)}")
62
+ end
63
+
64
+ # Prints out the details about the notice that wasn't sent to server
65
+ def report_notice(notice)
66
+ write_verbose_log("Notice details: \n#{notice}")
67
+ end
68
+
69
+ def report_notice_not_sent_for_configuration
70
+ write_verbose_log("Notice was not sent due to configuration: \
71
+ \n Environment Monitored? #{configuration.public?} \
72
+ \n API key set? #{configuration.configured?}")
73
+ end
74
+
75
+ # Returns the Ruby version, Rails version, and current Rails environment
76
+ def environment_info
77
+ info = "[Ruby: #{RUBY_VERSION}]"
78
+ info << " [#{configuration.framework}]" if configuration.framework
79
+ info << " [Env: #{configuration.environment_name}]" if configuration.environment_name
80
+ info
81
+ end
82
+
83
+ # Writes out the given message to the #logger
84
+ def write_verbose_log(message)
85
+ logger.debug LOG_PREFIX + message if logger
86
+ end
87
+
88
+ # Look for the Rails logger currently defined
89
+ def logger
90
+ self.configuration.logger ||
91
+ Logger.new(nil)
92
+ end
93
+
94
+ # Call this method to modify defaults in your initializers.
95
+ #
96
+ # @example
97
+ # Airbrake.configure do |config|
98
+ # config.api_key = '1234567890abcdef'
99
+ # config.secure = false
100
+ # end
101
+ def configure(silent = false)
102
+ yield(configuration)
103
+ self.sender = if configuration.test_mode?
104
+ CollectingSender.new(configuration)
105
+ else
106
+ Sender.new(configuration)
107
+ end
108
+
109
+ report_ready unless silent
110
+ self.sender
111
+ end
112
+
113
+ # The configuration object.
114
+ # @see Airbrake.configure
115
+ def configuration
116
+ @configuration ||= Configuration.new
117
+ end
118
+
119
+ # Sends an exception manually using this method, even when you are not in a controller.
120
+ #
121
+ # @param [Exception] exception The exception you want to notify Airbrake about.
122
+ # @param [Hash] opts Data that will be sent to Airbrake.
123
+ #
124
+ # @option opts [String] :api_key The API key for this project. The API key is a unique identifier that Airbrake uses for identification.
125
+ # @option opts [String] :error_message The error returned by the exception (or the message you want to log).
126
+ # @option opts [String] :backtrace A backtrace, usually obtained with +caller+.
127
+ # @option opts [String] :rack_env The Rack environment.
128
+ # @option opts [String] :session The contents of the user's session.
129
+ # @option opts [String] :environment_name The application environment name.
130
+ # @option opts [String] :parameters Additional parameters.
131
+ def notify(exception, opts = {})
132
+ send_notice(build_notice_for(exception, opts))
133
+ end
134
+
135
+ # Sends the notice unless it is one of the default ignored exceptions
136
+ # @see Airbrake.notify
137
+ def notify_or_ignore(exception, opts = {})
138
+ notice = build_notice_for(exception, opts)
139
+ send_notice(notice) unless notice.ignore?
140
+ end
141
+
142
+ def build_lookup_hash_for(exception, options = {})
143
+ notice = build_notice_for(exception, options)
144
+
145
+ result = {}
146
+ result[:action] = notice.action rescue nil
147
+ result[:component] = notice.component rescue nil
148
+ result[:error_class] = notice.error_class if notice.error_class
149
+ result[:environment_name] = 'production'
150
+
151
+ unless notice.backtrace.lines.empty?
152
+ result[:file] = notice.backtrace.lines.first.file
153
+ result[:line_number] = notice.backtrace.lines.first.number
154
+ end
155
+
156
+ result
157
+ end
158
+
159
+ private
160
+
161
+ def send_notice(notice)
162
+ if configuration.configured? && configuration.public?
163
+ if configuration.async?
164
+ configuration.async.call(notice)
165
+ nil # make sure we never set env["airbrake.error_id"] for async notices
166
+ else
167
+ sender.send_to_airbrake(notice)
168
+ end
169
+ else
170
+ report_notice_not_sent_for_configuration
171
+ end
172
+ end
173
+
174
+ def build_notice_for(exception, opts = {})
175
+ exception_classes = [exception.class.to_s]
176
+ exception = unwrap_exception(exception)
177
+ opts = opts.merge(:exception => exception) if exception.is_a?(Exception)
178
+ opts = opts.merge(exception.to_hash) if exception.respond_to?(:to_hash)
179
+ opts = opts.merge(:exception_classes => exception_classes)
180
+ Notice.new(configuration.merge(opts))
181
+ end
182
+
183
+ def unwrap_exception(exception)
184
+ if exception.respond_to?(:original_exception)
185
+ exception.original_exception
186
+ elsif exception.respond_to?(:continued_exception)
187
+ exception.continued_exception
188
+ end || exception
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,103 @@
1
+ module Airbrake
2
+ # Front end to parsing the backtrace for each notice
3
+ class Backtrace
4
+
5
+ # Handles backtrace parsing line by line
6
+ class Line
7
+
8
+ # regexp (optionnally allowing leading X: for windows support)
9
+ INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
10
+
11
+ # The file portion of the line (such as app/models/user.rb)
12
+ attr_reader :file
13
+
14
+ # The line number portion of the line
15
+ attr_reader :number
16
+
17
+ # The method_name of the line (such as index)
18
+ attr_reader :method_name
19
+
20
+ # Parses a single line of a given backtrace
21
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
22
+ # @return [Line] The parsed backtrace line
23
+ def self.parse(unparsed_line)
24
+ _, file, number, method_name = unparsed_line.match(INPUT_FORMAT).to_a
25
+ new(file, number, method_name)
26
+ end
27
+
28
+ def initialize(file, number, method_name)
29
+ @file = file
30
+ @number = number
31
+ @method_name = method_name
32
+ end
33
+
34
+ # Reconstructs the line in a readable fashion
35
+ def to_s
36
+ "#{file}:#{number}:in `#{method_name}'"
37
+ end
38
+
39
+ def ==(other)
40
+ to_s == other.to_s
41
+ end
42
+
43
+ def inspect
44
+ "<Line:#{to_s}>"
45
+ end
46
+ end
47
+
48
+ # holder for an Array of Backtrace::Line instances
49
+ attr_reader :lines
50
+
51
+ def self.parse(ruby_backtrace, opts = {})
52
+ ruby_lines = split_multiline_backtrace(ruby_backtrace)
53
+
54
+ filters = opts[:filters] || []
55
+ filtered_lines = ruby_lines.to_a.map do |line|
56
+ filters.inject(line) do |l, proc|
57
+ proc.call(l)
58
+ end
59
+ end.compact
60
+
61
+ lines = filtered_lines.collect do |unparsed_line|
62
+ Line.parse(unparsed_line)
63
+ end
64
+
65
+ new(lines)
66
+ end
67
+
68
+ def initialize(lines)
69
+ @lines = lines
70
+ end
71
+
72
+ def inspect
73
+ "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
74
+ end
75
+
76
+ def to_s
77
+ content = []
78
+ lines.each do |line|
79
+ content << line
80
+ end
81
+ content.join("\n")
82
+ end
83
+
84
+ def ==(other)
85
+ if other.respond_to?(:lines)
86
+ lines == other.lines
87
+ else
88
+ false
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def self.split_multiline_backtrace(backtrace)
95
+ backtrace = [backtrace] unless backtrace.respond_to?(:to_a)
96
+ if backtrace.to_a.size == 1
97
+ backtrace.to_a.first.split(/\n\s*/)
98
+ else
99
+ backtrace
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,103 @@
1
+ # Defines deploy:notify_airbrake which will send information about the deploy to Airbrake.
2
+ require 'capistrano'
3
+
4
+ module Airbrake
5
+ module Capistrano
6
+ # What follows is a copy-paste backport of the shellescape method
7
+ # included in Ruby 1.9 and greater. The FSF's guidance on a snippet
8
+ # of this size indicates that such a small function is not subject
9
+ # to copyright and as such there is no risk of a license conflict:
10
+ # See www.gnu.org/prep/maintain/maintain.html#Legally-Significant
11
+ #
12
+ # Escapes a string so that it can be safely used in a Bourne shell
13
+ # command line. +str+ can be a non-string object that responds to
14
+ # +to_s+.
15
+ #
16
+ # Note that a resulted string should be used unquoted and is not
17
+ # intended for use in double quotes nor in single quotes.
18
+ #
19
+ # argv = Shellwords.escape("It's better to give than to receive")
20
+ # argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
21
+ #
22
+ # String#shellescape is a shorthand for this function.
23
+ #
24
+ # argv = "It's better to give than to receive".shellescape
25
+ # argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
26
+ #
27
+ # # Search files in lib for method definitions
28
+ # pattern = "^[ \t]*def "
29
+ # open("| grep -Ern #{pattern.shellescape} lib") { |grep|
30
+ # grep.each_line { |line|
31
+ # file, lineno, matched_line = line.split(':', 3)
32
+ # # ...
33
+ # }
34
+ # }
35
+ #
36
+ # It is the caller's responsibility to encode the string in the right
37
+ # encoding for the shell environment where this string is used.
38
+ #
39
+ # Multibyte characters are treated as multibyte characters, not bytes.
40
+ #
41
+ # Returns an empty quoted String if +str+ has a length of zero.
42
+ def self.shellescape(str)
43
+ str = str.to_s
44
+
45
+ # An empty argument will be skipped, so return empty quotes.
46
+ return "''" if str.empty?
47
+
48
+ str = str.dup
49
+
50
+ # Treat multibyte characters as is. It is caller's responsibility
51
+ # to encode the string in the right encoding for the shell
52
+ # environment.
53
+ str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
54
+
55
+ # A LF cannot be escaped with a backslash because a backslash + LF
56
+ # combo is regarded as line continuation and simply ignored.
57
+ str.gsub!(/\n/, "'\n'")
58
+
59
+ return str
60
+ end
61
+
62
+ def self.load_into(configuration)
63
+ configuration.load do
64
+ after "deploy", "airbrake:deploy"
65
+ after "deploy:migrations", "airbrake:deploy"
66
+ after "deploy:cold", "airbrake:deploy"
67
+
68
+ namespace :airbrake do
69
+ desc <<-DESC
70
+ Notify Airbrake of the deployment by running the notification on the REMOTE machine.
71
+ - Run remotely so we use remote API keys, environment, etc.
72
+ DESC
73
+ task :deploy, :except => { :no_release => true } do
74
+ rack_env = fetch(:rack_env, nil)
75
+ rails_env = fetch(:rails_env, nil)
76
+ airbrake_env = fetch(:airbrake_env, rack_env || rails_env || "production")
77
+ local_user = ENV['USER'] || ENV['USERNAME']
78
+ executable = RUBY_PLATFORM.downcase.include?('mswin') ? fetch(:rake, 'rake.bat') : fetch(:rake, 'bundle exec rake ')
79
+ directory = configuration.release_path
80
+ notify_command = "cd #{directory}; #{executable}"
81
+ notify_command << " RACK_ENV=#{rack_env}" if rack_env
82
+ notify_command << " RAILS_ENV=#{rails_env}" if rails_env
83
+ notify_command << " airbrake:deploy TO=#{airbrake_env} REVISION=#{current_revision} REPO=#{repository} USER=#{Airbrake::Capistrano::shellescape(local_user)}"
84
+ notify_command << " DRY_RUN=true" if dry_run
85
+ logger.info "Notifying Airbrake of Deploy (#{notify_command})"
86
+ if configuration.dry_run
87
+ logger.info "DRY RUN: Notification not actually run."
88
+ else
89
+ result = ""
90
+ run(notify_command, :once => true) { |ch, stream, data| result << data }
91
+ # TODO: Check if SSL is active on account via result content.
92
+ end
93
+ logger.info "Airbrake Notification Complete."
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ if Capistrano::Configuration.instance
102
+ Airbrake::Capistrano.load_into(Capistrano::Configuration.instance)
103
+ end