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,3 @@
1
+ # Defines airbrake:deploy which will send information about the deploy to Airbrake.
2
+
3
+ load File.expand_path("../tasks/airbrake.cap", __FILE__)
@@ -0,0 +1,76 @@
1
+ require File.expand_path( "../runner", __FILE__)
2
+
3
+ module Client
4
+ extend self
5
+
6
+ def options
7
+ Runner.options
8
+ end
9
+
10
+ def fetch_projects
11
+ uri = URI.parse "http://#{options.account}.airbrake.io"\
12
+ "/data_api/v1/projects.xml?auth_token=#{options.auth_token}"
13
+ http = Net::HTTP.new(uri.host,uri.port)
14
+ request = Net::HTTP::Get.new(uri.request_uri)
15
+ response = http.request(request)
16
+ response.body
17
+ end
18
+
19
+ def create_project
20
+ uri = URI.parse "http://#{options.account}.airbrake.io"\
21
+ "/data_api/v1/projects.xml"
22
+ http = Net::HTTP.new(uri.host,uri.port)
23
+ request = Net::HTTP::Post.new(uri.request_uri)
24
+ request.set_form_data('project[name]' => options.name,'auth_token' => options.auth_token)
25
+ response = http.request(request)
26
+ response.body
27
+
28
+ print_project_response(response.body)
29
+ end
30
+
31
+ def create_deploy
32
+ uri = URI.parse "http://airbrake.io/deploys.txt"
33
+ http = Net::HTTP.new(uri.host,uri.port)
34
+ request = Net::HTTP::Post.new(uri.request_uri)
35
+ opts = { 'api_key' => options.api_key }.merge!(deploy_opts)
36
+ request.set_form_data(opts)
37
+ response = http.request(request)
38
+ puts response.message if response.respond_to?(:message)
39
+ end
40
+
41
+ def deploy_opts
42
+ opts = {}
43
+ ['rails_env', 'scm_revision', 'scm_repository', 'local_username'].each do |attr|
44
+ opts.merge!("deploy[#{attr}]" => options.send(attr))
45
+ end
46
+ opts
47
+ end
48
+
49
+ def print_projects
50
+ factory = ProjectFactory.new
51
+ projects = fetch_projects
52
+ factory.create_projects_from_xml(projects)
53
+ abort "No projects were fetched. Did you provide the correct auth token?" if projects.match(/error/m)
54
+ puts "\nProjects\n" + "".rjust(63,"#")
55
+ factory.projects.each do |project|
56
+ puts project
57
+ end
58
+ puts
59
+ end
60
+
61
+ def print_project_response(response)
62
+ case response
63
+ when /errors/
64
+ puts "Error creating project: #{response.gsub("\n","").scan(/.*<error[^>]*>(.*?)<\/error>.*/).last.first.gsub(/\s{1,}/," ")}"
65
+ when /project/
66
+ project = Project.new(:id => response[/<id[^>]*>(.*?)<\/id>/,1],
67
+ :name => response[/<name[^>]*>(.*?)<\/name>/,1],
68
+ :api_key => response[/<api-key[^>]*>(.*?)<\/api-key>/,1])
69
+ puts "\nProject details\n" + "".rjust(63,"#")
70
+ puts project
71
+ else
72
+ puts "Unexpected error. Please try again!\n"
73
+ puts response
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,45 @@
1
+ class Options
2
+
3
+ ATTRIBUTES = [:error, :message, :api_key, :host, :port, :auth_token, :name,
4
+ :account, :rails_env, :scm_revision, :scm_repository, :local_username]
5
+
6
+ ATTRIBUTES.each do |attribute|
7
+ attr_reader attribute
8
+ end
9
+
10
+ private
11
+
12
+ # You should not write to this from outside
13
+ ATTRIBUTES.each do |attribute|
14
+ attr_writer attribute
15
+ end
16
+
17
+ public
18
+
19
+ # Parses all the options passed and stores them in attributes
20
+ def initialize(array = [])
21
+ opts = Hash[*array]
22
+ self.error = opts.delete("-e") || opts.delete("--error") { RuntimeError }
23
+ self.message = opts.delete("-m") || opts.delete("--message") { "I've made a huge mistake" }
24
+ self.api_key = opts.delete("-k") || opts.delete("--api-key") || config_from_file.api_key || ENV["AIRBRAKE_API_KEY"]
25
+ self.host = opts.delete("-h") || opts.delete("--host") || config_from_file.host
26
+ self.port = opts.delete("-p") || opts.delete("--port") || config_from_file.port
27
+ self.auth_token = opts.delete("-t") || opts.delete("--auth-token") || ENV["AIRBRAKE_AUTH_TOKEN"]
28
+ self.name = opts.delete("-n") || opts.delete("--name")
29
+ self.account = opts.delete("-a") || opts.delete("--account") || ENV["AIRBRAKE_ACCOUNT"]
30
+ self.rails_env = opts.delete("-E") || opts.delete("--rails-env") || ENV["RAILS_ENV"] || "production"
31
+ self.scm_revision = opts.delete("-r") || opts.delete("--scm-revision") || ENV["REVISION"]
32
+ self.scm_repository = opts.delete("-R") || opts.delete("--scm-repository") || ENV["REPO"]
33
+ self.local_username = opts.delete("-u") || opts.delete("--local-username") || ENV["USER"]
34
+ opts
35
+ end
36
+
37
+ # Fallback to read from the initializer
38
+ def config_from_file
39
+ begin
40
+ load "config/initializers/airbrake.rb"
41
+ rescue LoadError
42
+ end
43
+ Airbrake.configuration
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ module Printer
2
+ def self.print(collection)
3
+ collection.each do |element|
4
+ puts element
5
+ end
6
+ end
7
+
8
+ def self.print_usage
9
+ puts <<-USAGE
10
+ Usage: airbrake [COMMAND] [OPTION]...
11
+ Commands:
12
+ raise # Raise an exception specified by ERROR and MESSAGE.
13
+ list # List all the projects for given AUTH_TOKEN and ACCOUNT.
14
+ create # Create a project with the given NAME.
15
+ deploy # Send a new deployment notification to a project that matches the API_KEY.
16
+
17
+ Options:
18
+ -e, [--error=ERROR] # Error class to raise. Default: RuntimeError
19
+ -m, [--message=MESSAGE] # Error message. Default: "I've made a huge mistake"
20
+ -k, [--api-key=API_KEY] # Api key of your Airbrake application
21
+ -h, [--host=HOST] # URL of the Airbrake API server. Default: api.airbrake.io
22
+ -p, [--port=PORT] # Port of the Airbrake API server. Default: 80
23
+ -t, [--auth-token=AUTH_TOKEN] # The auth token used for API requests
24
+ -a, [--account=ACCOUNT] # The account used for API requests
25
+ -n, [--name=NAME] # The name of the project you're trying to create
26
+ -E, [--rails-env=NAME] # The name of the environment you're deploying to. Default: production
27
+ -r, [--scm-revision=REVISION] # SCM revision for deployment info
28
+ -R, [--scm-repository=REPO] # SCM repository for deployment info
29
+ -u, [--local-username=USER] # The name of the user who is deploying
30
+ -h, [--help] # Show this usage
31
+ USAGE
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ class Project
2
+ attr_writer :name, :id, :api_key
3
+
4
+ def initialize(attributes = {})
5
+ attributes.keys.each do |key|
6
+ instance_variable_set("@#{key}",attributes[key])
7
+ end
8
+ end
9
+
10
+ def to_s
11
+ "#{@name}".rjust(20) + "(#{@id}):".rjust(10) + " #{@api_key}"
12
+ end
13
+
14
+ def valid?
15
+ @name && @id && @api_key
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path( "../project", __FILE__)
2
+ # Responsible for creating projects when needed.
3
+ # Creates them from XML received.
4
+ class ProjectFactory
5
+ attr_reader :project, :projects
6
+
7
+ def initialize
8
+ @project = Project.new
9
+ @projects = []
10
+ end
11
+
12
+ def create_projects_from_xml(xml)
13
+ xml.split("\n").each do |line|
14
+ /<name[^>]*>(.*)<\/name>/ =~ line
15
+ name = $1
16
+ project.name = name.capitalize if name
17
+ /<id[^>]*>(.*)<\/id>/ =~ line
18
+ id = $1
19
+ project.id = id if id
20
+ /<api-key[^>]*>(.*)<\/api-key>/ =~ line
21
+ api_key = $1
22
+ project.api_key = api_key if api_key
23
+ check_project
24
+ end
25
+ end
26
+
27
+ def check_project
28
+ if @project.valid?
29
+ projects << @project
30
+ @project = Project.new
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path( "../project_factory", __FILE__)
2
+ require File.expand_path( "../options", __FILE__)
3
+ require File.expand_path( "../validator", __FILE__)
4
+ require File.expand_path( "../printer", __FILE__)
5
+ require File.expand_path( "../client", __FILE__)
6
+
7
+ module Runner
8
+ extend Validator
9
+
10
+ extend self
11
+
12
+ attr_accessor :options
13
+
14
+ def run!(command, cli_options = {})
15
+
16
+ self.options = Options.new(cli_options)
17
+
18
+ case command
19
+ when 'raise'
20
+ validates :api_key
21
+ Airbrake.configure do |c|
22
+ c.api_key = options.api_key
23
+ c.host = options.host if options.host
24
+ c.port = options.port if options.port
25
+ c.secure = options.port.to_i == 443
26
+ end
27
+ exception_id = Airbrake.notify(:error_class => options.error,
28
+ :error_message => "#{options.error}: #{options.message}",
29
+ :cgi_data => ENV)
30
+ abort "Error sending exception to Airbrake server. Try again later." unless exception_id
31
+ puts "Exception sent successfully: http://airbrake.io/locate/#{exception_id}"
32
+
33
+ when "list"
34
+ validates :auth_token, :account
35
+ Client.print_projects
36
+
37
+ when "create"
38
+ validates :auth_token, :account
39
+ Client.create_project
40
+
41
+ when "deploy"
42
+ validates :api_key
43
+ Client.create_deploy
44
+
45
+ else
46
+ Printer.print_usage
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ module Validator
2
+ def validates(*attributes)
3
+ attributes.each do |attribute|
4
+ abort "You didn't provide #{attribute.to_s.upcase}"\
5
+ " so no API request was made." unless Runner.options.send(attribute)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,366 @@
1
+ module Airbrake
2
+ # Used to set up and modify settings for the notifier.
3
+ class Configuration
4
+
5
+ OPTIONS = [:api_key, :backtrace_filters, :development_environments,
6
+ :development_lookup, :environment_name, :host,
7
+ :http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters,
8
+ :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
9
+ :params_filters, :params_whitelist_filters, :project_root, :port, :protocol, :proxy_host,
10
+ :proxy_pass, :proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain,
11
+ :framework, :user_information, :rescue_rake_exceptions, :rake_environment_filters,
12
+ :test_mode].freeze
13
+
14
+ # The API key for your project, found on the project edit form.
15
+ attr_accessor :api_key
16
+
17
+ # The host to connect to (defaults to airbrake.io).
18
+ attr_accessor :host
19
+
20
+ # The port on which your Airbrake server runs (defaults to 443 for secure
21
+ # connections, 80 for insecure connections).
22
+ attr_writer :port
23
+
24
+ # +true+ for https connections, +false+ for http connections.
25
+ attr_accessor :secure
26
+
27
+ # +true+ to use whatever CAs OpenSSL has installed on your system. +false+ to use the ca-bundle.crt file included in Airbrake itself (reccomended and default)
28
+ attr_accessor :use_system_ssl_cert_chain
29
+
30
+ # The HTTP open timeout in seconds (defaults to 2).
31
+ attr_accessor :http_open_timeout
32
+
33
+ # The HTTP read timeout in seconds (defaults to 5).
34
+ attr_accessor :http_read_timeout
35
+
36
+ # The hostname of your proxy server (if using a proxy)
37
+ attr_accessor :proxy_host
38
+
39
+ # The port of your proxy server (if using a proxy)
40
+ attr_accessor :proxy_port
41
+
42
+ # The username to use when logging into your proxy server (if using a proxy)
43
+ attr_accessor :proxy_user
44
+
45
+ # The password to use when logging into your proxy server (if using a proxy)
46
+ attr_accessor :proxy_pass
47
+
48
+ # A list of parameters that should be filtered out of what is sent to Airbrake.
49
+ # By default, all "password" attributes will have their contents replaced.
50
+ attr_accessor :params_filters
51
+
52
+ # A list of whitelisted parameters that will be sent to Airbrake.
53
+ # All other parameters will be filtered and their content replaced.
54
+ # By default this list is empty (all parameters are whitelisted).
55
+ attr_accessor :params_whitelist_filters
56
+
57
+ # A list of filters for cleaning and pruning the backtrace. See #filter_backtrace.
58
+ attr_reader :backtrace_filters
59
+
60
+ # A list of filters for ignoring exceptions. See #ignore_by_filter.
61
+ attr_reader :ignore_by_filters
62
+
63
+ # A list of environment keys that will be ignored from what is sent to Airbrake server
64
+ # Empty by default and used only in rake handler
65
+ attr_reader :rake_environment_filters
66
+
67
+ # A list of exception classes to ignore during server requests. The array can be appended to.
68
+ attr_reader :ignore
69
+
70
+ # A list of exception classes to ignore during Rake tasks. The array can be appended to.
71
+ attr_reader :ignore_rake
72
+
73
+ # A list of user agents that are being ignored. The array can be appended to.
74
+ attr_reader :ignore_user_agent
75
+
76
+ # A list of environments in which notifications should not be sent.
77
+ attr_accessor :development_environments
78
+
79
+ # +true+ if you want to check for production errors matching development errors, +false+ otherwise.
80
+ attr_accessor :development_lookup
81
+
82
+ # The name of the environment the application is running in
83
+ attr_accessor :environment_name
84
+
85
+ # The path to the project in which the error occurred, such as the Rails.root
86
+ attr_accessor :project_root
87
+
88
+ # The name of the notifier library being used to send notifications (such as "Airbrake Notifier")
89
+ attr_accessor :notifier_name
90
+
91
+ # The version of the notifier library being used to send notifications (such as "1.0.2")
92
+ attr_accessor :notifier_version
93
+
94
+ # The url of the notifier library being used to send notifications
95
+ attr_accessor :notifier_url
96
+
97
+ # The logger used by Airbrake
98
+ attr_accessor :logger
99
+
100
+ # The text that the placeholder is replaced with. {{error_id}} is the actual error number.
101
+ attr_accessor :user_information
102
+
103
+ # The framework Airbrake is configured to use
104
+ attr_accessor :framework
105
+
106
+ # Should Airbrake catch exceptions from Rake tasks?
107
+ # (boolean or nil; set to nil to catch exceptions when rake isn't running from a terminal; default is nil)
108
+ attr_accessor :rescue_rake_exceptions
109
+ alias_method :rescue_rake_exceptions?, :rescue_rake_exceptions
110
+
111
+ # User attributes that are being captured
112
+ attr_reader :user_attributes
113
+
114
+ # Only used for JSON API
115
+ attr_reader :project_id
116
+
117
+ # Setting this to true will use the CollectingSender instead of
118
+ # the default one which will cause storing the last notice locally
119
+ attr_accessor :test_mode
120
+ alias_method :test_mode?, :test_mode
121
+
122
+ DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
123
+ DEFAULT_PARAMS_WHITELIST_FILTERS = [].freeze
124
+
125
+ DEFAULT_USER_ATTRIBUTES = %w(id).freeze
126
+
127
+ VALID_USER_ATTRIBUTES = %w(id name username email).freeze
128
+
129
+ DEFAULT_BACKTRACE_FILTERS = [
130
+ lambda { |line|
131
+ if defined?(Airbrake.configuration.project_root) && Airbrake.configuration.project_root.to_s != ''
132
+ line.sub(/#{Airbrake.configuration.project_root}/, "[PROJECT_ROOT]")
133
+ else
134
+ line
135
+ end
136
+ },
137
+ lambda { |line| line.gsub(/^\.\//, "") },
138
+ lambda { |line|
139
+ Gem.path.each{ |path| line.sub!(/#{path}/, "[GEM_ROOT]") unless path.to_s.strip.empty? } if defined?(Gem)
140
+ line
141
+ },
142
+ lambda { |line| line if line !~ %r{lib/airbrake} }
143
+ ].freeze
144
+
145
+ IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
146
+ 'ActionController::RoutingError',
147
+ 'ActionController::InvalidAuthenticityToken',
148
+ 'CGI::Session::CookieStore::TamperedWithCookie',
149
+ 'ActionController::UnknownHttpMethod',
150
+ 'ActionController::UnknownAction',
151
+ 'AbstractController::ActionNotFound',
152
+ 'Mongoid::Errors::DocumentNotFound',
153
+ 'ActionController::UnknownFormat']
154
+
155
+ alias_method :secure?, :secure
156
+ alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
157
+
158
+ def initialize
159
+ @secure = false
160
+ @use_system_ssl_cert_chain= false
161
+ @host = 'api.airbrake.io'
162
+ @port = nil
163
+ @http_open_timeout = 2
164
+ @http_read_timeout = 5
165
+ @params_filters = DEFAULT_PARAMS_FILTERS.dup
166
+ @params_whitelist_filters = DEFAULT_PARAMS_WHITELIST_FILTERS.dup
167
+ @backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
168
+ @ignore_by_filters = [] # These filters are applied to both server requests and Rake tasks
169
+ @ignore = IGNORE_DEFAULT.dup
170
+ @ignore_rake = [] # Rake tasks don't ignore any exception classes by default
171
+ @ignore_user_agent = []
172
+ @development_environments = %w(development test cucumber)
173
+ @development_lookup = true
174
+ @notifier_name = 'Airbrake Notifier'
175
+ @notifier_version = VERSION
176
+ @notifier_url = 'https://github.com/airbrake/airbrake'
177
+ @framework = 'Standalone'
178
+ @user_information = 'Airbrake Error {{error_id}}'
179
+ @rescue_rake_exceptions = nil
180
+ @user_attributes = DEFAULT_USER_ATTRIBUTES.dup
181
+ @rake_environment_filters = []
182
+ @async = nil
183
+ end
184
+
185
+ # Takes a block and adds it to the list of backtrace filters. When the filters
186
+ # run, the block will be handed each line of the backtrace and can modify
187
+ # it as necessary.
188
+ #
189
+ # @example
190
+ # config.filter_bracktrace do |line|
191
+ # line.gsub(/^#{Rails.root}/, "[Rails.root]")
192
+ # end
193
+ #
194
+ # @param [Proc] block The new backtrace filter.
195
+ # @yieldparam [String] line A line in the backtrace.
196
+ def filter_backtrace(&block)
197
+ self.backtrace_filters << block
198
+ end
199
+
200
+ # Takes a block and adds it to the list of ignore filters.
201
+ # When the filters run, the block will be handed the exception.
202
+ # @example
203
+ # config.ignore_by_filter do |exception_data|
204
+ # true if exception_data[:error_class] == "RuntimeError"
205
+ # end
206
+ #
207
+ # @param [Proc] block The new ignore filter
208
+ # @yieldparam [Hash] data The exception data given to +Airbrake.notify+
209
+ # @yieldreturn [Boolean] If the block returns true the exception will be ignored, otherwise it will be processed by airbrake.
210
+ def ignore_by_filter(&block)
211
+ self.ignore_by_filters << block
212
+ end
213
+
214
+ # Overrides the list of default ignored errors.
215
+ #
216
+ # @param [Array<Exception>] names A list of exceptions to ignore.
217
+ def ignore_only=(names)
218
+ @ignore = [names].flatten
219
+ end
220
+
221
+ # Overrides the list of default ignored errors during Rake tasks.
222
+ #
223
+ # @param [Array<Exception>] names A list of rake exceptions to ignore.
224
+ def ignore_rake_only=(names)
225
+ @ignore_rake = [names].flatten
226
+ end
227
+
228
+ # Overrides the list of default ignored user agents
229
+ #
230
+ # @param [Array<String>] A list of user agents to ignore
231
+ def ignore_user_agent_only=(names)
232
+ @ignore_user_agent = [names].flatten
233
+ end
234
+
235
+ # Allows config options to be read like a hash
236
+ #
237
+ # @param [Symbol] option Key for a given attribute
238
+ def [](option)
239
+ send(option)
240
+ end
241
+
242
+ # Returns a hash of all configurable options
243
+ def to_hash
244
+ OPTIONS.inject({}) do |hash, option|
245
+ hash[option.to_sym] = self.send(option)
246
+ hash
247
+ end
248
+ end
249
+
250
+ # Returns a hash of all configurable options merged with +hash+
251
+ #
252
+ # @param [Hash] hash A set of configuration options that will take precedence over the defaults
253
+ def merge(hash)
254
+ to_hash.merge(hash)
255
+ end
256
+
257
+ # Determines if the notifier will send notices.
258
+ # @return [Boolean] Returns +true+ if an api string exists, +false+ otherwise.
259
+ def configured?
260
+ !api_key.nil? && !api_key.empty?
261
+ end
262
+
263
+ # Determines if the notifier will send notices.
264
+ # @return [Boolean] Returns +false+ if in a development environment, +true+ otherwise.
265
+ def public?
266
+ @public ||= !development_environments.include?(environment_name)
267
+ end
268
+
269
+ def port
270
+ @port || default_port
271
+ end
272
+
273
+ # Determines whether protocol should be "http" or "https".
274
+ # @return [String] Returns +"http"+ if you've set secure to +false+ in
275
+ # configuration, and +"https"+ otherwise.
276
+ def protocol
277
+ if secure?
278
+ 'https'
279
+ else
280
+ 'http'
281
+ end
282
+ end
283
+
284
+ def user_attributes=(user_attributes)
285
+ @user_attributes = validate_user_attributes user_attributes
286
+ end
287
+
288
+ # Should Airbrake send notifications asynchronously
289
+ # (boolean, nil or callable; default is nil).
290
+ # Can be used as callable-setter when block provided.
291
+ def async(&block)
292
+ if block_given?
293
+ @async = block
294
+ end
295
+ @async
296
+ end
297
+ alias_method :async?, :async
298
+
299
+ def async=(use_default_or_this)
300
+ @async = use_default_or_this == true ?
301
+ default_async_processor :
302
+ use_default_or_this
303
+ end
304
+
305
+ def rescue_rake_exceptions=(val)
306
+ if val && !defined?(Airbrake::RakeHandler)
307
+ raise LoadError, "you must require 'airbrake/rake_handler' to rescue from rake exceptions"
308
+ end
309
+ @rescue_rake_exceptions = val
310
+ end
311
+
312
+ def ca_bundle_path
313
+ if use_system_ssl_cert_chain?
314
+ OpenSSL::X509::DEFAULT_CERT_FILE if File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
315
+ else
316
+ local_cert_path # ca-bundle.crt built from source, see resources/README.md
317
+ end
318
+ end
319
+
320
+ def local_cert_path
321
+ File.expand_path(File.join("..", "..", "..", "resources", "ca-bundle.crt"), __FILE__)
322
+ end
323
+
324
+ def project_id=(project_id)
325
+ @project_id = "#{project_id}"
326
+ end
327
+
328
+ private
329
+ # Determines what port should we use for sending notices.
330
+ # @return [Fixnum] Returns 443 if you've set secure to true in your
331
+ # configuration, and 80 otherwise.
332
+ def default_port
333
+ if secure?
334
+ 443
335
+ else
336
+ 80
337
+ end
338
+ end
339
+
340
+ # Async notice delivery defaults to girl friday
341
+ def default_async_processor
342
+ if defined?(SuckerPunch)
343
+ lambda {|notice| SendJob.new.async.perform(notice)}
344
+ elsif defined?(GirlFriday)
345
+ queue = GirlFriday::WorkQueue.new(nil, :size => 3) do |notice|
346
+ Airbrake.sender.send_to_airbrake(notice)
347
+ end
348
+ lambda {|notice| queue << notice}
349
+ else
350
+ warn "[AIRBRAKE] You can't use the default async handler without sucker_punch or girl_friday."\
351
+ " Please make sure you have sucker_punch or girl_friday installed (sucker_punch is recommended)."
352
+ end
353
+ end
354
+
355
+ def validate_user_attributes(user_attributes)
356
+ user_attributes.reject do |attribute|
357
+ unless VALID_USER_ATTRIBUTES.include? attribute.to_s
358
+ warn "[AIRBRAKE] Unsupported user attribute: '#{attribute}'. "\
359
+ "This attribute will not be shown in the Airbrake UI. "\
360
+ "Check http://git.io/h6YRpA for more info."
361
+ true
362
+ end
363
+ end
364
+ end
365
+ end
366
+ end