hutch 0.25.0-java → 2.0.0-java

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +402 -5
  3. data/LICENSE +1 -0
  4. data/README.md +157 -47
  5. data/bin/ci/before_build.sh +20 -0
  6. data/bin/ci/before_build_docker.sh +15 -0
  7. data/bin/ci/install_on_debian.sh +46 -0
  8. data/lib/hutch/acknowledgements/nack_on_all_failures.rb +1 -1
  9. data/lib/hutch/adapters/bunny.rb +29 -1
  10. data/lib/hutch/adapters/march_hare.rb +24 -0
  11. data/lib/hutch/broker.rb +98 -35
  12. data/lib/hutch/cli.rb +16 -12
  13. data/lib/hutch/config.rb +42 -13
  14. data/lib/hutch/consumer.rb +63 -4
  15. data/lib/hutch/error_handlers/bugsnag.rb +30 -0
  16. data/lib/hutch/error_handlers/{opbeat.rb → rollbar.rb} +11 -13
  17. data/lib/hutch/error_handlers/sentry.rb +6 -11
  18. data/lib/hutch/error_handlers.rb +2 -1
  19. data/lib/hutch/publisher.rb +1 -1
  20. data/lib/hutch/serializers/json.rb +3 -3
  21. data/lib/hutch/tracers/datadog.rb +18 -0
  22. data/lib/hutch/tracers.rb +1 -1
  23. data/lib/hutch/version.rb +1 -2
  24. data/lib/hutch/worker.rb +46 -9
  25. data/lib/hutch.rb +8 -4
  26. data/lib/yard-settings/yard-settings.rb +2 -2
  27. data/spec/hutch/broker_spec.rb +192 -14
  28. data/spec/hutch/cli_spec.rb +12 -9
  29. data/spec/hutch/config_spec.rb +84 -12
  30. data/spec/hutch/consumer_spec.rb +93 -4
  31. data/spec/hutch/error_handlers/airbrake_spec.rb +4 -1
  32. data/spec/hutch/error_handlers/bugsnag_spec.rb +56 -0
  33. data/spec/hutch/error_handlers/honeybadger_spec.rb +1 -1
  34. data/spec/hutch/error_handlers/logger_spec.rb +1 -1
  35. data/spec/hutch/error_handlers/rollbar_spec.rb +45 -0
  36. data/spec/hutch/error_handlers/sentry_spec.rb +13 -3
  37. data/spec/hutch/message_spec.rb +1 -1
  38. data/spec/hutch/tracers/datadog_spec.rb +46 -0
  39. data/spec/hutch/waiter_spec.rb +2 -2
  40. data/spec/hutch/worker_spec.rb +138 -24
  41. data/spec/integration/channel_recovery_spec.rb +187 -0
  42. data/spec/integration/publish_consume_spec.rb +47 -0
  43. metadata +32 -59
  44. data/.gitignore +0 -10
  45. data/.rspec +0 -1
  46. data/.travis.yml +0 -16
  47. data/.yardopts +0 -5
  48. data/Gemfile +0 -33
  49. data/Guardfile +0 -14
  50. data/Rakefile +0 -21
  51. data/examples/consumer.rb +0 -13
  52. data/examples/producer.rb +0 -10
  53. data/hutch.gemspec +0 -29
  54. data/lib/hutch/tracers/opbeat.rb +0 -37
  55. data/spec/hutch/error_handlers/opbeat_spec.rb +0 -37
  56. data/spec/spec_helper.rb +0 -42
  57. data/spec/tracers/opbeat_spec.rb +0 -44
data/lib/hutch/broker.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'uri'
1
2
  require 'active_support/core_ext/object/blank'
2
3
 
3
4
  require 'carrot-top'
@@ -113,6 +114,8 @@ module Hutch
113
114
  logger.info 'enabling publisher confirms'
114
115
  ch.confirm_select
115
116
  end
117
+
118
+ connection.install_channel_recovery(ch)
116
119
  end
117
120
  end
118
121
 
@@ -120,13 +123,23 @@ module Hutch
120
123
  @channel = open_channel
121
124
  end
122
125
 
126
+ # Closing the old channel makes RabbitMQ forget the consumers on it
127
+ # and requeue their unacknowledged deliveries.
128
+ def replace_channel!
129
+ close_consumer_channel
130
+ open_channel!
131
+ declare_exchange!
132
+ declare_publisher!
133
+ end
134
+
123
135
  def declare_exchange(ch = channel)
124
136
  exchange_name = @config[:mq_exchange]
137
+ exchange_type = @config[:mq_exchange_type]
125
138
  exchange_options = { durable: true }.merge(@config[:mq_exchange_options])
126
139
  logger.info "using topic exchange '#{exchange_name}'"
127
140
 
128
141
  with_bunny_precondition_handler('exchange') do
129
- ch.topic(exchange_name, exchange_options)
142
+ Adapter.new_exchange(ch, exchange_type, exchange_name, exchange_options)
130
143
  end
131
144
  end
132
145
 
@@ -169,24 +182,35 @@ module Hutch
169
182
  @config[:tracer] && @config[:tracer] != Hutch::Tracers::NullTracer
170
183
  end
171
184
 
172
- # Create / get a durable queue and apply namespace if it exists.
173
- def queue(name, arguments = {})
185
+ # Create / get a durable queue.
186
+ def queue(name, options = {})
174
187
  with_bunny_precondition_handler('queue') do
175
- namespace = @config[:namespace].to_s.downcase.gsub(/[^-_:\.\w]/, "")
176
- name = name.prepend(namespace + ":") if namespace.present?
177
- channel.queue(name, durable: true, arguments: arguments)
188
+ channel.queue(name, **options)
178
189
  end
179
190
  end
180
191
 
192
+ def queue_exists?(name)
193
+ connection.queue_exists?(name)
194
+ end
195
+
196
+ # Apply the configured namespace prefix to a queue name.
197
+ def namespaced_queue_name(name)
198
+ namespace = @config[:namespace].to_s.downcase.gsub(/[^-_:\.\w]/, "")
199
+ namespace.present? ? "#{namespace}:#{name}" : name
200
+ end
201
+
181
202
  # Return a mapping of queue names to the routing keys they're bound to.
182
203
  def bindings
183
204
  results = Hash.new { |hash, key| hash[key] = [] }
184
- api_client.bindings.each do |binding|
185
- next if binding['destination'] == binding['routing_key']
186
- next unless binding['source'] == @config[:mq_exchange]
187
- next unless binding['vhost'] == @config[:mq_vhost]
205
+
206
+ filtered = api_client.bindings.
207
+ reject { |b| b['destination'] == b['routing_key'] }.
208
+ select { |b| b['source'] == @config[:mq_exchange] && b['vhost'] == vhost }
209
+
210
+ filtered.each do |binding|
188
211
  results[binding['destination']] << binding['routing_key']
189
212
  end
213
+
190
214
  results
191
215
  end
192
216
 
@@ -194,8 +218,8 @@ module Hutch
194
218
  def unbind_redundant_bindings(queue, routing_keys)
195
219
  return unless http_api_use_enabled?
196
220
 
197
- bindings.each do |dest, keys|
198
- next unless dest == queue.name
221
+ filtered = bindings.select { |dest, keys| dest == queue.name }
222
+ filtered.each do |dest, keys|
199
223
  keys.reject { |key| routing_keys.include?(key) }.each do |key|
200
224
  logger.debug "removing redundant binding #{queue.name} <--> #{key}"
201
225
  queue.unbind(exchange, routing_key: key)
@@ -220,29 +244,26 @@ module Hutch
220
244
  if defined?(JRUBY_VERSION)
221
245
  channel.close
222
246
  else
