airbrake 12.0.0 → 13.0.2

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
  SHA256:
3
- metadata.gz: c85c0e09edeb08efee5cabc43e11d56a02fb9cacbb2c0e415b81d0effa3f27b1
4
- data.tar.gz: e6c403b7219e58f8e296f9a2dddc510a6749b651e52569b995db6ff8b5e71b86
3
+ metadata.gz: 553d46f184e67e783a37345e049d4f7bb429520d27d732d2b732ab42ea4a5314
4
+ data.tar.gz: e162a0342478e68ec608faf5c3c8d033dd9c41a0956da1a08d26a040281fff2d
5
5
  SHA512:
6
- metadata.gz: be4e1cffdf8560bc786970f5db60fd6a85961a55ea7d5f1c87533ef338d30a0f462909879977b91e3d921f4ed4b3b616ca0fcdbcc4d18c0c5ccb01841b00be5d
7
- data.tar.gz: 28c44be64dd701b8afbf286b3dbced4756b6132b807ffc00e8a89c269d91b5e6b268a5e983e87b50fc00a524bc8a71f8e6a5da15bd7d385ce0a0f9d6d141b623
6
+ metadata.gz: 0452b7f7d9acb6af56babd7b44cf3bc741de12d218c3d07e25be4de67ab3576ab8ba88a30a620abe9a3b419a9d24d86428583ddcb3858a2755f5b20ad0020b89
7
+ data.tar.gz: fba302f11402c2c066d2d909b5c5cb2988f4071940fdb4a32e32e30f4b62fce4b683f322c72736fff0e24df64af603953ec32b9ed01941be658ca322ab6ee499
@@ -21,7 +21,7 @@ module Airbrake
21
21
  RAILS_ENV=#{fetch(:rails_env, nil)} \
22
22
 
23
23
  bundle exec rake airbrake:deploy \
24
- USERNAME=#{Shellwords.shellescape(ENV['USER'] || ENV['USERNAME'])} \
24
+ USERNAME=#{Shellwords.shellescape(ENV.fetch('USER', nil) || ENV.fetch('USERNAME', nil))} \
25
25
  ENVIRONMENT=#{fetch(:airbrake_env, fetch(:rails_env, 'production'))} \
26
26
  REVISION=#{current_revision.strip} \
27
27
  REPOSITORY=#{repository} \
@@ -40,12 +40,12 @@ module Airbrake
40
40
  # Skip "catch-all" routes such as:
41
41
  # get '*path => 'pages#about'
42
42
  #
43
- # @todo The `glob?` method was added in Rails v4.2.0.beta1. We
44
- # should remove the `respond_to?` check once we drop old Rails
45
- # versions support.
43
+ # Ideally, we should be using `route.glob?` but in Rails 7+ this
44
+ # call would fail with a `NoMethodError`. This is because in
45
+ # Rails 7+ the AST for the route is not kept in memory anymore.
46
46
  #
47
- # https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f
48
- next if route.respond_to?(:glob?) && route.glob?
47
+ # See: https://github.com/rails/rails/pull/43006#discussion_r783895766
48
+ next if route.path.spec.any?(ActionDispatch::Journey::Nodes::Star)
49
49
 
50
50
  path =
51
51
  if engine == ::Rails.application
@@ -10,10 +10,14 @@ module Airbrake
10
10
  # @see https://github.com/rails/rails/issues/8987
11
11
  HTML_RESPONSE_WILDCARD = "*/*"
12
12
 
13
+ # @return [Integer]
14
+ MILLISECOND = 1000
15
+
13
16
  include Airbrake::Loggable
14
17
 
15
18
  def initialize(*args)
16
19
  @event = ActiveSupport::Notifications::Event.new(*args)
20
+ @rails_7_or_greater = ::Rails::VERSION::MAJOR >= 7
17
21
  end
18
22
 
19
23
  def method
@@ -42,7 +46,15 @@ module Airbrake
42
46
  end
43
47
 
44
48
  def time
45
- @event.time
49
+ # On Rails 7+ `ActiveSupport::Notifications::Event#time` returns an
50
+ # instance of Float. It represents monotonic time in milliseconds.
51
+ # Airbrake Ruby expects that the provided time is in seconds. Hence,
52
+ # we need to convert it from milliseconds to seconds. In the
53
+ # versions below Rails 7, time is an instance of Time.
54
+ #
55
+ # Relevant commit:
56
+ # https://github.com/rails/rails/commit/81d0dc90becfe0b8e7f7f26beb66c25d84b8ec7f
57
+ @rails_7_or_greater ? @event.time / MILLISECOND : @event.time
46
58
  end
