airbrake 5.0.5 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: