airbrake 7.4.0 → 7.5.0.pre.1

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: ac1ef23c5180983961f8a55e150443cb90ec92aa
4
- data.tar.gz: ed676f689b5b7fc965a7235f8b7c114ada734079
3
+ metadata.gz: daaa847e504e6c5ce90d20d14abbfe22673742c8
4
+ data.tar.gz: bf1064650bc6e0ea0ac33913bb8f1ec27b914b8a
5
5
  SHA512:
6
- metadata.gz: ebd1afea0eece28fe5e8b1bdd63e886f48e8150d6a2bf2a504aa1fac7f9f60c75b65483335be73b729d5f6ce9dc4b7b7bad71a60a0d311dba0d9df39182edf4b
7
- data.tar.gz: 8268bbc33ec9bbbfd9f1a33de99afe3c45d0aedff28a131a902dc739b065004d1295d0ed8be57687e60f8ce8c0954a1cc1bdeebcd315f4b1c4766022c050be82
6
+ metadata.gz: e9ca5aab85564501b4035eae6d3263c8ce2d0b3d2353b04326d9aa7c1c8538d5a2c226055b04a46c33b4e5c7260e6527ce89a34d5544a7e34371304118c0c269
7
+ data.tar.gz: 2b04e5b8828ae024ea513c0e8189f95490bdacdbaa9b511d121a8691f921de756968bde759e28673eb53feb5c477aac7d02896657ca5d23d5b6b984779d1a929
@@ -4,4 +4,5 @@ require 'airbrake/rack/session_filter'
4
4
  require 'airbrake/rack/http_params_filter'
5
5
  require 'airbrake/rack/http_headers_filter'
6
6
  require 'airbrake/rack/request_body_filter'
7
+ require 'airbrake/rack/route_filter'
7
8
  require 'airbrake/rack/middleware'
@@ -6,6 +6,8 @@ module Airbrake
6
6
  #
7
7
  # The middleware automatically sends information about the framework that
8
8
  # uses it (name and version).
9
+ #
10
+ # For Rails apps the middleware collects route performance statistics.
9
11
  class Middleware
10
12
  # @return [Array<Class>] the list of Rack filters that read Rack request
11
13
  # information and append it to notices
@@ -13,7 +15,8 @@ module Airbrake
13
15
  Airbrake::Rack::ContextFilter,
14
16
  Airbrake::Rack::SessionFilter,
15
17
  Airbrake::Rack::HttpParamsFilter,
16
- Airbrake::Rack::HttpHeadersFilter
18
+ Airbrake::Rack::HttpHeadersFilter,
19
+ Airbrake::Rack::RouteFilter,
17
20
 
18
21
  # Optional filters (must be included by users):
19
22
  # Airbrake::Rack::RequestBodyFilter
@@ -37,6 +40,9 @@ module Airbrake
37
40
  RACK_FILTERS.each do |filter|
38
41
  @notifier.add_filter(filter.new)
39
42
  end
43
+
44
+ return unless defined?(Rails)
45
+ subscribe_route_stats_hook
40
46
  end
41
47
 
42
48
  # Rescues any exceptions, sends them to Airbrake and re-raises the
@@ -69,6 +75,8 @@ module Airbrake
69
75
  notice.stash[:rack_request] =
70
76
  if defined?(ActionDispatch::Request)
71
77
  ActionDispatch::Request.new(env)
78
+ elsif defined?(Sinatra::Request)
79
+ Sinatra::Request.new(env)
72
80
  else
73
81
  ::Rack::Request.new(env)
74
82
  end
@@ -87,6 +95,50 @@ module Airbrake
87
95
  env['sinatra.error'] ||
88
96
  env['rack.exception']
89
97
  end
98
+
99
+ def subscribe_route_stats_hook
100
+ ActiveSupport::Notifications.subscribe(
101
+ 'process_action.action_controller'
102
+ ) do |*args|
103
+ @all_routes ||= find_all_routes
104
+
105
+ event = ActiveSupport::Notifications::Event.new(*args)
106
+ payload = event.payload
107
+
108
+ if (route = find_route(payload[:params]))
109
+ @notifier.inc_request(
110
+ payload[:method],
111
+ route,
112
+ payload[:status],
113
+ event.duration,
114
+ event.time
115
+ )
116
+ else
117
+ @config.logger.info(
118
+ "#{LOG_LABEL} Rack::Middleware#route_stats_hook: couldn't find " \
119
+ "a route for path: #{payload[:path]}"
120
+ )
121
+ end
122
+ end
123
+ end
124
+
125
+ def find_route(params)
126
+ @all_routes.each do |r|
127
+ if r.defaults[:controller] == params['controller'] &&
128
+ r.defaults[:action] == params['action']
129
+ return r.path.spec.to_s
130
+ end
131
+ end
132
+ end
133
+
134
+ # Finds all routes that the app supports, including engines.
135
+ def find_all_routes
136
+ routes = [*::Rails.application.routes.routes.routes]
137
+ ::Rails::Engine.subclasses.each do |engine|
138
+ routes.push(*engine.routes.routes.routes)
139
+ end
140
+ routes
141
+ end
90
142
  end
91
143
  end
92
144
  end
@@ -0,0 +1,45 @@
1
+ module Airbrake
2
+ module Rack
3
+ # Adds route slugs to context/route.
4
+ # @since v7.5.0
5
+ class RouteFilter
6
+ attr_reader :weight
7
+
8
+ def initialize
9
+ @weight = 100
10
+ end
11
+
12
+ def call(notice)
13
+ return unless (request = notice.stash[:rack_request])
14
+
15
+ notice[:context][:route] =
16
+ if defined?(ActionDispatch::Request) &&
17
+ request.instance_of?(ActionDispatch::Request)
18
+ rails_route(request)
19
+ elsif defined?(Sinatra::Request) &&
20
+ request.instance_of?(Sinatra::Request)
21
+ sinatra_route(request)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def rails_route(request)
28
+ ::Rails.application.routes.router.recognize(request) do |route, _parameters|
29
+ # Rails can recognize multiple routes for the given request. For
30
+ # example, if we visit /users/2/edit, then Rails sees these routes:
31
+ # * "/users/:id/edit(.:format)"
32
+ # * "/"
33
+ #
34
+ # We return the first route as, what it seems, the most optimal
35
+ # approach.
36
+ return route.path.spec.to_s
37
+ end
38
+ end
39
+
40
+ def sinatra_route(request)
41
+ request.env['sinatra.route'].split(' ').drop(1).join(' ')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # We use Semantic Versioning v2.0.0
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
- AIRBRAKE_VERSION = '7.4.0'.freeze
4
+ AIRBRAKE_VERSION = '7.5.0.pre.1'.freeze
5
5
  end
@@ -8,6 +8,12 @@ RSpec.describe "Rails integration specs" do
8
8
 
9
9
  include_examples 'rack examples'
10
10
 
11
+ # Airbrake Ruby has a background thread that sends requests to
12
+ # `/routes-stats` periodically. We don't want this to get in the way.
13
+ before do
14
+ allow(Airbrake[:default]).to receive(:inc_request).and_return(nil)
15
+ end
16
+
11
17
  if ::Rails.version.start_with?('5.')
12
18
  it "inserts the Airbrake Rack middleware after DebugExceptions" do
13
19
  middlewares = Rails.configuration.middleware.middlewares.map(&:inspect)
@@ -68,6 +74,10 @@ RSpec.describe "Rails integration specs" do
68
74
  /"context":{.*"params":{.*"controller":"dummy","action":"#{action}".*}/
69
75
  )
70
76
  end
77
+
78
+ it "includes route" do
79
+ wait_for_a_request_with_body(/"context":{.*"route":"#{route}\(\.:format\)".*}/)
80
+ end
71
81
  end
72
82
 
73
83
  describe "context payload" do
@@ -273,4 +283,27 @@ RSpec.describe "Rails integration specs" do
273
283
  end
274
284
  end
275
285
  end
286
+
287
+ describe "the route stats hook" do
288
+ let(:routes_endpoint) do
289
+ 'https://api.airbrake.io/api/v4/projects/113743/routes-stats'
290
+ end
291
+
292
+ before do
293
+ stub_request(:put, routes_endpoint).to_return(status: 200, body: '')
294
+ end
295
+
296
+ it "increments the request count" do
297
+ expect(Airbrake[:default]).to receive(:inc_request).and_call_original
298
+
299
+ get '/crash'
300
+ sleep 2
301
+
302
+ wait_for_a_request_with_body(/"errors"/)
303
+
304
+ body = %r|{"routes":\[{"method":"GET","route":"\/crash\(\.:format\)"|
305
+ wait_for(a_request(:put, routes_endpoint).with(body: body)).
306
+ to have_been_made.once
307
+ end
308
+ end
276
309
  end
@@ -106,7 +106,7 @@ RSpec.describe "Rake integration" do
106
106
 
107
107
  it "includes argv info" do
108
108
  wait_for_a_request_with_body(
109
- %r("params":{.*"argv":"--pattern spec/integration/rails/\*_spec.rb".*})
109
+ %r("params":{.*"argv":".*spec/integration/rails/.+_spec.rb".*})
110
110
  )
111
111
  end
112
112
 
@@ -17,6 +17,11 @@ RSpec.describe "Sinatra integration specs" do
17
17
  get '/crash'
18
18
  wait_for_a_request_with_body(/"context":{.*"versions":{"sinatra":"\d\./)
19
19
  end
20
+
21
+ it "includes route" do
22
+ get '/crash'
23
+ wait_for_a_request_with_body(%r("context":{.*"route":"\/crash".*}))
24
+ end
20
25
  end
21
26
 
22
27
  context "when multiple apps are mounted" do
@@ -16,6 +16,7 @@ Airbrake.configure do |c|
16
16
  c.logger = Logger.new('/dev/null')
17
17
  c.app_version = '1.2.3'
18
18
  c.workers = 5
19
+ c.route_stats_flush_period = 1
19
20
  end
20
21
 
21
22
  RSpec.configure do |c|
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake::Rack::RouteFilter do
4
+ context "when there's no request object available" do
5
+ it "doesn't add context/route" do
6
+ notice = Airbrake.build_notice('oops')
7
+ subject.call(notice)
8
+ expect(notice[:context][:route]).to be_nil
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.4.0
4
+ version: 7.5.0.pre.1
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: 2018-10-11 00:00:00.000000000 Z
11
+ date: 2018-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: airbrake-ruby
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: '2.12'
19
+ version: 2.13.0.pre.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: '2.12'
26
+ version: 2.13.0.pre.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -254,6 +254,7 @@ files:
254
254
  - lib/airbrake/rack/http_params_filter.rb
255
255
  - lib/airbrake/rack/middleware.rb
256
256
  - lib/airbrake/rack/request_body_filter.rb
257
+ - lib/airbrake/rack/route_filter.rb
257
258
  - lib/airbrake/rack/session_filter.rb
258
259
  - lib/airbrake/rack/user.rb
259
260
  - lib/airbrake/rails.rb
@@ -288,6 +289,7 @@ files:
288
289
  - spec/unit/rack/http_params_filter_spec.rb
289
290
  - spec/unit/rack/middleware_spec.rb
290
291
  - spec/unit/rack/request_body_filter_spec.rb
292
+ - spec/unit/rack/route_filter_spec.rb
291
293
  - spec/unit/rack/session_filter_spec.rb
292
294
  - spec/unit/rack/user_spec.rb
293
295
  - spec/unit/rake/tasks_spec.rb
@@ -310,9 +312,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
310
312
  version: '2.0'
311
313
  required_rubygems_version: !ruby/object:Gem::Requirement
312
314
  requirements:
313
- - - ">="
315
+ - - ">"
314
316
  - !ruby/object:Gem::Version
315
- version: '0'
317
+ version: 1.3.1
316
318
  requirements: []
317
319
  rubyforge_project:
318
320
  rubygems_version: 2.6.13
@@ -335,6 +337,7 @@ test_files:
335
337
  - spec/unit/rack/session_filter_spec.rb
336
338
  - spec/unit/rack/context_filter_spec.rb
337
339
  - spec/unit/rack/user_spec.rb
340
+ - spec/unit/rack/route_filter_spec.rb
338
341
  - spec/integration/sinatra/sinatra_spec.rb
339
342
  - spec/integration/rack/rack_spec.rb
340
343
  - spec/integration/shared_examples/rack_examples.rb