47
59
 
48
60
  def groups
@@ -5,37 +5,10 @@ module Airbrake
5
5
  # This railtie works for any Rails application that supports railties (Rails
6
6
  # 3.2+ apps). It makes Airbrake Ruby work with Rails and report errors
7
7
  # occurring in the application automatically.
8
- #
9
- # rubocop:disable Metrics/BlockLength
10
8
  class Railtie < ::Rails::Railtie
11
9
  initializer('airbrake.middleware') do |app|
12
- # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
13
- # responsible for logging exceptions and showing a debugging page in
14
- # case the request is local. We want to insert our middleware after
15
- # DebugExceptions, so we don't notify Airbrake about local requests.
16
-
17
- if ::Rails.version.to_i >= 5
18
- # Avoid the warning about deprecated strings.
19
- # Insert after DebugExceptions, since ConnectionManagement doesn't
20
- # exist in Rails 5 anymore.
21
- app.config.middleware.insert_after(
22
- ActionDispatch::DebugExceptions,
23
- Airbrake::Rack::Middleware,
24
- )
25
- elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
26
- # Insert after ConnectionManagement to avoid DB connection leakage:
27
- # https://github.com/airbrake/airbrake/pull/568
28
- app.config.middleware.insert_after(
29
- ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
30
- 'Airbrake::Rack::Middleware',
31
- )
32
- else
33
- # Insert after DebugExceptions for apps without ActiveRecord.
34
- app.config.middleware.insert_after(
35
- ActionDispatch::DebugExceptions,
36
- 'Airbrake::Rack::Middleware',
37
- )
38
- end
10
+ require 'airbrake/rails/railties/middleware_tie'
11
+ Railties::MiddlewareTie.new(app).call
39
12
  end
40
13
 
41
14
  rake_tasks do
@@ -47,82 +20,13 @@ module Airbrake
47
20
  end
48
21
 
49
22
  initializer('airbrake.action_controller') do
50
- ActiveSupport.on_load(:action_controller, run_once: true) do
51
- # Patches ActionController with methods that allow us to retrieve
52
- # interesting request data. Appends that information to notices.
53
- require 'airbrake/rails/action_controller'
54
- include Airbrake::Rails::ActionController
55
-
56
- # Cache route information for the duration of the request.
57
- require 'airbrake/rails/action_controller_route_subscriber'
58
- ActiveSupport::Notifications.subscribe(
59
- 'start_processing.action_controller',
60
- Airbrake::Rails::ActionControllerRouteSubscriber.new,
61
- )
62
-
63
- # Send route stats.
64
- require 'airbrake/rails/action_controller_notify_subscriber'
65
- ActiveSupport::Notifications.subscribe(
66
- 'process_action.action_controller',
67
- Airbrake::Rails::ActionControllerNotifySubscriber.new,
68
- )
69
-
70
- # Send performance breakdown: where a request spends its time.
71
- require 'airbrake/rails/action_controller_performance_breakdown_subscriber'
72
- ActiveSupport::Notifications.subscribe(
73
- 'process_action.action_controller',
74
- Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new,
75
- )
76
-
77
- require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
78
- require 'airbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
79
- require 'airbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
80
- require 'airbrake/rails/http_client' if defined?(HTTPClient)
81
- require 'airbrake/rails/typhoeus' if defined?(Typhoeus)
82
-
83
- if defined?(Excon)
84
- require 'airbrake/rails/excon_subscriber'
85
- ActiveSupport::Notifications.subscribe(/excon/, Airbrake::Rails::Excon.new)
86
- ::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
87
- end
88
- end
23
+ require 'airbrake/rails/railties/action_controller_tie'
24
+ Railties::ActionControllerTie.new.call
89
25
  end
90
26
 
91
27
  initializer('airbrake.active_record') do
