airbrake 5.0.5 → 5.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9fb3a24e2ef1921f245f81469badb794354224d
4
- data.tar.gz: b35c155e6ac5a49871aa16b40801e357f27205c1
3
+ metadata.gz: f2c700ad9e90a15de2d6482a6cc9429875fc9606
4
+ data.tar.gz: 74e37331ad0ebd00f2c3beede67c45480a5e607f
5
5
  SHA512:
6
- metadata.gz: cf060e37c464c0054d033b6e615a45645cfa43c56431689309042071e178779fcd3ebc4688207d062ee9d68cc805653f765c2bac3a7301763fb6203b4b3659d0
7
- data.tar.gz: 2dd49353df42dd92b7017b97cf0ccc888cff6ec21d886a531c21b1aefeb9fba886b7cf36038dc3290ae03b75b0541fc8292b8c6663886ee928849ed3d6f5544f
6
+ metadata.gz: 97c0ca4f52385e44a1512b911e2bfef8d64223916a9d52fafbd630bbdfdbc022f909a1772cfa6883e4d5ce9c1752627856e916e552a7a075db986c9d12da4fa3
7
+ data.tar.gz: abd13310879565f3aa327a7dd1564b89afe2573ad2ea66394c9c351835ab2855296a15142b1187500a230d71ee6fc784ad24b11ec3734ebe10dfe777372043b8
@@ -19,3 +19,29 @@ require 'airbrake/rake/task_ext' if defined?(Rake::Task)
19
19
  require 'airbrake/resque/failure' if defined?(Resque)
20
20
  require 'airbrake/sidekiq/error_handler' if defined?(Sidekiq)
21
21
  require 'airbrake/delayed_job/plugin' if defined?(Delayed)
22
+
23
+ ##
24
+ # This module reopens the original Airbrake module from airbrake-ruby and adds
25
+ # integration specific methods.
26
+ module Airbrake
27
+ class << self
28
+ ##
29
+ # Attaches a callback (builder) that runs every time the Rack integration
30
+ # reports an error. Can be used to attach additional data from the Rack
31
+ # request.
32
+ #
33
+ # @example Adding remote IP from the Rack environment
34
+ # Airbrake.add_rack_builder do |notice, request|
35
+ # notice[:params][:remoteIp] = request.env['REMOTE_IP']
36
+ # end
37
+ #
38
+ # @yieldparam notice [Airbrake::Notice] notice that will be sent to Airbrake
39
+ # @yieldparam request [Rack::Request] current rack request
40
+ # @yieldreturn [void]
41
+ # @return [void]
42
+ # @since 5.1.0
43
+ def add_rack_builder(&block)
44
+ Airbrake::Rack::NoticeBuilder.add_builder(&block)
45
+ end
46
+ end
47
+ end
@@ -9,7 +9,7 @@ if defined?(Capistrano::VERSION) &&
9
9
  with rails_env: fetch(:rails_env, fetch(:stage)) do
10
10
  execute :rake, <<-CMD
11
11
  airbrake:deploy USERNAME=#{Shellwords.shellescape(local_user)} \
12
- ENVIRONMENT=#{fetch(:rails_env, fetch(:stage))} \
12
+ ENVIRONMENT=#{fetch(:airbrake_env, fetch(:rails_env, fetch(:stage)))} \
13
13
  REVISION=#{fetch(:current_revision)} \
14
14
  REPOSITORY=#{fetch(:repo_url)} \
15
15
  VERSION=#{fetch(:app_version)}
@@ -26,9 +26,7 @@ module Airbrake
26
26
  end
27
27
  # rubocop:enable Lint/RescueException
28
28
 
29
- # The internal framework middlewares store exceptions inside the Rack
30
- # env. See: https://goo.gl/Kd694n
31
- exception = env['action_dispatch.exception'] || env['sinatra.error']
29
+ exception = framework_exception(env)
32
30
  notify_airbrake(exception, env) if exception
33
31
 
34
32
  response
@@ -40,6 +38,19 @@ module Airbrake
40
38
  notice = NoticeBuilder.new(env).build_notice(exception)