223
- # Enqueue a failing job that kills the consumer loop
224
- channel_work_pool.shutdown
225
- # Give `timeout` seconds to jobs that are still being processed
226
- channel_work_pool.join(@config[:graceful_exit_timeout])
227
- # If after `timeout` they are still running, they are killed
228
- channel_work_pool.kill
247
+ drain_consumer_work_pool
229
248
  end
230
249
  end
231
250
 
232
- def requeue(delivery_tag)
233
- channel.reject(delivery_tag, true)
251
+ # Delivery tags are scoped to their channel: a tag from a replaced one
252
+ # is dropped, the server has requeued that delivery anyway.
253
+ def requeue(delivery_tag, channel: self.channel)
254
+ channel.reject(delivery_tag, true) if current_channel?(channel)
234
255
  end
235
256
 
236
- def reject(delivery_tag, requeue=false)
237
- channel.reject(delivery_tag, requeue)
257
+ def reject(delivery_tag, requeue = false, channel: self.channel)
258
+ channel.reject(delivery_tag, requeue) if current_channel?(channel)
238
259
  end
239
260
 
240
- def ack(delivery_tag)
241
- channel.ack(delivery_tag, false)
261
+ def ack(delivery_tag, channel: self.channel)
262
+ channel.ack(delivery_tag, false) if current_channel?(channel)
242
263
  end
243
264
 
244
- def nack(delivery_tag)
245
- channel.nack(delivery_tag, false, false)
265
+ def nack(delivery_tag, channel: self.channel)
266
+ channel.nack(delivery_tag, false, false) if current_channel?(channel)
246
267
  end
247
268
 
248
269
  def publish(*args)
@@ -264,15 +285,39 @@ module Hutch
264
285
 
265
286
  private
266
287
 
288
+ def current_channel?(ch)
289
+ ch.equal?(channel)
290
+ end
291
+
292
+ def close_consumer_channel
293
+ return unless @channel && @channel.open?
294
+
295
+ drain_consumer_work_pool unless defined?(JRUBY_VERSION)
296
+ @channel.close
297
+ rescue Hutch::Adapter::ChannelAlreadyClosed
298
+ # The server closed the channel first: same outcome.
299
+ end
300
+
301
+ # Bunny kills the consumer work pool when the channel closes. Draining
302
+ # first gives the running handlers `graceful_exit_timeout` to finish.
303
+ def drain_consumer_work_pool
304
+ channel_work_pool.shutdown
305
+ channel_work_pool.join(@config[:graceful_exit_timeout])
306
+ channel_work_pool.kill
307
+ end
308
+
309
+ Config = Struct.new(:host, :port, :username, :password, :ssl, :protocol, :sanitized_uri)
310
+ private_constant :Config
311
+
267
312
  def api_config
268
- @api_config ||= OpenStruct.new.tap do |config|
313
+ @api_config ||= Config.new.tap do |config|
269
314
  config.host = @config[:mq_api_host]
270
315
  config.port = @config[:mq_api_port]
271
316
  config.username = @config[:mq_username]
272
317
  config.password = @config[:mq_password]
273
318
  config.ssl = @config[:mq_api_ssl]
274
319
  config.protocol = config.ssl ? "https://" : "http://"
275
- config.sanitized_uri = "#{config.protocol}#{config.username}@#{config.host}:#{config.port}/"
320
+ config.sanitized_uri = "#{config.protocol}#{URI.encode_uri_component(config.username.to_s)}@#{config.host}:#{config.port}/"
276
321
  end
277
322
  end
278
323
 
@@ -282,7 +327,8 @@ module Hutch
282
327
  {}.tap do |params|
283
328
  params[:host] = @config[:mq_host]
284
329
  params[:port] = @config[:mq_port]
285
- params[:vhost] = @config[:mq_vhost].presence || Hutch::Adapter::DEFAULT_VHOST
330
+ params[:vhost] = vhost
331
+ params[:auth_mechanism] = @config[:mq_auth_mechanism]
286
332
  params[:username] = @config[:mq_username]
287
333
  params[:password] = @config[:mq_password]
288
334
  params[:tls] = @config[:mq_tls]
@@ -293,13 +339,15 @@ module Hutch
293
339
  params[:tls_ca_certificates] = @config[:mq_tls_ca_certificates]
294
340
  end
295
341
  params[:heartbeat] = @config[:heartbeat]
342
+ params[:client_properties] = @config[:mq_client_properties]
343
+ params[:connection_name] = @config[:connection_name]
296
344
  params[:connection_timeout] = @config[:connection_timeout]
297
345
  params[:read_timeout] = @config[:read_timeout]
298
346
  params[:write_timeout] = @config[:write_timeout]
299
347
 
300
348
 
301
- params[:automatically_recover] = true
302
- params[:network_recovery_interval] = 1
349
+ params[:automatically_recover] = @config[:automatically_recover]
350
+ params[:network_recovery_interval] = @config[:network_recovery_interval]
303
351
 
304
352
  params[:logger] = @config[:client_logger] if @config[:client_logger]
305
353
  end
@@ -313,9 +361,23 @@ module Hutch
313
361
  @config[:mq_tls] = u.scheme == 'amqps'
314
362
  @config[:mq_host] = u.host
315
363
  @config[:mq_port] = u.port || default_mq_port
316
- @config[:mq_vhost] = u.path.sub(/^\//, "")
317
- @config[:mq_username] = u.user
318
- @config[:mq_password] = u.password
364
+ @config[:mq_vhost] = parse_vhost(u)
365
+ @config[:mq_username] = u.user && URI.decode_uri_component(u.user)
366
+ @config[:mq_password] = u.password && URI.decode_uri_component(u.password)
367
+ end
368
+
369
+ # Resolved here rather than in #connection_params, so that
370
+ # Config[:mq_vhost] holds the name the HTTP API reports too.
371
+ def parse_vhost(uri)
372
+ URI.decode_uri_component(uri.path.sub(%r{\A/}, "")).presence ||
373
+ Hutch::Adapter::DEFAULT_VHOST
374
+ end
375
+
376
+ # The vhost Hutch connects to. Config[:mq_vhost] can still be blank when it
377
+ # was set directly rather than through a URI, while the broker and its HTTP
378
+ # API call the default vhost `/`.
379
+ def vhost
380
+ @config[:mq_vhost].presence || Hutch::Adapter::DEFAULT_VHOST
319
381
  end
320
382
 
321
383
  def default_mq_port
@@ -326,7 +388,8 @@ module Hutch
326
388
  p = connection_params
327
389
  scheme = p[:tls] ? "amqps" : "amqp"
328
390
 
329
- "#{scheme}://#{p[:username]}@#{p[:host]}:#{p[:port]}/#{p[:vhost].sub(/^\//, '')}"
391
+ "#{scheme}://#{URI.encode_uri_component(p[:username].to_s)}" \
392
+ "@#{p[:host]}:#{p[:port]}/#{URI.encode_uri_component(p[:vhost].sub(%r{\A/}, ''))}"
330
393
  end
331
394
 
332
395
  def with_authentication_error_handler
data/lib/hutch/cli.rb CHANGED
@@ -33,6 +33,20 @@ module Hutch
33
33
  def load_app
34
34
  # Try to load a Rails app in the current directory
35
35
  load_rails_app('.') if Hutch::Config.autoload_rails
36
+ set_up_code_paths!
37
+
38
+ # Because of the order things are required when we run the Hutch binary
39
+ # in hutch/bin, the sentry-ruby gem gets required **after** the error
40
+ # handlers are set up. Due to this, we never got any Sentry notifications
41
+ # when an error occurred in any of the consumers.
42
+ if defined?(Sentry)
43
+ Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::Sentry.new
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def set_up_code_paths!
36
50
  Hutch::Config.require_paths.each do |path|
37
51
  # See if each path is a Rails app. If so, try to load it.
38
52
  next if load_rails_app(path)
@@ -48,19 +62,9 @@ module Hutch
48
62
  return false
49
63
  ensure
50
64
  # Clean up load path
51
- $LOAD_PATH.pop
65
+ $LOAD_PATH.delete('.')
52
66
  end
53
67
  end
54
-
55
- # Because of the order things are required when we run the Hutch binary
56
- # in hutch/bin, the Sentry Raven gem gets required **after** the error
57
- # handlers are set up. Due to this, we never got any Sentry notifications
58
- # when an error occurred in any of the consumers.
59
- if defined?(Raven)
60
- Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::Sentry.new
61
- end
62
-
63
- true
64
68
  end
65
69
 
66
70
  def load_rails_app(path)
@@ -227,7 +231,7 @@ module Hutch
227
231
  end
228
232
 
229
233
  def abort_without_file(file, file_description, &block)
230
- abort_with_message("#{file_description} '#{file}' not found") unless File.exists?(file)
234
+ abort_with_message("#{file_description} '#{file}' not found") unless File.exist?(file)
231
235
 
232
236
  yield
233
237
  end
data/lib/hutch/config.rb CHANGED
@@ -49,17 +49,23 @@ module Hutch
49
49
  # RabbitMQ Exchange to use for publishing
50
50
  string_setting :mq_exchange, 'hutch'
51
51
 
52
+ # RabbitMQ Exchange type to use for publishing
53
+ string_setting :mq_exchange_type, 'topic'
54
+
52
55
  # RabbitMQ vhost to use
53
56
  string_setting :mq_vhost, '/'
54
57
 
55
58
  # RabbitMQ username to use.
56
59
  #
57
- # As of RabbitMQ 3.3.0, <tt>guest</tt> can only can connect from localhost.
60
+ # The <tt>guest</tt> user can only connect from localhost.
58
61
  string_setting :mq_username, 'guest'
59
62
 
60
63
  # RabbitMQ password
61
64
  string_setting :mq_password, 'guest'
62
65
 
66
+ # RabbitMQ Auth Mechanism
67
+ string_setting :mq_auth_mechanism, 'PLAIN'
68
+
63
69
  # RabbitMQ URI (takes precedence over MQ username, password, host, port and vhost settings)
64
70
  string_setting :uri, nil
65
71
 
@@ -72,7 +78,7 @@ module Hutch
72
78
  # RabbitMQ HTTP API port
73
79
  number_setting :mq_api_port, 15672
74
80
 
75
- # [RabbitMQ heartbeat timeout](http://rabbitmq.com/heartbeats.html)
81
+ # [RabbitMQ heartbeat timeout](https://www.rabbitmq.com/docs/heartbeats)
76
82
  number_setting :heartbeat, 30
77
83
 
78
84
  # The <tt>basic.qos</tt> prefetch value to use.
@@ -80,6 +86,9 @@ module Hutch
80
86
  # Default: `0`, no limit. See Bunny and RabbitMQ documentation.
81
87
  number_setting :channel_prefetch, 0
82
88
 
89
+ # [Client-Provided Connection Name](https://www.rabbitmq.com/docs/connections#client-provided-names)
90
+ string_setting :connection_name, nil
91
+
83
92
  # Bunny's socket open timeout
84
93
  number_setting :connection_timeout, 11
85
94
 
@@ -89,7 +98,13 @@ module Hutch
89
98
  # Bunny's socket write timeout
90
99
  number_setting :write_timeout, 11
91
100
 
92
- # FIXME: DOCUMENT THIS
101
+ # Bunny's enable/disable network recovery
102
+ boolean_setting :automatically_recover, true
103
+
104
+ # Bunny's reconnect interval
105
+ number_setting :network_recovery_interval, 1
106
+
107
+ # Timeout (in seconds) for consumer threads to finish before being killed during graceful shutdown
93
108
  number_setting :graceful_exit_timeout, 11
94
109
 
95
110
  # Bunny consumer work pool size
@@ -136,6 +151,9 @@ module Hutch
136
151
  # Prefix displayed on the consumers tags.
137
152
  string_setting :consumer_tag_prefix, 'hutch'
138
153
 
154
+ # A namespace to help group your queues
155
+ string_setting :namespace, nil
156
+
139
157
  string_setting :group, ''
140
158
 
141
159
  # Set of all setting keys
@@ -156,6 +174,7 @@ module Hutch
156
174
  # @return [Hash]
157
175
  def self.default_config
158
176
  @settings_defaults.merge({
177
+ mq_client_properties: {},
159
178
  mq_exchange_options: {},
160
179
  mq_tls_cert: nil,
161
180
  mq_tls_key: nil,
@@ -184,14 +203,7 @@ module Hutch
184
203
  env_keys_configured.each_with_object({}) {|attr, result|
185
204
  value = ENV[key_for(attr)]
186
205
 
187
- case
188
- when is_bool(attr) || value == 'false'
189
- result[attr] = to_bool(value)
190
- when is_num(attr)
191
- result[attr] = value.to_i
192
- else
193
- result[attr] = value
194
- end
206
+ result[attr] = type_cast(attr, value)
195
207
  }
196
208
  end
197
209
 
@@ -217,7 +229,12 @@ module Hutch
217
229
  end
218
230
 
219
231
  def self.to_bool(value)
220
- !(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i || value == false)
232
+ case value
233
+ when nil, false, '', /^(false|f|no|n|0)$/i
234
+ false
235
+ else
236
+ true
237
+ end
221
238
  end
222
239
 
223
240
  def self.is_num(attr)
@@ -226,7 +243,7 @@ module Hutch
226
243
 
227
244
  def self.set(attr, value)
228
245
  check_attr(attr.to_sym)
229
- user_config[attr.to_sym] = value
246
+ user_config[attr.to_sym] = type_cast(attr, value)
230
247
  end
231
248
 
232
249
  class << self
@@ -263,6 +280,18 @@ module Hutch
263
280
  end
264
281
  end
265
282
 
283
+ def self.type_cast(attr, value)
284
+ case
285
+ when is_bool(attr) || value == 'false'
286
+ to_bool(value)
287
+ when is_num(attr)
288
+ value.to_i
289
+ else
290
+ value
291
+ end
292
+ end
293
+ private_class_method :type_cast
294
+
266
295
  def self.define_methods
267
296
  @config.keys.each do |key|
268
297
  define_singleton_method(key) do
@@ -13,11 +13,17 @@ module Hutch
13
13
  end
14
14
 
15
15
  def reject!
16
- broker.reject(delivery_info.delivery_tag)
16
+ @message_rejected = true
17
+ broker.reject(delivery_info.delivery_tag, channel: delivery_info.channel)
17
18
  end
18
19
 
19
20
  def requeue!
20
- broker.requeue(delivery_info.delivery_tag)
21
+ @message_rejected = true
22
+ broker.requeue(delivery_info.delivery_tag, channel: delivery_info.channel)
23
+ end
24
+
25
+ def message_rejected?
26
+ !!@message_rejected
21
27
  end
22
28
 
23
29
  def logger
@@ -29,18 +35,56 @@ module Hutch
29
35
  # wants to subscribe to.
30
36
  def consume(*routing_keys)
31
37
  @routing_keys = self.routing_keys.union(routing_keys)
38
+ # these are opt-in
39
+ @queue_mode = nil
40
+ @queue_type = nil
32
41
  end
33
42
 
43
+ attr_reader :queue_mode, :queue_type, :initial_group_size
44
+
34
45
  # Explicitly set the queue name
35
46
  def queue_name(name)
36
47
  @queue_name = name
37
48
  end
38
49
 
39
- # Allow to specify custom arguments that will be passed when creating the queue.
50
+ # Opt out of the namespace prefix for this consumer's queue
51
+ def without_namespace
52
+ @without_namespace = true
53
+ end
54
+
55
+ def without_namespace?
56
+ !!@without_namespace
57
+ end
58
+
59
+ # Explicitly set the queue mode to 'lazy'
60
+ def lazy_queue
61
+ @queue_mode = 'lazy'
62
+ end
63
+
64
+ # Explicitly set the queue type to 'classic'
65
+ def classic_queue
66
+ @queue_type = 'classic'
67
+ end
68
+
69
+ # Explicitly set the queue type to 'quorum'
70
+ # @param [Hash] options the options params related to quorum queue
71
+ # @option options [Integer] :initial_group_size Initial Replication Factor
72
+ def quorum_queue(options = {})
73
+ @queue_type = 'quorum'
74
+ @initial_group_size = options[:initial_group_size]
75
+ end
76
+
77
+ # Configures an optional argument that will be passed when declaring the queue.
78
+ # Prefer using a policy to this DSL: https://www.rabbitmq.com/parameters.html#policies
40
79
  def arguments(arguments = {})
41
80
  @arguments = arguments
42
81
  end
43
82
 
83
+ # Configures queue options that will be passed when declaring the queue.
84
+ def queue_options(options = {})
85
+ @queue_options = options
86
+ end
87
+
44
88
  # Set custom serializer class, override global value
45
89
  def serializer(name)
46
90
  @serializer = name
@@ -58,7 +102,22 @@ module Hutch
58
102
 
59
103
  # Returns consumer custom arguments.
60
104
  def get_arguments
61
- @arguments || {}
105
+ all_arguments = @arguments || {}
106
+
107
+ all_arguments['x-queue-mode'] = @queue_mode if @queue_mode
108
+ all_arguments['x-queue-type'] = @queue_type if @queue_type
109
+ all_arguments['x-quorum-initial-group-size'] = @initial_group_size if @initial_group_size
110
+
111
+ all_arguments
112
+ end
113
+
114
+ def get_options
115
+ default_options = { durable: true }
116
+
117
+ all_options = default_options.merge(@queue_options || {})
118
+ all_options[:arguments] = get_arguments
119
+
120
+ all_options
62
121
  end
63
122
 
64
123
  # Accessor for the consumer's routing key.
@@ -0,0 +1,30 @@
1
+ require "hutch/logging"
2
+ require "bugsnag"
3
+ require "hutch/error_handlers/base"
4
+
5
+ module Hutch
6
+ module ErrorHandlers
7
+ class Bugsnag < Base
8
+ def handle(properties, payload, consumer, ex)
9
+ message_id = properties.message_id
10
+ prefix = "message(#{message_id || "-"}):"
11
+ logger.error "#{prefix} Logging event to Bugsnag"
12
+ logger.error "#{prefix} #{ex.class} - #{ex.message}"
13
+
14
+ ::Bugsnag.notify(ex) do |report|
15
+ report.add_tab(:hutch, {
16
+ payload: payload,
17
+ consumer: consumer
18
+ })
19
+ end
20
+ end
21
+
22
+ def handle_setup_exception(ex)
23
+ logger.error "Logging setup exception to Bugsnag"
24
+ logger.error "#{ex.class} - #{ex.message}"
25
+
26
+ ::Bugsnag.notify(ex)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,29 +1,27 @@
1
1
  require 'hutch/logging'
2
- require 'opbeat'
2
+ require 'rollbar'
3
3
  require 'hutch/error_handlers/base'
4
4
 
5
5
  module Hutch
6
6
  module ErrorHandlers
7
- class Opbeat < Base
8
-
9
- def initialize
10
- unless ::Opbeat.respond_to?(:report)
11
- raise "The Opbeat error handler requires Opbeat >= 3.0"
12
- end
13
- end
14
-
7
+ class Rollbar < Base
15
8
  def handle(properties, payload, consumer, ex)
16
9
  message_id = properties.message_id
17
10
  prefix = "message(#{message_id || '-'}):"
18
- logger.error "#{prefix} Logging event to Opbeat"
11
+ logger.error "#{prefix} Logging event to Rollbar"
19
12
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
20
- ::Opbeat.report(ex, extra: { payload: payload })
13
+
14
+ ::Rollbar.error(ex,
15
+ payload: payload,
16
+ consumer: consumer
17
+ )
21
18
  end
22
19
 
23
20
  def handle_setup_exception(ex)
24
- logger.error "Logging setup exception to Opbeat"
21
+ logger.error "Logging setup exception to Rollbar"
25
22
  logger.error "#{ex.class} - #{ex.message}"
26
- ::Opbeat.report(ex)
23
+
24
+ ::Rollbar.error(ex)
27
25
  end
28
26
  end
29
27
  end
@@ -1,31 +1,26 @@
1
1
  require 'hutch/logging'
2
- require 'raven'
2
+ require 'sentry-ruby'
3
3
  require 'hutch/error_handlers/base'
4
4
 
5
5
  module Hutch
6
6
  module ErrorHandlers
7
7
  class Sentry < Base
8
-
9
- def initialize
10
- unless Raven.respond_to?(:capture_exception)
11
- raise "The Hutch Sentry error handler requires Raven >= 0.4.0"
12
- end
13
- end
14
-
15
8
  def handle(properties, payload, consumer, ex)
16
9
  message_id = properties.message_id
17
10
  prefix = "message(#{message_id || '-'}):"
18
11
  logger.error "#{prefix} Logging event to Sentry"
19
12
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
20
- Raven.capture_exception(ex, extra: { payload: payload })
13
+ ::Sentry.configure_scope do |scope|
14
+ scope.set_context("payload", JSON.parse(payload))
15
+ end
16
+ ::Sentry.capture_exception(ex)
21
17
  end
22
18
 
23
19
  def handle_setup_exception(ex)
24
20
  logger.error "Logging setup exception to Sentry"
25
21
  logger.error "#{ex.class} - #{ex.message}"
26
- Raven.capture_exception(ex)
22
+ ::Sentry.capture_exception(ex)
27
23
  end
28
-
29
24
  end
30
25
  end
31
26
  end
@@ -4,6 +4,7 @@ module Hutch
4
4
  autoload :Sentry, 'hutch/error_handlers/sentry'
5
5
  autoload :Honeybadger, 'hutch/error_handlers/honeybadger'
6
6
  autoload :Airbrake, 'hutch/error_handlers/airbrake'
7
- autoload :Opbeat, 'hutch/error_handlers/opbeat'
7
+ autoload :Rollbar, 'hutch/error_handlers/rollbar'
8
+ autoload :Bugsnag, 'hutch/error_handlers/bugsnag'
8
9
  end
9
10
  end
@@ -42,7 +42,7 @@ module Hutch
42
42
  private
43
43
 
44
44
  def log_publication(serializer, payload, routing_key)
45
- logger.info {
45
+ logger.debug {
46
46
  spec =
47
47
  if serializer.binary?
48
48
  "#{payload.bytesize} bytes message"
@@ -1,4 +1,4 @@
1
- require 'multi_json'
1
+ require 'json'
2
2
  require 'active_support/core_ext/hash/indifferent_access'
3
3
 
4
4
  module Hutch
@@ -6,11 +6,11 @@ module Hutch
6
6
  class JSON
7
7
 
8
8
  def self.encode(payload)
9
- ::JSON.dump(payload)
9
+ ::JSON.generate(payload)
10
10
  end
11
11
 
12
12
  def self.decode(payload)
13
- ::MultiJson.load(payload).with_indifferent_access
13
+ ::JSON.parse(payload).with_indifferent_access
14
14
  end
15
15
 
16
16
  def self.binary? ; false ; end