92
- ActiveSupport.on_load(:active_record, run_once: true) do
93
- # Reports exceptions occurring in some bugged ActiveRecord callbacks.
94
- # Applicable only to the versions of Rails lower than 4.2.
95
- if defined?(::Rails) &&
96
- Gem::Version.new(::Rails.version) <= Gem::Version.new('4.2')
97
- require 'airbrake/rails/active_record'
98
- include Airbrake::Rails::ActiveRecord
99
- end
100
-
101
- if defined?(ActiveRecord)
102
- # Send SQL queries.
103
- require 'airbrake/rails/active_record_subscriber'
104
- ActiveSupport::Notifications.subscribe(
105
- 'sql.active_record', Airbrake::Rails::ActiveRecordSubscriber.new
106
- )
107
-
108
- # Filter out parameters from SQL body.
109
- if ::ActiveRecord::Base.respond_to?(:connection_db_config)
110
- # Rails 6.1+ deprecates "connection_config" in favor of
111
- # "connection_db_config", so we need an updated call.
112
- Airbrake.add_performance_filter(
113
- Airbrake::Filters::SqlFilter.new(
114
- ::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter],
115
- ),
116
- )
117
- else
118
- Airbrake.add_performance_filter(
119
- Airbrake::Filters::SqlFilter.new(
120
- ::ActiveRecord::Base.connection_config[:adapter],
121
- ),
122
- )
123
- end
124
- end
125
- end
28
+ require 'airbrake/rails/railties/active_record_tie'
29
+ Railties::ActiveRecordTie.new.call
126
30
  end
127
31
 
128
32
  initializer('airbrake.active_job') do
@@ -146,6 +50,5 @@ module Airbrake
146
50
  end
147
51
  end
148
52
  end
149
- # rubocop:enable Metrics/BlockLength
150
53
  end