41
39
  Airbrake.notify(notice)
42
40
  end
41
+
42
+ ##
43
+ # Web framework middlewares often store rescued exceptions inside the
44
+ # Rack env, but Rack doesn't have a standard key for it:
45
+ #
46
+ # - Rails uses action_dispatch.exception: https://goo.gl/Kd694n
47
+ # - Sinatra uses sinatra.error: https://goo.gl/LLkVL9
48
+ # - Goliath uses rack.exception: https://goo.gl/i7e1nA
49
+ def framework_exception(env)
50
+ env['action_dispatch.exception'] ||
51
+ env['sinatra.error'] ||
52
+ env['rack.exception']
53
+ end
43
54
  end
44
55
  end
45
56
  end
@@ -4,6 +4,20 @@ module Airbrake
4
4
  # A helper class for filling notices with all sorts of useful information
5
5
  # coming from the Rack environment.
6
6
  class NoticeBuilder
7
+ @builders = []
8
+
9
+ class << self
10
+ ##
11
+ # @return [Array<#call>] the list of notice's builders
12
+ attr_reader :builders
13
+
14
+ ##
15
+ # Adds user defined builders to the chain.
16
+ def add_builder(&block)
17
+ @builders << block
18
+ end
19
+ end
20
+
7
21
  ##
8
22
  # @return [Array<String>] the prefixes of the majority of HTTP headers in
9
23
  # Rack (some prefixes match the header names for simplicity)
@@ -16,20 +30,7 @@ module Airbrake
16
30
  ##
17
31
  # @param [Hash{String=>Object}] rack_env The Rack environment
18
32
  def initialize(rack_env)
19
- @rack_env = rack_env
20
33
  @request = ::Rack::Request.new(rack_env)
21
- @controller = rack_env['action_controller.instance']
22
- @session = @request.session
23
- @user = Airbrake::Rack::User.extract(rack_env)
24
-
25
- @framework_version =
26
- if defined?(::Rails)
27
- "Rails/#{::Rails.version}"
28
- elsif defined?(::Sinatra)
29
- "Sinatra/#{Sinatra::VERSION}"
30
- else
31
- "Rack.version/#{::Rack.version} Rack.release/#{::Rack.release}"
32
- end.freeze
33
34
  end
34
35
 
35
36
  ##
@@ -39,64 +40,71 @@ module Airbrake
39
40
  # @return [Airbrake::Notice] the notice with extra information
40
41
  def build_notice(exception)
41
42
  notice = Airbrake.build_notice(exception)
42
-
43
- add_context(notice)
44
- add_session(notice)
45
- add_params(notice)
46
- add_environment(notice)
47
-
43
+ NoticeBuilder.builders.each { |builder| builder.call(notice, @request) }
48
44
  notice
49
45
  end
50
46
 
51
- private
52
-
53
- def add_context(notice)
47
+ # Adds context (url, user agent, framework version, controller, etc)
48
+ add_builder do |notice, request|
54
49
  context = notice[:context]
55
50
 
56
- context[:url] = @request.url
57
- context[:userAgent] = @request.user_agent
51
+ context[:url] = request.url
52
+ context[:userAgent] = request.user_agent
53
+
54
+ framework_version =
55
+ if defined?(::Rails)
56
+ "Rails/#{::Rails.version}"
57
+ elsif defined?(::Sinatra)
58
+ "Sinatra/#{Sinatra::VERSION}"
59
+ else
60
+ "Rack.version/#{::Rack.version} Rack.release/#{::Rack.release}"
61
+ end.freeze
58
62
 
59
63
  if context.key?(:version)
60
- context[:version] += " #{@framework_version}"
64
+ context[:version] += " #{framework_version}"
61
65
  else
62
- context[:version] = @framework_version
66
+ context[:version] = framework_version
63
67
  end
64
68
 
65
- if @controller
66
- context[:component] = @controller.controller_name
67
- context[:action] = @controller.action_name
69
+ controller = request.env['action_controller.instance']
70
+ if controller
71
+ context[:component] = controller.controller_name
72
+ context[:action] = controller.action_name
68
73
  end
69
74
 
70
- notice[:context].merge!(@user.as_json) if @user
75
+ user = Airbrake::Rack::User.extract(request.env)
76
+ notice[:context].merge!(user.as_json) if user
71
77
 
72
78
  nil
73
79
  end
74
80
 
75
- def add_session(notice)
76
- notice[:session] = @session if @session
81
+ # Adds session
82
+ add_builder do |notice, request|
83
+ session = request.session
84
+ notice[:session] = session if session
77
85
  end
78
86
 
79
- def add_params(notice)
80
- params = @request.env['action_dispatch.request.parameters']
87
+ # Adds request params
88
+ add_builder do |notice, request|
89
+ params = request.env['action_dispatch.request.parameters']
81
90
  notice[:params] = params if params
82
91
  end
83
92
 
84
- def add_environment(notice)
85
- notice[:environment] = {
86
- httpMethod: @request.request_method,
87
- referer: @request.referer,
88
- headers: request_headers
89
- }
90
- end
91
-
92
- def request_headers
93
- @rack_env.map.with_object({}) do |(key, value), headers|
93
+ # Adds http referer, method and headers to the environment
94
+ add_builder do |notice, request|
95
+ http_headers = request.env.map.with_object({}) do |(key, value), headers|
94
96
  if HTTP_HEADER_PREFIXES.any? { |prefix| key.to_s.start_with?(prefix) }
95
97
  headers[key] = value
96
98
  end
97
99
 
98
100
  headers
99
101
  end
102
+
103
+ notice[:environment].merge!(
104
+ httpMethod: request.request_method,
105
+ referer: request.referer,
106
+ headers: http_headers
107
+ )
100
108
  end
101
109
  end
102
110
  end
@@ -27,7 +27,7 @@ module Rake
27
27
  argv: ARGV.join(' ')
28
28
  }
29
29
 
30
- Airbrake.notify(notice)
30
+ Airbrake.notify_sync(notice)
31
31
  raise ex
32
32
  end
33
33
  # rubocop:enable Lint/RescueException
@@ -2,5 +2,5 @@
2
2
  # We use Semantic Versioning v2.0.0
3
3
  # More information: http://semver.org/
4
4
  module Airbrake
5
- AIRBRAKE_VERSION = '5.0.5'.freeze
5
+ AIRBRAKE_VERSION = '5.1.0'.freeze
6
6
  end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake do
4
+ describe ".add_rack_builder" do
5
+ let :builder do
6
+ proc { |_, _| nil }
7
+ end
8
+
9
+ after { Airbrake::Rack::NoticeBuilder.builders.delete(builder) }
10
+
11
+ it "adds new builder to the chain" do
12
+ expect { Airbrake.add_rack_builder(&builder) }.to change {
13
+ Airbrake::Rack::NoticeBuilder.builders.count
14
+ }.by(1)
15
+ end
16
+ end
17
+ end
@@ -57,7 +57,7 @@ RSpec.describe Airbrake::Rack::Middleware do
57
57
  end
58
58
  end
59
59
 
60
- ['action_dispatch.exception', 'sinatra.error'].each do |type|
60
+ ['rack.exception', 'action_dispatch.exception', 'sinatra.error'].each do |type|
61
61
  include_examples 'stored exception', type
62
62
  end
63
63
  end
@@ -31,5 +31,53 @@ RSpec.describe Airbrake::Rack::NoticeBuilder do
31
31
 
32
32
  expect(notice[:params]).to eq(params)
33
33
  end
