hydraulic_brake 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 83e086fbfbdec0c32a3e454b8c53c5589c7b8439
4
+ data.tar.gz: 339f4004d5a2e02447cbf211733bba531c89a4dd
5
+ SHA512:
6
+ metadata.gz: 76803cffcf750e04c17cc953b01a2f57f374c64cdcdc1caf4626ea535893aa78924f2d731df9dac8df576cb56926b24bacf562a400c21b67106cbd0080f454a3
7
+ data.tar.gz: e7a13f6265a431a26fc4e833a6fac075375d680e9b002b7b1e4af096a4c7982240cc49a55fb5239cbf3841623730ed56a64eae55dedf7ce63262fca1097bab9f
@@ -0,0 +1,20 @@
1
+ log/*
2
+ tmp
3
+ db/schema.rb
4
+ db/*.sqlite3
5
+ public/system
6
+ *.swp
7
+ *.DS_Store
8
+ coverage/*
9
+ rdoc/
10
+ tags
11
+ .yardoc
12
+ doc
13
+ pkg
14
+
15
+ Gemfile.lock
16
+ .bundle
17
+
18
+ *.rbc
19
+ *.plan
20
+ .ruby-version
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.1.1
5
+ - jruby-head
6
+ - rbx
7
+ script: bundle exec rake test
data/Gemfile CHANGED
@@ -1,16 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "builder"
4
-
5
- group "development" do
6
- gem "bourne", ">= 1.0"
7
- gem "cucumber", "~> 0.10.6"
8
- gem "fakeweb", "~> 1.3.0"
9
- gem "jeweler"
10
- gem "nokogiri", "~> 1.4.3.1"
11
- gem "rake"
12
- gem "rspec", "~> 2.6.0"
13
- gem "shoulda", "~> 2.11.3"
14
- gem "simplecov"
15
- gem "yard"
16
- end
3
+ gemspec
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- HydraulicBrake
2
- ========
1
+ # HydraulicBrake [![Build Status](https://travis-ci.org/stevecrozz/hydraulic_brake.svg?branch=master)](https://travis-ci.org/stevecrozz/hydraulic_brake)
3
2
 
4
3
  This is a replacement notifier gem for the [Airbrake
5
4
  gem](https://github.com/airbrake/airbrake) which is used
@@ -76,9 +75,20 @@ HydraulicBrake#notify.
76
75
  Async Notifications
77
76
  -------------------
78
77
 
79
- HydraulicBrake doesn't provide anything special for async notifications.
80
- Just wrap your calls to Airbrake#notify in the async library of your
81
- choice if you want to.
78
+ HydraulicBrake can send notifications for you in the background using a Ruby
79
+ thread. If configured for async notifications, calls to HydraulicBrake::Notify
80
+ will populate an in-memory queue up to a configurable limit. If you reach the
81
+ limit, HydraulicBrake will send an error to HydraulicBrake.logger for every
82
+ additional notice beyond the queue's capacity and this notice will contain
83
+ information about the notice that could not be sent. Shown below are the
84
+ default options, but you may adjust them to suit your needs.
85
+
86
+ ```ruby
87
+ HydraulicBrake.configure do |config|
88
+ config.async = false
89
+ config.async_queue_capacity = 100
90
+ end
91
+ ```
82
92
 
83
93
  Proxy Support
84
94
  -------------
@@ -94,7 +104,7 @@ HydraulicBrake.configure do |config|
94
104
  config.proxy_pass = bar # optional
95
105
  end
96
106
  ```
97
-
107
+
98
108
  Logging
99
109
  ------------
100
110
 
@@ -107,22 +117,6 @@ HydraulicBrake.configure do |config|
107
117
  end
108
118
  ```
109
119
 
110
- Deploy Hook
111
- -----------
112
-
113
- HydraulicBrake can notify Airbrake whenever you deploy your app. Just
114
- call HydraulicBrake::Hook#deploy whenever you deploy:
115
-
116
- ```ruby
117
- HydraulicBrake::Hook.deploy({
118
- :scm_revision => "cd6b969f66ad0794c7117d5030f926b49f82b038",
119
- :scm_repository => "stevecrozz/hydraulic_brake",
120
- :local_username => "stevecrozz",
121
- :rails_env => "production", # everything is rails, right?
122
- :message => "Another deployment hook brought to you by HydraulicBrake"
123
- })
124
- ```
125
-
126
120
  Credits
127
121
  -------
128
122
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -5,55 +5,50 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hydraulic_brake"
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Stephen Crosby"]
12
- s.date = "2013-07-19"
12
+ s.date = "2015-09-03"
13
13
  s.description = "Sends notifications to an Airbrake server"
14
14
  s.email = "stevecrozz@gmail.com"
15
15
  s.extra_rdoc_files = [
16
16
  "README.md"
17
17
  ]
18
- s.files = [
19
- ".rbenv-version",
20
- ".rvmrc",
21
- ".yardopts",
22
- "Gemfile",
23
- "INSTALL",
24
- "MIT-LICENSE",
25
- "README.md",
26
- "Rakefile",
27
- "VERSION",
28
- "features/rake.feature",
29
- "features/step_definitions/rake_steps.rb",
30
- "features/support/matchers.rb",
31
- "features/support/rake/Rakefile",
32
- "features/support/terminal.rb",
33
- "hydraulic_brake.gemspec",
34
- "lib/hydraulic_brake.rb",
35
- "lib/hydraulic_brake/backtrace.rb",
36
- "lib/hydraulic_brake/configuration.rb",
37
- "lib/hydraulic_brake/hook.rb",
38
- "lib/hydraulic_brake/notice.rb",
39
- "lib/hydraulic_brake/sender.rb",
40
- "lib/hydraulic_brake/test_notification.rb",
41
- "lib/hydraulic_brake/version.rb",
42
- "lib/hydraulic_brake_tasks.rb",
43
- "resources/README.md",
44
- "resources/ca-bundle.crt",
45
- "script/deploy_integration_test.rb",
46
- "script/notify_integration_test.rb",
47
- "test/airbrake_2_3.xsd",
48
- "test/backtrace_test.rb",
49
- "test/configuration_test.rb",
50
- "test/helper.rb",
51
- "test/logger_test.rb",
52
- "test/notice_test.rb",
53
- "test/notifier_test.rb",
54
- "test/recursion_test.rb",
55
- "test/sender_test.rb"
56
- ]
18
+ s.files = %w(
19
+ .gitignore
20
+ .rvmrc
21
+ .travis.yml
22
+ .yardopts
23
+ Gemfile
24
+ INSTALL
25
+ MIT-LICENSE
26
+ README.md
27
+ Rakefile
28
+ VERSION
29
+ hydraulic_brake.gemspec
30
+ lib/hydraulic_brake.rb
31
+ lib/hydraulic_brake/async_sender.rb
32
+ lib/hydraulic_brake/backtrace.rb
33
+ lib/hydraulic_brake/configuration.rb
34
+ lib/hydraulic_brake/notice.rb
35
+ lib/hydraulic_brake/sender.rb
36
+ lib/hydraulic_brake/test_notification.rb
37
+ lib/hydraulic_brake/version.rb
38
+ resources/README.md
39
+ resources/ca-bundle.crt
40
+ script/notify_integration_test.rb
41
+ test/airbrake_2_3.xsd
42
+ test/async_sender_test.rb
43
+ test/backtrace_test.rb
44
+ test/configuration_test.rb
45
+ test/helper.rb
46
+ test/logger_test.rb
47
+ test/notice_test.rb
48
+ test/notifier_test.rb
49
+ test/recursion_test.rb
50
+ test/sender_test.rb
51
+ )
57
52
  s.homepage = "http://github.com/stevecrozz/hydraulic_brake"
58
53
  s.licenses = ["MIT"]
59
54
  s.require_paths = ["lib"]
@@ -65,38 +60,32 @@ Gem::Specification.new do |s|
65
60
 
66
61
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
62
  s.add_runtime_dependency(%q<builder>, [">= 0"])
68
- s.add_development_dependency(%q<bourne>, [">= 1.0"])
69
- s.add_development_dependency(%q<cucumber>, ["~> 0.10.6"])
63
+ s.add_development_dependency(%q<bourne>, ["~> 1.5.0"])
70
64
  s.add_development_dependency(%q<fakeweb>, ["~> 1.3.0"])
71
65
  s.add_development_dependency(%q<jeweler>, [">= 0"])
72
66
  s.add_development_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
73
67
  s.add_development_dependency(%q<rake>, [">= 0"])
74
- s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
75
68
  s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
76
69
  s.add_development_dependency(%q<simplecov>, [">= 0"])
77
70
  s.add_development_dependency(%q<yard>, [">= 0"])
78
71
  else
79
72
  s.add_dependency(%q<builder>, [">= 0"])
80
- s.add_dependency(%q<bourne>, [">= 1.0"])
81
- s.add_dependency(%q<cucumber>, ["~> 0.10.6"])
73
+ s.add_dependency(%q<bourne>, ["~> 1.5.0"])
82
74
  s.add_dependency(%q<fakeweb>, ["~> 1.3.0"])
83
75
  s.add_dependency(%q<jeweler>, [">= 0"])
84
76
  s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
85
77
  s.add_dependency(%q<rake>, [">= 0"])
86
- s.add_dependency(%q<rspec>, ["~> 2.6.0"])
87
78
  s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
88
79
  s.add_dependency(%q<simplecov>, [">= 0"])
89
80
  s.add_dependency(%q<yard>, [">= 0"])
90
81
  end
91
82
  else
92
83
  s.add_dependency(%q<builder>, [">= 0"])
93
- s.add_dependency(%q<bourne>, [">= 1.0"])
94
- s.add_dependency(%q<cucumber>, ["~> 0.10.6"])
84
+ s.add_dependency(%q<bourne>, ["~> 1.5.0"])
95
85
  s.add_dependency(%q<fakeweb>, ["~> 1.3.0"])
96
86
  s.add_dependency(%q<jeweler>, [">= 0"])
97
87
  s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
98
88
  s.add_dependency(%q<rake>, [">= 0"])
99
- s.add_dependency(%q<rspec>, ["~> 2.6.0"])
100
89
  s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
101
90
  s.add_dependency(%q<simplecov>, [">= 0"])
102
91
  s.add_dependency(%q<yard>, [">= 0"])
@@ -1,9 +1,10 @@
1
+ require 'logger'
1
2
  require 'net/http'
2
3
  require 'net/https'
3
4
  require 'rubygems'
5
+ require 'hydraulic_brake/async_sender'
4
6
  require 'hydraulic_brake/backtrace'
5
7
  require 'hydraulic_brake/configuration'
6
- require 'hydraulic_brake/hook'
7
8
  require 'hydraulic_brake/notice'
8
9
  require 'hydraulic_brake/sender'
9
10
  require 'hydraulic_brake/test_notification'
@@ -75,7 +76,15 @@ module HydraulicBrake
75
76
  # end
76
77
  def configure(silent = false)
77
78
  yield(configuration)
78
- self.sender = Sender.new(configuration)
79
+
80
+ if configuration.async
81
+ self.sender = AsyncSender.new(
82
+ :sync_sender => Sender.new(configuration),
83
+ :capacity => configuration.async_queue_capacity)
84
+ else
85
+ self.sender = Sender.new(configuration)
86
+ end
87
+
79
88
  report_ready unless silent
80
89
  self.sender
81
90
  end
@@ -102,7 +111,7 @@ module HydraulicBrake
102
111
  # @option opts [String] :session_data The contents of the user's session
103
112
  # @option opts [String] :environment_name The application environment name
104
113
  def notify(exception, opts = {})
105
- send_notice(build_notice_for(exception, opts))
114
+ send_notice build_notice_for(exception, opts)
106
115
  end
107
116
 
108
117
  def build_lookup_hash_for(exception, options = {})
@@ -0,0 +1,39 @@
1
+ module HydraulicBrake
2
+ # In-memory, thread-safe storage for notices that haven't yet been sent to
3
+ # Airbrake
4
+ class AsyncSender
5
+ attr_reader :sync_sender
6
+ attr_reader :thread
7
+
8
+ def initialize(opts={})
9
+ @sync_sender = opts[:sync_sender]
10
+ @q = Queue.new
11
+ @capacity = opts[:capacity] || 100
12
+ @logger = opts
13
+ @thread = nil
14
+ end
15
+
16
+ def send_to_airbrake(notice)
17
+ return will_not_deliver(notice) if @q.length >= @capacity
18
+
19
+ @q.push(notice)
20
+
21
+ return if @thread && @thread.alive?
22
+
23
+ @thread = Thread.new do
24
+ while n = @q.pop
25
+ @sync_sender.send_to_airbrake(n)
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+ def will_not_deliver(notice)
32
+ HydraulicBrake.logger.error(
33
+ "[HydraulicBrake::AsyncSender has reached its capacity of " \
34
+ "#{@capacity} and the following notice will not be delivered " \
35
+ "Error: #{notice.error_class} - #{notice.error_message}\n" \
36
+ "Backtrace: \n#{notice.backtrace}")
37
+ end
38
+ end
39
+ end
@@ -4,6 +4,8 @@ module HydraulicBrake
4
4
 
5
5
  OPTIONS = [
6
6
  :api_key,
7
+ :async,
8
+ :async_queue_capacity,
7
9
  :backtrace_filters,
8
10
  :development_environments,
9
11
  :development_lookup,
@@ -65,6 +67,14 @@ module HydraulicBrake
65
67
  # The password to use when logging into your proxy server (if using a proxy)
66
68
  attr_accessor :proxy_pass
67
69
 
70
+ # Send the notice to airbrake using a background thread
71
+ attr_accessor :async
72
+
73
+ # Maxmimum number of notices to store in memory. If the number of notices
74
+ # waiting to be sent to airbrake reaches this value, further notices will
75
+ # be dropped until the queue size drops.
76
+ attr_accessor :async_queue_capacity
77
+
68
78
  # A list of parameters that should be filtered out of what is sent to Airbrake.
69
79
  # By default, all "password" attributes will have their contents replaced.
70
80
  attr_reader :params_filters
@@ -139,6 +149,8 @@ module HydraulicBrake
139
149
  alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
140
150
 
141
151
  def initialize
152
+ @async = false
153
+ @async_queue_capacity = 100
142
154
  @secure = false
143
155
  @use_system_ssl_cert_chain= false
144
156
  @host = 'api.airbrake.io'
@@ -146,7 +158,7 @@ module HydraulicBrake
146
158
  @http_read_timeout = 5
147
159
  @params_filters = DEFAULT_PARAMS_FILTERS.dup
148
160
  @backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
149
- @development_environments = %w(development test cucumber)
161
+ @development_environments = %w(development test)
150
162
  @development_lookup = true
151
163
  @notifier_name = 'HydraulicBrake Notifier'
152
164
  @notifier_version = VERSION
@@ -155,6 +167,7 @@ module HydraulicBrake
155
167
  @rescue_rake_exceptions = nil
156
168
  @user_attributes = DEFAULT_USER_ATTRIBUTES.dup
157
169
  @rake_environment_filters = []
170
+ @logger = Logger.new(STDOUT)
158
171
  end
159
172
 
160
173
  # Takes a block and adds it to the list of backtrace filters. When the filters
@@ -13,7 +13,8 @@ module HydraulicBrake
13
13
  Errno::ECONNREFUSED].freeze
14
14
 
15
15
  def initialize(options = {})
16
- [ :proxy_host,
16
+ [
17
+ :proxy_host,
17
18
  :proxy_port,
18
19
  :proxy_user,
19
20
  :proxy_pass,
@@ -31,9 +32,9 @@ module HydraulicBrake
31
32
 
32
33
  # Sends the notice data off to Airbrake for processing.
33
34
  #
34
- # @param [Notice or String] notice The notice to be sent off
35
+ # @param [Notice] notice The notice to be sent off
35
36
  def send_to_airbrake(notice)
36
- data = notice.respond_to?(:to_xml) ? notice.to_xml : notice
37
+ data = notice.to_xml
37
38
  http = setup_http_connection
38
39
 
39
40
  response = begin
@@ -47,19 +48,14 @@ module HydraulicBrake
47
48
  case response
48
49
  when Net::HTTPSuccess then
49
50
  log :level => :info,
50
- :message => "Success: #{response.class}",
51
- :response => response
51
+ :message => success_message_from_response(response)
52
+ error_id_from_response(response)
52
53
  else
53
54
  log :level => :error,
54
55
  :message => "Failure: #{response.class}",
55
56
  :response => response,
56
57
  :notice => notice
57
58
  end
58
-
59
- if response && response.respond_to?(:body)
60
- error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
61
- error_id[1] if error_id
62
- end
63
59
  rescue => e
64
60
  log :level => :error,
65
61
  :message => "[HydraulicBrake::Sender#send_to_airbrake] Cannot send notification. Error: #{e.class}" +
@@ -85,12 +81,44 @@ module HydraulicBrake
85
81
 
86
82
  private
87
83
 
84
+ def success_message_from_response(response)
85
+ error_url = error_url_from_response(response)
86
+
87
+ if error_url
88
+ "Success: sent error to Airbrake: #{error_url}"
89
+ else
90
+ "Success: sent error to Airbrake"
91
+ end
92
+ end
93
+
94
+ def error_id_from_response(response)
95
+ if response && response.respond_to?(:body)
96
+ error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
97
+ return error_id[1] if error_id
98
+ end
99
+
100
+ nil
101
+ rescue
102
+ nil
103
+ end
104
+
105
+ def error_url_from_response(response)
106
+ if response && response.respond_to?(:body)
107
+ error_url = response.body.match(%r{<url[^>]*>(.*?)</url>})
108
+ return error_url[1] if error_url
109
+ end
110
+
111
+ nil
112
+ rescue
113
+ nil
114
+ end
115
+
88
116
  def url
89
117
  URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
90
118
  end
91
119
 
92
120
  def log(opts = {})
93
- opts[:logger].send opts[:level], LOG_PREFIX + opts[:message] if opts[:logger]
121
+ logger.send opts[:level], LOG_PREFIX + opts[:message]
94
122
  HydraulicBrake.report_environment_info
95
123
  HydraulicBrake.report_response_body(opts[:response].body) if opts[:response] && opts[:response].respond_to?(:body)
96
124
  HydraulicBrake.report_notice(opts[:notice]) if opts[:notice]