151
54
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'airbrake/rails/action_controller'
4
+ require 'airbrake/rails/action_controller_route_subscriber'
5
+ require 'airbrake/rails/action_controller_notify_subscriber'
6
+ require 'airbrake/rails/action_controller_performance_breakdown_subscriber'
7
+
8
+ module Airbrake
9
+ module Rails
10
+ module Railties
11
+ # Ties Airbrake APM (routes) and HTTP clients with Rails.
12
+ #
13
+ # @api private
14
+ # @since v13.0.1
15
+ class ActionControllerTie
16
+ def initialize
17
+ @route_subscriber = Airbrake::Rails::ActionControllerRouteSubscriber.new
18
+ @notify_subscriber = Airbrake::Rails::ActionControllerNotifySubscriber.new
19
+ @performance_breakdown_subscriber =
20
+ Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
21
+ end
22
+
23
+ def call
24
+ ActiveSupport.on_load(:action_controller, run_once: true, yield: self) do
25
+ # Patches ActionController with methods that allow us to retrieve
26
+ # interesting request data. Appends that information to notices.
27
+ ::ActionController::Base.include(Airbrake::Rails::ActionController)
28
+
29
+ tie_routes_apm
30
+ tie_http_integrations
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def tie_routes_apm
37
+ [
38
+ # Cache route information for the duration of the request.
39
+ ['start_processing.action_controller', @route_subscriber],
40
+
41
+ # Send route stats.
42
+ ['process_action.action_controller', @notify_subscriber],
43
+
44
+ # Send performance breakdown: where a request spends its time.
45
+ ['process_action.action_controller', @performance_breakdown_subscriber],
46
+ ].each do |(event, callback)|
47
+ ActiveSupport::Notifications.subscribe(event, callback)
48
+ end
49
+ end
50
+
51
+ def tie_http_integrations
52
+ tie_net_http
53
+ tie_curl
54
+ tie_http
55
+ tie_http_client
56
+ tie_typhoeus
57
+ tie_excon
58
+ end
59
+
60
+ def tie_net_http
61
+ require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
62
+ end
63
+
64
+ def tie_curl
65
+ require 'airbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
66
+ end
67
+
68
+ def tie_http
69
+ require 'airbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
70
+ end
71
+
72
+ def tie_http_client
73
+ require 'airbrake/rails/http_client' if defined?(HTTPClient)
74
+ end
75
+
76
+ def tie_typhoeus
77
+ require 'airbrake/rails/typhoeus' if defined?(Typhoeus)
78
+ end
79
+
80
+ def tie_excon
81
+ return unless defined?(Excon)
82
+
83
+ require 'airbrake/rails/excon_subscriber'
84
+ ActiveSupport::Notifications.subscribe(/excon/, Airbrake::Rails::Excon.new)
85
+ ::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'airbrake/rails/active_record'
4
+ require 'airbrake/rails/active_record_subscriber'
5
+
6
+ module Airbrake
7
+ module Rails
8
+ module Railties
9
+ # Ties Airbrake APM (queries) with Rails.
10
+ #
11
+ # @api private
12
+ # @since v13.0.1
13
+ class ActiveRecordTie
14
+ def initialize
15
+ @active_record_subscriber = Airbrake::Rails::ActiveRecordSubscriber.new
16
+ end
17
+
18
+ def call
19
+ ActiveSupport.on_load(:active_record, run_once: true, yield: self) do
20
+ tie_activerecord_callback_fix
21
+ tie_activerecord_apm
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def tie_activerecord_callback_fix
28
+ # Reports exceptions occurring in some bugged ActiveRecord callbacks.
29
+ # Applicable only to the versions of Rails lower than 4.2.
30
+ return unless defined?(::Rails)
31
+ return if Gem::Version.new(::Rails.version) > Gem::Version.new('4.2')
32
+
33
+ ActiveRecord::Base.include(Airbrake::Rails::ActiveRecord)
34
+ end
35
+
36
+ def tie_activerecord_apm
37
+ # Some Rails apps don't use ActiveRecord.
38
+ return unless defined?(::ActiveRecord)
39
+
40
+ # However, some dependencies might still require it, so we need an
41
+ # extra check. Apps that don't need ActiveRecord will likely have no
42
+ # AR configurations defined. We will skip APM integration in that
43
+ # case. See: https://github.com/airbrake/airbrake/issues/1222
44
+ configurations = ::ActiveRecord::Base.configurations
45
+ return unless configurations.any?
46
+
47
+ # Send SQL queries.
48
+ ActiveSupport::Notifications.subscribe(
49
+ 'sql.active_record',
50
+ @active_record_subscriber,
51
+ )
52
+
53
+ # Filter out parameters from SQL body.
54
+ sql_filter = Airbrake::Filters::SqlFilter.new(
55
+ detect_activerecord_adapter(configurations),
56
+ )
57
+ Airbrake.add_performance_filter(sql_filter)
58
+ end
59
+
60
+ # Rails 6+ introduces the `configs_for` API instead of the deprecated
61
+ # `#[]`, so we need an updated call.
62
+ def detect_activerecord_adapter(configurations)
63
+ unless configurations.respond_to?(:configs_for)
64
+ return configurations[::Rails.env]['adapter']
65
+ end
66
+
67
+ cfg = configurations.configs_for(env_name: ::Rails.env).first
68
+ # Rails 7+ API : Rails 6 API.
69
+ cfg.respond_to?(:adapter) ? cfg.adapter : cfg.config['adapter']
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airbrake
4
+ module Rails
5
+ module Railties
6
+ # Ties Airbrake Rails Middleware with Rails (error sending).
7
+ #
8
+ # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
9
+ # responsible for logging exceptions and showing a debugging page in case
10
+ # the request is local. We want to insert our middleware after
11
+ # DebugExceptions, so we don't notify Airbrake about local requests.
12
+ #
13
+ # @api private
14
+ # @since v13.0.1
15
+ class MiddlewareTie
16
+ def initialize(app)
17
+ @app = app
18
+ @middleware = app.config.middleware
19
+ end
20
+
21
+ def call
22
+ return tie_rails_5_or_above if ::Rails.version.to_i >= 5
23
+
24
+ if defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
25
+ return tie_rails_4_or_below_with_active_record
26
+ end
27
+
28
+ tie_rails_4_or_below_with_active_record
29
+ end
30
+
31
+ private
32
+
33
+ # Avoid the warning about deprecated strings.
34
+ # Insert after DebugExceptions, since ConnectionManagement doesn't
35
+ # exist in Rails 5 anymore.
36
+ def tie_rails_5_or_above
37
+ @middleware.insert_after(
38
+ ActionDispatch::DebugExceptions,
39
+ Airbrake::Rack::Middleware,
40
+ )
41
+ end
42
+
43
+ # Insert after ConnectionManagement to avoid DB connection leakage:
44
+ # https://github.com/airbrake/airbrake/pull/568
45
+ def tie_rails_4_or_below_with_active_record
46
+ @middleware.insert_after(
47
+ ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
48
+ 'Airbrake::Rack::Middleware',
49
+ )
50
+ end
51
+
52
+ # Insert after DebugExceptions for apps without ActiveRecord.
53
+ def tie_rails_4_or_below_without_active_record
54
+ @middleware.insert_after(
55
+ ActionDispatch::DebugExceptions,
56
+ 'Airbrake::Rack::Middleware',
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -53,11 +53,11 @@ namespace :airbrake do
53
53
  raise Airbrake::Error, 'airbrake-ruby is not configured' unless Airbrake.configured?
54
54
 
55
55
  deploy_params = {
56
- environment: ENV['ENVIRONMENT'],
57
- username: ENV['USERNAME'],
58
- revision: ENV['REVISION'],
59
- repository: ENV['REPOSITORY'],
60
- version: ENV['VERSION'],
56
+ environment: ENV.fetch('ENVIRONMENT', nil),
57
+ username: ENV.fetch('USERNAME', nil),
58
+ revision: ENV.fetch('REVISION', nil),
59
+ repository: ENV.fetch('REPOSITORY', nil),
60
+ version: ENV.fetch('VERSION', nil),
61
61
  }
62
62
  promise = Airbrake.notify_deploy(deploy_params)
63
63
  promise.then do
@@ -68,7 +68,7 @@ namespace :airbrake do
68
68
 
69
69
  desc 'Install a Heroku deploy hook to notify Airbrake of deploys'
70
70
  task :install_heroku_deploy_hook do
71
- app = ENV['HEROKU_APP']
71
+ app = ENV.fetch('HEROKU_APP', nil)
72
72
 
73
73
  config = Bundler.with_clean_env do
74
74
  `heroku config --shell#{" --app #{app}" if app}`
@@ -89,7 +89,7 @@ namespace :airbrake do
89
89
  " environment will be used."
90
90
  end
91
91
 
92
- unless (repo = ENV['REPOSITORY_URL'])
92
+ unless (repo = ENV.fetch('REPOSITORY_URL', nil))
93
93
  repo = `git remote get-url origin 2>/dev/null`.chomp
94
94
  if repo.empty?
95
95
  puts "Airbrake couldn't identify your app's repository."
@@ -3,5 +3,5 @@
3
3
  # We use Semantic Versioning v2.0.0
4
4
  # More information: http://semver.org/
5
5
  module Airbrake
6
- AIRBRAKE_VERSION = '12.0.0'
6
+ AIRBRAKE_VERSION = '13.0.2'
7
7
  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: 12.0.0
4
+ version: 13.0.2
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: 2021-09-22 00:00:00.000000000 Z
11
+ date: 2022-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: airbrake-ruby
@@ -154,16 +154,16 @@ dependencies:
154
154
  name: redis
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - '='
157
+ - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 4.1.4
159
+ version: '4.5'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - '='
164
+ - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 4.1.4
166
+ version: '4.5'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: sidekiq
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '0.9'
187
+ version: '1.0'
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '0.9'
194
+ version: '1.0'
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: excon
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -335,6 +335,9 @@ files:
335
335
  - lib/airbrake/rails/http_client.rb
336
336
  - lib/airbrake/rails/net_http.rb
337
337
  - lib/airbrake/rails/railtie.rb
338
+ - lib/airbrake/rails/railties/action_controller_tie.rb
339
+ - lib/airbrake/rails/railties/active_record_tie.rb
340
+ - lib/airbrake/rails/railties/middleware_tie.rb
338
341
  - lib/airbrake/rails/typhoeus.rb
339
342
  - lib/airbrake/rake.rb
340
343
  - lib/airbrake/rake/tasks.rb
@@ -349,7 +352,8 @@ files:
349
352
  homepage: https://airbrake.io
350
353
  licenses:
351
354
  - MIT
352
- metadata: {}
355
+ metadata:
356
+ rubygems_mfa_required: 'true'
353
357
  post_install_message:
354
358
  rdoc_options: []
355
359
  require_paths:
@@ -358,14 +362,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
358
362
  requirements:
359
363
  - - ">="
360
364
  - !ruby/object:Gem::Version
361
- version: '2.5'
365
+ version: '2.6'
362
366
  required_rubygems_version: !ruby/object:Gem::Requirement
363
367
  requirements:
364
368
  - - ">="
365
369
  - !ruby/object:Gem::Version
366
370
  version: '0'
367
371
  requirements: []
368
- rubygems_version: 3.1.4
372
+ rubygems_version: 3.3.3
369
373
  signing_key:
370
374
  specification_version: 4
371
375
  summary: Airbrake is an online tool that provides robust exception tracking in any