34
+
35
+ it "adds CONTENT_TYPE, CONTENT_LENGTH and HTTP_* headers in the environment" do
36
+ headers = {
37
+ "HTTP_HOST" => "example.com",
38
+ "CONTENT_TYPE" => "text/html",
39
+ "CONTENT_LENGTH" => 100500
40
+ }
41
+ notice_builder = described_class.new(headers.dup)
42
+ notice = notice_builder.build_notice(AirbrakeTestError.new)
43
+ expect(notice[:environment][:headers]).to eq(headers)
44
+ end
45
+
46
+ it "skips headers that were not selected to be stored in the environment" do
47
+ headers = {
48
+ "HTTP_HOST" => "example.com",
49
+ "CONTENT_TYPE" => "text/html",
50
+ "CONTENT_LENGTH" => 100500
51
+ }
52
+ notice_builder = described_class.new(headers.merge("X-SOME-HEADER" => "value"))
53
+ notice = notice_builder.build_notice(AirbrakeTestError.new)
54
+ expect(notice[:environment][:headers]).to eq(headers)
55
+ end
56
+
57
+ it "preserves data that already has been added to the environment" do
58
+ headers = {
59
+ "HTTP_HOST" => "example.com",
60
+ "CONTENT_TYPE" => "text/html",
61
+ "CONTENT_LENGTH" => 100500
62
+ }
63
+ allow(Airbrake).to receive(:build_notice).and_wrap_original do |method, *args|
64
+ notice = method.call(*args)
65
+ notice[:environment]["SOME_KEY"] = "SOME_VALUE"
66
+ notice
67
+ end
68
+ notice_builder = described_class.new(headers)
69
+ notice = notice_builder.build_notice(AirbrakeTestError.new)
70
+ expect(notice[:environment]["SOME_KEY"]).to eq("SOME_VALUE")
71
+ end
72
+
73
+ it "runs user defined builders against notices" do
74
+ extended_class = described_class.dup
75
+ extended_class.add_builder do |notice, request|
76
+ notice[:params][:remoteIp] = request.env['REMOTE_IP']
77
+ end
78
+ notice_builder = extended_class.new('REMOTE_IP' => '127.0.0.1')
79
+ notice = notice_builder.build_notice(AirbrakeTestError.new)
80
+ expect(notice[:params][:remoteIp]).to eq("127.0.0.1")
81
+ end
34
82
  end
35
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.5
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbrake Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-10 00:00:00.000000000 Z
11
+ date: 2016-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: airbrake-ruby
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -230,25 +230,25 @@ specification_version: 4
230
230
  summary: Airbrake is an online tool that provides robust exception tracking in any
231
231
  of your Ruby applications.
232
232
  test_files:
233
+ - spec/airbrake_spec.rb
234
+ - spec/apps/rack/dummy_app.rb
235
+ - spec/apps/rails/dummy_app.rb
236
+ - spec/apps/rails/dummy_task.rake
237
+ - spec/apps/rails/logs/32.log
238
+ - spec/apps/rails/logs/40.log
239
+ - spec/apps/rails/logs/41.log
240
+ - spec/apps/rails/logs/42.log
241
+ - spec/apps/rails/logs/50.log
242
+ - spec/apps/sinatra/dummy_app.rb
233
243
  - spec/integration/rack/rack_spec.rb
234
- - spec/integration/sinatra/sinatra_spec.rb
235
244
  - spec/integration/rails/rails_spec.rb
236
245
  - spec/integration/rails/rake_spec.rb
237
246
  - spec/integration/shared_examples/rack_examples.rb
247
+ - spec/integration/sinatra/sinatra_spec.rb
238
248
  - spec/spec_helper.rb
239
- - spec/apps/rack/dummy_app.rb
240
- - spec/apps/sinatra/dummy_app.rb
241
- - spec/apps/rails/logs/40.log
242
- - spec/apps/rails/logs/42.log
243
- - spec/apps/rails/logs/50.log
244
- - spec/apps/rails/logs/32.log
245
- - spec/apps/rails/logs/41.log
246
- - spec/apps/rails/dummy_app.rb
247
- - spec/apps/rails/dummy_task.rake
248
- - spec/airbrake_spec.rb
249
249
  - spec/unit/rack/middleware_spec.rb
250
- - spec/unit/rack/user_spec.rb
251
250
  - spec/unit/rack/notice_builder_spec.rb
252
- - spec/unit/sidekiq/error_handler_spec.rb
251
+ - spec/unit/rack/user_spec.rb
253
252
  - spec/unit/rake/tasks_spec.rb
253
+ - spec/unit/sidekiq/error_handler_spec.rb
254
254
  has_rdoc: