banter 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/README.md +4 -3
- data/Rakefile +6 -0
- data/lib/banter.rb +7 -10
- data/lib/banter/cli.rb +1 -0
- data/lib/banter/configuration.rb +38 -6
- data/lib/banter/publisher.rb +13 -18
- data/lib/banter/rabbit_logger.rb +32 -0
- data/lib/banter/railtie.rb +10 -0
- data/lib/banter/server/client_queue_listener.rb +3 -3
- data/lib/banter/server/rabbit_mq_subscriber.rb +7 -8
- data/lib/banter/server/subscriber_server.rb +3 -3
- data/lib/banter/subscriber.rb +9 -4
- data/lib/banter/version.rb +1 -1
- data/lib/generators/banter/install_generator.rb +13 -0
- data/lib/generators/banter/templates/banter.yml +38 -0
- data/spec/banter/configuration_spec.rb +10 -0
- data/spec/{message_spec.rb → banter/message_spec.rb} +0 -0
- data/spec/banter/rabbit_logger_spec.rb +73 -0
- data/spec/banter/server/client_queue_listener_spec.rb +2 -3
- data/spec/banter/{server/client_worker_spec.rb → subscriber_spec.rb} +17 -1
- data/spec/spec_helper.rb +2 -1
- metadata +14 -11
- data/config/pubsub.yml +0 -17
- data/lib/banter/logger.rb +0 -49
- data/lib/banter/logging.rb +0 -42
- data/spec/logger_spec.rb +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d0f8fa6fb6efcc9befadeaac184560b989606f3
|
4
|
+
data.tar.gz: 55445f64eb387bacb905718ba61284d272d72db7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 820d17a44fdb4b450f0d982c689ccc16883d589208c990db1b9e7279109876e55585c11404dc5dccf1839e823fe513ef59575c37d010784ec6f3f529e2195b6a
|
7
|
+
data.tar.gz: 5e74cca350d83ac11c6a8e32ca4fb771d931593616839b5086f4f25118a48d5e3a6f663dd7b3aea6833a7c3910b029687bca1c75afe8eb6850a9da57f7f1a209
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
Simple Publishers and subscribers for Ruby using RabbitMQ
|
4
4
|
|
5
|
-
[![
|
5
|
+
[![Build Status](https://travis-ci.org/honest/banter.svg?branch=master)](https://travis-ci.org/honest/banter)
|
6
|
+
![Gem Version](https://badge.fury.io/rb/banter.svg?foo=bar)
|
6
7
|
|
7
8
|
Developed & maintained by [The Honest Company](https://www.honest.com). We are always looking for the best talent, find your next opportunity at [Honest Careers](https://www.honest.com/careers)
|
8
9
|
|
@@ -61,8 +62,8 @@ You can declare a subscriber class as follows
|
|
61
62
|
|
62
63
|
```ruby
|
63
64
|
class UserWelcomeSubscriber < Banter::Subscriber
|
64
|
-
subscribe_to "user_created"
|
65
|
-
#
|
65
|
+
subscribe_to "user_created"
|
66
|
+
# Message prefix that you're subscribing to. You can also specify options for queue_name and queue_ttl
|
66
67
|
|
67
68
|
def perform(payload)
|
68
69
|
# My awesome logic goes here...
|
data/Rakefile
CHANGED
data/lib/banter.rb
CHANGED
@@ -5,9 +5,8 @@ require "airbrake"
|
|
5
5
|
|
6
6
|
require "banter/configuration"
|
7
7
|
require "banter/context"
|
8
|
-
require "banter/db_logger"
|
9
|
-
require "banter/
|
10
|
-
require "banter/logging"
|
8
|
+
# require "banter/db_logger"
|
9
|
+
require "banter/rabbit_logger"
|
11
10
|
require "banter/message"
|
12
11
|
require "banter/publisher"
|
13
12
|
require "banter/server/rabbit_mq_subscriber"
|
@@ -23,10 +22,6 @@ end
|
|
23
22
|
|
24
23
|
module Banter
|
25
24
|
|
26
|
-
def self.root
|
27
|
-
File.expand_path '../..', __FILE__
|
28
|
-
end
|
29
|
-
|
30
25
|
# This method publishes payload to rabbitmq. All listeners with appropriate
|
31
26
|
# routing keys will receive the payload.
|
32
27
|
# @param [String] routing_key Identifier of the message type
|
@@ -36,12 +31,14 @@ module Banter
|
|
36
31
|
Publisher.instance.publish(Banter::Context.instance, routing_key, payload)
|
37
32
|
end
|
38
33
|
|
34
|
+
# @return [Logger] Logger used for logging through the Banter gem
|
39
35
|
def self.logger
|
40
|
-
Banter::
|
36
|
+
return Banter::Configuration.logger if Banter::Configuration.logger
|
37
|
+
@logger ||= ActiveSupport::TaggedLogging.new(Logger.new($stdout))
|
41
38
|
end
|
42
39
|
|
43
|
-
# @param [Logger] logger Logger
|
40
|
+
# @param [Logger] logger Logger for publish, message received and error events
|
44
41
|
def self.logger=(logger)
|
45
|
-
Banter::
|
42
|
+
Banter::Configuration.logger = ActiveSupport::TaggedLogging.new(logger)
|
46
43
|
end
|
47
44
|
end
|
data/lib/banter/cli.rb
CHANGED
data/lib/banter/configuration.rb
CHANGED
@@ -1,9 +1,41 @@
|
|
1
1
|
module Banter
|
2
|
-
|
2
|
+
module Configuration
|
3
3
|
@@conf = nil
|
4
4
|
|
5
|
+
# Configuration setting to specify the number of seconds a message will live in the queue before
|
6
|
+
# being either consumed or discarded.
|
7
|
+
# @default 1 hour
|
8
|
+
mattr_accessor :default_queue_ttl
|
9
|
+
@@default_queue_ttl = 1.hour
|
10
|
+
|
11
|
+
# The exchange to publish (and subscribe) on RabbitMQ
|
12
|
+
# @default true
|
13
|
+
mattr_accessor :exchange_name
|
14
|
+
@@exchange_name = "banter"
|
15
|
+
|
16
|
+
# The topic prefix for RabbitMQ
|
17
|
+
# @default banter
|
18
|
+
mattr_accessor :topic_prefix
|
19
|
+
@@topic_prefix = "banter"
|
20
|
+
|
21
|
+
mattr_accessor :logger
|
22
|
+
|
23
|
+
# RabbitMQ push and subscribe logging toggle
|
24
|
+
# @default true
|
25
|
+
mattr_accessor :logging_enabled
|
26
|
+
@@logging_enabled = true
|
27
|
+
|
28
|
+
# RabbitMQ push enabled
|
29
|
+
# @default true
|
30
|
+
mattr_accessor :push_enabled
|
31
|
+
@@push_enabled = true
|
32
|
+
|
33
|
+
# Dead letter queue name
|
34
|
+
# @default nil
|
35
|
+
mattr_accessor :dead_letter_queue
|
36
|
+
|
5
37
|
def self.configure_with(environment_name, yaml_file = nil)
|
6
|
-
@@yaml_file = yaml_file.nil? ? "config/
|
38
|
+
@@yaml_file = yaml_file.nil? ? "config/banter.yml" : yaml_file
|
7
39
|
@@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
|
8
40
|
@@conf = @@all_conf[environment_name.to_sym]
|
9
41
|
self
|
@@ -14,6 +46,10 @@ module Banter
|
|
14
46
|
@@conf
|
15
47
|
end
|
16
48
|
|
49
|
+
def self.connection
|
50
|
+
configuration[:connection]
|
51
|
+
end
|
52
|
+
|
17
53
|
def self.environment
|
18
54
|
val = ENV["RAILS_ENV"] || ENV["RACK_ENV"]
|
19
55
|
val = if val.present?
|
@@ -27,10 +63,6 @@ module Banter
|
|
27
63
|
end
|
28
64
|
end
|
29
65
|
|
30
|
-
def self.default_queue_ttl
|
31
|
-
24.hours * 1000
|
32
|
-
end
|
33
|
-
|
34
66
|
def self.application_name
|
35
67
|
@application_name ||= if defined?(Rails)
|
36
68
|
Rails.application.class.name.to_s.gsub("::Application", '')
|
data/lib/banter/publisher.rb
CHANGED
@@ -12,14 +12,9 @@ module Banter
|
|
12
12
|
@@publisher
|
13
13
|
end
|
14
14
|
|
15
|
-
def initialize(exchange=
|
16
|
-
@exchange = exchange
|
15
|
+
def initialize(exchange = nil)
|
16
|
+
@exchange = exchange || Banter::Configuration.exchange_name
|
17
17
|
@disabled = false
|
18
|
-
@logger = ::Banter::Logger.new()
|
19
|
-
|
20
|
-
@config = Configuration.configuration
|
21
|
-
|
22
|
-
self
|
23
18
|
end
|
24
19
|
|
25
20
|
def enable(value)
|
@@ -28,47 +23,47 @@ module Banter
|
|
28
23
|
end
|
29
24
|
|
30
25
|
def start
|
31
|
-
|
26
|
+
unless Configuration.push_enabled
|
32
27
|
@disabled = true
|
33
28
|
return
|
34
29
|
end
|
35
30
|
|
36
31
|
# grab server configuration from initialization file somewhere
|
37
32
|
begin
|
38
|
-
@connection = Bunny.new(Configuration.
|
33
|
+
@connection = Bunny.new(Configuration.connection)
|
39
34
|
@connection.start
|
40
35
|
|
41
|
-
@channel
|
42
|
-
@publisher = @channel.topic(@exchange, :durable=>true, :auto_delete=>false)
|
36
|
+
@channel = @connection.create_channel
|
37
|
+
@publisher = @channel.topic(@exchange, :durable => true, :auto_delete => false)
|
43
38
|
|
44
39
|
rescue => e
|
45
|
-
Airbrake.notify(e, parameters: {message: e.message}, environment_name: ENV['RAILS_ENV']
|
40
|
+
Airbrake.notify(e, parameters: { message: e.message }, environment_name: ENV['RAILS_ENV'])
|
46
41
|
return
|
47
42
|
end
|
48
43
|
|
49
44
|
|
50
45
|
@publisher.on_return do |return_info, properties, content|
|
51
46
|
# contents are already transformed into message that we want to send
|
52
|
-
|
47
|
+
Banter::RabbitLogger.failed_publish(return_info[:routing_key], properties, content)
|
53
48
|
end
|
54
49
|
|
55
50
|
end
|
56
51
|
|
57
52
|
def publish(context, key, payload, enabled = true)
|
58
53
|
routing_key = "#{@exchange}.#{key}"
|
59
|
-
envelope
|
54
|
+
envelope = ::Banter::Message.new.serialize(context, key, payload)
|
60
55
|
|
61
56
|
if @publisher.nil?
|
62
57
|
start
|
63
58
|
end
|
64
59
|
|
65
60
|
if @disabled || @publisher.nil? || !enabled
|
66
|
-
|
61
|
+
Banter::RabbitLogger.failed_publish(routing_key, {}, envelope)
|
67
62
|
else
|
68
63
|
tries = 2
|
69
64
|
begin
|
70
|
-
@publisher.publish(envelope.to_json, :persistent=>true, :mandatory=>true, :timestamp=>envelope[:ts], :content_type=>"application/json", :routing_key =>routing_key
|
71
|
-
|
65
|
+
@publisher.publish(envelope.to_json, :persistent => true, :mandatory => true, :timestamp => envelope[:ts], :content_type => "application/json", :routing_key => routing_key)
|
66
|
+
Banter::RabbitLogger.log_publish(routing_key, envelope)
|
72
67
|
rescue => e
|
73
68
|
tries -= 1
|
74
69
|
teardown
|
@@ -76,7 +71,7 @@ module Banter
|
|
76
71
|
if tries > 0 && @publisher
|
77
72
|
retry
|
78
73
|
else
|
79
|
-
|
74
|
+
Banter::RabbitLogger.failed_publish(routing_key, {}, envelope)
|
80
75
|
end
|
81
76
|
end
|
82
77
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Internal log class that is used to log messages before sending, after receiving, and failure to send
|
2
|
+
module Banter
|
3
|
+
class RabbitLogger
|
4
|
+
def self.enabled?
|
5
|
+
Banter::Configuration.logging_enabled
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.log_publish(routing_key, message)
|
9
|
+
return unless enabled?
|
10
|
+
tags = ["BANTER PUBLISH", message[:ts], message[:pub], message[:v], routing_key]
|
11
|
+
Banter.logger.tagged(tags) { Banter.logger.debug message[:payload].as_json }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.failed_publish(routing_key, properties, message)
|
15
|
+
return unless enabled?
|
16
|
+
tags = ["BANTER FAILED_SEND", message[:ts], message[:pub], message[:v], routing_key]
|
17
|
+
Banter.logger.tagged(tags) { Banter.logger.error message[:payload].as_json }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.log_receive(routing_key, message)
|
21
|
+
return unless enabled?
|
22
|
+
tags = ["BANTER RECEIVED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
|
23
|
+
Banter.logger.tagged(tags) { Banter.logger.debug message[:payload].as_json }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.log_service(service_name, message)
|
27
|
+
return unless enabled?
|
28
|
+
tags = ["BANTER SERVICE", service_name, Process::pid]
|
29
|
+
Banter.logger.tagged(tags) {Banter.logger.info message[:payload].as_json}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/banter/railtie.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
# This railtie provides Rails engine hooks
|
2
|
+
require 'banter/middleware'
|
3
|
+
require 'banter/configuration'
|
2
4
|
|
3
5
|
module Banter
|
4
6
|
class Railtie < ::Rails::Railtie
|
7
|
+
config.banter = Banter::Configuration
|
8
|
+
|
5
9
|
initializer "banter.insert_middleware" do |app|
|
6
10
|
app.config.middleware.use Banter::Middleware
|
7
11
|
end
|
8
12
|
|
13
|
+
config.after_initialize do
|
14
|
+
Banter.logger = Rails.logger
|
15
|
+
end
|
16
|
+
|
9
17
|
console do
|
10
18
|
# Set up basic context to identify messages getting generated from console
|
11
19
|
Banter::Context.setup_context(
|
@@ -23,5 +31,7 @@ module Banter
|
|
23
31
|
unique_id: Digest::SHA1.new.to_s
|
24
32
|
)
|
25
33
|
end
|
34
|
+
|
35
|
+
|
26
36
|
end
|
27
37
|
end
|
@@ -10,13 +10,13 @@ module Banter
|
|
10
10
|
|
11
11
|
attr_reader :worker_class
|
12
12
|
|
13
|
-
def initialize(worker_class, request_key, queue, durable = true, topic =
|
14
|
-
@topic = topic
|
13
|
+
def initialize(worker_class, request_key, queue, durable = true, topic = nil)
|
14
|
+
@topic = topic || Banter::Configuration.topic_prefix
|
15
15
|
@request_key = request_key
|
16
16
|
@worker_class = worker_class
|
17
17
|
@queue_name = queue
|
18
18
|
@durable = durable
|
19
|
-
@subscriber = ::Banter::Server::RabbitMQSubscriber.new(@request_key, @durable, @topic)
|
19
|
+
@subscriber = ::Banter::Server::RabbitMQSubscriber.new(@request_key, worker_class.queue_ttl, @durable, @topic)
|
20
20
|
end
|
21
21
|
|
22
22
|
def start
|
@@ -5,27 +5,26 @@ module Banter
|
|
5
5
|
attr_reader :listener
|
6
6
|
attr_reader :exchange
|
7
7
|
attr_reader :channel
|
8
|
+
attr_reader :ttl
|
8
9
|
|
9
|
-
def initialize(routing_key, durable = true, topic=
|
10
|
+
def initialize(routing_key, ttl, durable = true, topic = Banter::Configuration.topic_prefix)
|
10
11
|
@initial_key = routing_key
|
11
12
|
@durable = durable
|
12
13
|
@topic = topic
|
14
|
+
@ttl = ttl
|
13
15
|
|
14
16
|
if @initial_key.present?
|
15
17
|
@routing_key = "#{@topic}.#{@initial_key}.#"
|
16
18
|
else
|
17
19
|
@routing_key = "#{@topic}.#"
|
18
20
|
end
|
19
|
-
@logger = ::Banter::Logger.new
|
20
|
-
|
21
|
-
self
|
22
21
|
end
|
23
22
|
|
24
23
|
# name - used to ensure that certain consumers are actually listening to an exchange
|
25
24
|
# pass in a lambda for this method to work. We might only want to expose the content instead of
|
26
25
|
# all 3 chunks.
|
27
26
|
def start(name, blocking=false)
|
28
|
-
@connection = Bunny.new(Configuration.
|
27
|
+
@connection = Bunny.new(Configuration.connection)
|
29
28
|
begin
|
30
29
|
@connection.start
|
31
30
|
rescue => e
|
@@ -46,8 +45,8 @@ module Banter
|
|
46
45
|
end
|
47
46
|
|
48
47
|
queue_arguments = {}
|
49
|
-
queue_arguments["x-dead-letter-exchange"] = Configuration.
|
50
|
-
queue_arguments["x-message-ttl"] =
|
48
|
+
queue_arguments["x-dead-letter-exchange"] = Configuration.dead_letter_queue if Configuration.dead_letter_queue.present?
|
49
|
+
queue_arguments["x-message-ttl"] = ttl * 1000
|
51
50
|
@listener = @channel.queue(@queue, arguments: queue_arguments).bind(@exchange, routing_key: @routing_key, exclusive: false)
|
52
51
|
|
53
52
|
# Parameters for subscribe that might be useful:
|
@@ -60,7 +59,7 @@ module Banter
|
|
60
59
|
Airbrake.notify("PubSub Message redelivery", params: { info: delivery_info, props: properties, contents: contents }, environment_name: ENV['RAILS_ENV'])
|
61
60
|
end
|
62
61
|
message = ::Banter::Message.new.parse(contents)
|
63
|
-
|
62
|
+
Banter::RabbitLogger.log_receive(delivery_info[:routing_key], message)
|
64
63
|
yield delivery_info, properties, message
|
65
64
|
true
|
66
65
|
end
|
@@ -38,15 +38,15 @@ module Banter
|
|
38
38
|
|
39
39
|
Thread.stop
|
40
40
|
|
41
|
-
::Banter::
|
41
|
+
::Banter::RabbitLogger.log_service("all_services", "Starting shutdown of all services")
|
42
42
|
|
43
43
|
@workers.each do |worker|
|
44
|
-
::Banter::
|
44
|
+
::Banter::RabbitLogger.log_service("all_services", "Tearing down worker: #{worker.worker_class.name}")
|
45
45
|
begin
|
46
46
|
STDOUT.puts "Tearing down subscriber for #{worker.worker_class.name}"
|
47
47
|
worker.shutdown
|
48
48
|
rescue => e
|
49
|
-
::Banter::
|
49
|
+
::Banter::RabbitLogger.log_service("all_services", "#{worker.worker_class.name} - did not tear down correctly. Error - #{e.message}")
|
50
50
|
end
|
51
51
|
end
|
52
52
|
ensure
|
data/lib/banter/subscriber.rb
CHANGED
@@ -8,7 +8,7 @@ module Banter
|
|
8
8
|
class Subscriber
|
9
9
|
@@registered_subscribers = []
|
10
10
|
|
11
|
-
class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue
|
11
|
+
class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue, :queue_ttl
|
12
12
|
attr_accessor :delivery_routing_data, :delivery_properties, :context
|
13
13
|
|
14
14
|
def self.inherited(klass)
|
@@ -17,14 +17,19 @@ module Banter
|
|
17
17
|
|
18
18
|
# Specify the routing key that the subscriber class should listen to.
|
19
19
|
# @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
|
20
|
-
# @param [Hash] options
|
20
|
+
# @param [Hash] options subscription options
|
21
|
+
# @option [String] :on Optionally specify a queue. If not provided, queue name is generated from the routing key
|
22
|
+
# @option [Integer] :queue_ttl Time, in seconds, that the message lives on the queue before being either consumer by a subscriber or being discarded.
|
23
|
+
# If not specified, then Banter::Configuration.default_queue_ttl is used
|
24
|
+
|
21
25
|
def self.subscribe_to(routing_key_name, options = {})
|
22
|
-
options.assert_valid_keys(:on)
|
26
|
+
options.assert_valid_keys(:on, :queue_ttl)
|
23
27
|
unless validate_routing_key_name(routing_key_name)
|
24
28
|
raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
|
25
29
|
end
|
26
30
|
self.subscribed_key = routing_key_name
|
27
31
|
self.subscribed_queue = generated_queue_name(routing_key_name, options[:on])
|
32
|
+
self.queue_ttl = options[:queue_ttl] || Banter::Configuration.default_queue_ttl
|
28
33
|
end
|
29
34
|
|
30
35
|
# Sets the validator for payload
|
@@ -59,7 +64,7 @@ module Banter
|
|
59
64
|
raise ::Banter::PayloadValidationError.new("Invalid Payload for #{self.class.name}")
|
60
65
|
end
|
61
66
|
|
62
|
-
perform(
|
67
|
+
perform(payload)
|
63
68
|
end
|
64
69
|
|
65
70
|
# Actual subscribers need to implement perform method. This is the method where the message is actually processed.
|
data/lib/banter/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Banter
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path("../templates", __FILE__)
|
4
|
+
def copy_config_file
|
5
|
+
copy_file "banter.yml", "config/banter.yml"
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_config_parameters
|
9
|
+
application "config.banter.default_queue_ttl = 1.hour"
|
10
|
+
application "config.banter.logger = Rails.logger"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
development:
|
2
|
+
|
3
|
+
# Documentation for parameters for rabbit and bunny is located here: http://rubybunny.info/articles/connecting.html
|
4
|
+
connection:
|
5
|
+
host: localhost
|
6
|
+
port: 5672
|
7
|
+
# username: rabbit
|
8
|
+
# password: rabbit
|
9
|
+
heartbeat: 60 # in seconds
|
10
|
+
log_level: 0
|
11
|
+
# log_file: log/rabbit.log
|
12
|
+
network_recovery_interval: 10 # in seconds
|
13
|
+
continuation_timeout: 4000 # in milliseconds
|
14
|
+
|
15
|
+
test:
|
16
|
+
# Documentation for parameters for rabbit and bunny is located here: http://rubybunny.info/articles/connecting.html
|
17
|
+
connection:
|
18
|
+
host: localhost
|
19
|
+
port: 5672
|
20
|
+
# username: rabbit
|
21
|
+
# password: rabbit
|
22
|
+
heartbeat: 60 # in seconds
|
23
|
+
log_level: 0
|
24
|
+
# log_file: log/rabbit.log
|
25
|
+
network_recovery_interval: 10 # in seconds
|
26
|
+
continuation_timeout: 4000 # in milliseconds
|
27
|
+
|
28
|
+
production:
|
29
|
+
connection:
|
30
|
+
host: localhost
|
31
|
+
port: 5672
|
32
|
+
# username: rabbit
|
33
|
+
# password: rabbit
|
34
|
+
heartbeat: 60 # in seconds
|
35
|
+
log_level: 0
|
36
|
+
# log_file: log/rabbit.log
|
37
|
+
network_recovery_interval: 10 # in seconds
|
38
|
+
continuation_timeout: 4000 # in milliseconds
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banter::Configuration do
|
4
|
+
context "default_queue_ttl" do
|
5
|
+
context 'value overridden' do
|
6
|
+
before { Banter::Configuration.default_queue_ttl = 2.hour }
|
7
|
+
it { expect(Banter::Configuration.default_queue_ttl).to eq 2.hour }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
File without changes
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banter::RabbitLogger do
|
4
|
+
let(:routing_key) { "test/logger" }
|
5
|
+
let(:subject) { Banter::RabbitLogger }
|
6
|
+
let(:payload) { {"hit"=>"me"} }
|
7
|
+
let(:message) { Banter::Message.new.serialize(context, routing_key, payload) }
|
8
|
+
let(:config_buffer) { StringIO.new }
|
9
|
+
let(:output_string) { config_buffer.string }
|
10
|
+
let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(Banter.logger).to receive(:debug)
|
14
|
+
allow(Banter.logger).to receive(:info)
|
15
|
+
allow(Banter.logger).to receive(:warning)
|
16
|
+
allow(Banter.logger).to receive(:error)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#log_publish" do
|
20
|
+
let!(:result) { subject.log_publish(routing_key, message)}
|
21
|
+
|
22
|
+
it "should not fail" do
|
23
|
+
expect{ result }.not_to raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should write a row to the log" do
|
27
|
+
expect(Banter.logger).to have_received(:debug).with(anything)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#failed_publish" do
|
33
|
+
let!(:result) { subject.failed_publish(routing_key, {}, message)}
|
34
|
+
|
35
|
+
context "warning log level" do
|
36
|
+
|
37
|
+
it "should not fail" do
|
38
|
+
expect{ result }.not_to raise_error()
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should respect the log level of the file" do
|
42
|
+
expect(Banter.logger).to have_received(:error).with(anything)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#log_receive" do
|
48
|
+
let!(:result) { subject.log_receive(routing_key, message)}
|
49
|
+
|
50
|
+
it "should not fail" do
|
51
|
+
expect{ result }.not_to raise_error()
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should respect the log level of the file" do
|
55
|
+
expect(Banter.logger).to have_received(:debug).with(anything)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#log_service" do
|
60
|
+
let(:service_name) { "test" }
|
61
|
+
let!(:result) { subject.log_service(service_name, message)}
|
62
|
+
|
63
|
+
context "debugger log" do
|
64
|
+
it "should not fail" do
|
65
|
+
expect{ result }.not_to raise_error()
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should respect the log level of the file" do
|
69
|
+
expect(Banter.logger).to have_received(:info).with(anything)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -31,18 +31,17 @@ describe Banter::Server::ClientQueueListener do
|
|
31
31
|
context "Normal execution" do
|
32
32
|
let!(:klass_instance){
|
33
33
|
instance = Klass.new({},{}, {})
|
34
|
-
allow(instance).to receive(:perform)
|
34
|
+
allow(instance).to receive(:perform!)
|
35
35
|
instance
|
36
36
|
}
|
37
37
|
|
38
38
|
context "test exception" do
|
39
39
|
let(:no_call) { true }
|
40
|
-
|
41
40
|
it { should_not raise_exception }
|
42
41
|
end
|
43
42
|
|
44
43
|
context "call" do
|
45
|
-
it { expect(klass_instance).to have_received(:perform).with(
|
44
|
+
it { expect(klass_instance).to have_received(:perform!).with(payload) }
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
@@ -150,12 +150,28 @@ describe Banter::Subscriber do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
context 'invalid routing key' do
|
153
|
-
before { }
|
154
153
|
it 'sets the routing key and queue name' do
|
155
154
|
expect {
|
156
155
|
Klass.subscribe_to 'a.b.c.', on: 'foo_bar'
|
157
156
|
}.to raise_error(ArgumentError)
|
158
157
|
end
|
159
158
|
end
|
159
|
+
|
160
|
+
context "queue_ttl" do
|
161
|
+
context "option provided" do
|
162
|
+
before { Klass.subscribe_to "a.b", queue_ttl: 4 }
|
163
|
+
it 'sets the ttl' do
|
164
|
+
expect(Klass.queue_ttl).to eq(4)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "option not provided" do
|
169
|
+
before { Klass.subscribe_to "a.b" }
|
170
|
+
it 'uses default' do
|
171
|
+
expect(Klass.queue_ttl).to eq(Banter::Configuration.default_queue_ttl)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
160
176
|
end
|
161
177
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -26,7 +26,8 @@ RSpec.configure do |config|
|
|
26
26
|
config.order = 'random'
|
27
27
|
end
|
28
28
|
|
29
|
-
Banter::Configuration.configure_with("test", File.
|
29
|
+
Banter::Configuration.configure_with("test", File.expand_path("../config.yml", __FILE__))
|
30
|
+
Banter::Configuration.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
|
30
31
|
|
31
32
|
# Some test subscriber classes to make testing easier
|
32
33
|
class MyTestSubscriber1 < Banter::Subscriber
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: banter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- The Honest Company
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-07-
|
14
|
+
date: 2014-07-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -193,24 +193,23 @@ extra_rdoc_files: []
|
|
193
193
|
files:
|
194
194
|
- .gitignore
|
195
195
|
- .ruby-version
|
196
|
+
- .travis.yml
|
196
197
|
- Gemfile
|
197
198
|
- LICENSE.txt
|
198
199
|
- README.md
|
199
200
|
- Rakefile
|
200
201
|
- banter.gemspec
|
201
202
|
- bin/start_subscribers
|
202
|
-
- config/pubsub.yml
|
203
203
|
- lib/banter.rb
|
204
204
|
- lib/banter/cli.rb
|
205
205
|
- lib/banter/configuration.rb
|
206
206
|
- lib/banter/context.rb
|
207
207
|
- lib/banter/db_logger.rb
|
208
208
|
- lib/banter/exceptions/payload_validation_error.rb
|
209
|
-
- lib/banter/logger.rb
|
210
|
-
- lib/banter/logging.rb
|
211
209
|
- lib/banter/message.rb
|
212
210
|
- lib/banter/middleware.rb
|
213
211
|
- lib/banter/publisher.rb
|
212
|
+
- lib/banter/rabbit_logger.rb
|
214
213
|
- lib/banter/railtie.rb
|
215
214
|
- lib/banter/server.rb
|
216
215
|
- lib/banter/server/client_queue_listener.rb
|
@@ -218,13 +217,16 @@ files:
|
|
218
217
|
- lib/banter/server/subscriber_server.rb
|
219
218
|
- lib/banter/subscriber.rb
|
220
219
|
- lib/banter/version.rb
|
220
|
+
- lib/generators/banter/install_generator.rb
|
221
|
+
- lib/generators/banter/templates/banter.yml
|
221
222
|
- spec/banter/cli_spec.rb
|
223
|
+
- spec/banter/configuration_spec.rb
|
224
|
+
- spec/banter/message_spec.rb
|
225
|
+
- spec/banter/rabbit_logger_spec.rb
|
222
226
|
- spec/banter/server/client_queue_listener_spec.rb
|
223
|
-
- spec/banter/server/client_worker_spec.rb
|
224
227
|
- spec/banter/server/rabbitmq_subscriber_spec.rb
|
228
|
+
- spec/banter/subscriber_spec.rb
|
225
229
|
- spec/config.yml
|
226
|
-
- spec/logger_spec.rb
|
227
|
-
- spec/message_spec.rb
|
228
230
|
- spec/spec_helper.rb
|
229
231
|
homepage: https://github.com/honest/banter
|
230
232
|
licenses:
|
@@ -252,10 +254,11 @@ specification_version: 4
|
|
252
254
|
summary: Library for pub-sub (Message Bus)
|
253
255
|
test_files:
|
254
256
|
- spec/banter/cli_spec.rb
|
257
|
+
- spec/banter/configuration_spec.rb
|
258
|
+
- spec/banter/message_spec.rb
|
259
|
+
- spec/banter/rabbit_logger_spec.rb
|
255
260
|
- spec/banter/server/client_queue_listener_spec.rb
|
256
|
-
- spec/banter/server/client_worker_spec.rb
|
257
261
|
- spec/banter/server/rabbitmq_subscriber_spec.rb
|
262
|
+
- spec/banter/subscriber_spec.rb
|
258
263
|
- spec/config.yml
|
259
|
-
- spec/logger_spec.rb
|
260
|
-
- spec/message_spec.rb
|
261
264
|
- spec/spec_helper.rb
|
data/config/pubsub.yml
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
development:
|
2
|
-
# Documentation for parameters for rabbit and bunny is located here: http://rubybunny.info/articles/connecting.html
|
3
|
-
connection:
|
4
|
-
host: localhost
|
5
|
-
port: 5672
|
6
|
-
# username: rabbit
|
7
|
-
# password: rabbit
|
8
|
-
heartbeat: 60 # in seconds
|
9
|
-
log_level: 0
|
10
|
-
log_file: rabbit.log
|
11
|
-
network_recovery_interval: 10 # in seconds
|
12
|
-
continuation_timeout: 4000 # in milliseconds
|
13
|
-
|
14
|
-
logger:
|
15
|
-
enabled: true
|
16
|
-
level: warn
|
17
|
-
file: pubsub.log
|
data/lib/banter/logger.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
# Internal log class that is used to log messages before sending, after receiving, and failure to send
|
2
|
-
module Banter
|
3
|
-
class Logger
|
4
|
-
def initialize(enable_publish_logging = true)
|
5
|
-
@enabled = Banter::Configuration.configuration[:logger][:enabled]
|
6
|
-
@enabled = enable_publish_logging if @enabled.nil?
|
7
|
-
end
|
8
|
-
|
9
|
-
def log_publish(routing_key, message)
|
10
|
-
return unless @enabled
|
11
|
-
# generate tags needed
|
12
|
-
tags = ["PUBLISH", message[:ts], message[:pub], message[:v], routing_key]
|
13
|
-
|
14
|
-
# FIX!!! -thl
|
15
|
-
# Could logging like this be too slow?
|
16
|
-
# Or should it be threaded?
|
17
|
-
# We'll need to benchmark, as we don't want this to get too slow.
|
18
|
-
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
19
|
-
|
20
|
-
# TODO: -thl
|
21
|
-
# Log it into mongo as well?
|
22
|
-
end
|
23
|
-
|
24
|
-
def failed_publish(routing_key, properties, message)
|
25
|
-
tags = ["FAILED_SEND", message[:ts], message[:pub], message[:v], routing_key]
|
26
|
-
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
27
|
-
end
|
28
|
-
|
29
|
-
def log_receive(routing_key, message)
|
30
|
-
tags = ["RECEIVED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
|
31
|
-
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
32
|
-
end
|
33
|
-
|
34
|
-
def log_service(service_name, log_level, message)
|
35
|
-
tags = ["SERVICE", service_name, Process::pid]
|
36
|
-
log_method = log_level.to_sym.to_proc
|
37
|
-
logger.tagged(tags) { log_method.call(logger) { message.as_json } }
|
38
|
-
end
|
39
|
-
|
40
|
-
def teardown
|
41
|
-
logger.close
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def logger
|
46
|
-
@logger ||= ActiveSupport::TaggedLogging.new(Banter.logger)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/banter/logging.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
# Manages a logger that can be used throughout the pubsub gem
|
4
|
-
# The logger can be set to any object that quacks like a logger including the
|
5
|
-
# Rails logger if so desired
|
6
|
-
#
|
7
|
-
# The logger can be set in the initializer of a program or anywhere throughout as
|
8
|
-
# Banter.logger = Rails.logger for example
|
9
|
-
|
10
|
-
module Banter
|
11
|
-
module Logging
|
12
|
-
# Sets the logger
|
13
|
-
#
|
14
|
-
# @param logger The logger to use throughout the pubsub gem
|
15
|
-
# @return The logger we use throughout the gem
|
16
|
-
def self.logger=(logger)
|
17
|
-
raise StandardError("Can't set logger to nil") unless logger.present?
|
18
|
-
@logger = logger
|
19
|
-
end
|
20
|
-
|
21
|
-
# Gets the logger
|
22
|
-
# If no logger is defined when this method is called it will return a standard
|
23
|
-
# ruby logger
|
24
|
-
#
|
25
|
-
# @return The logger we use throughout the gem
|
26
|
-
def self.logger
|
27
|
-
config = Banter::Configuration.configuration[:logger]
|
28
|
-
@logger ||= create_logger( {}.
|
29
|
-
merge( config[:level].present? ? { log_level: ::Logger::Severity.const_get(config[:level].upcase) } : {} ).
|
30
|
-
merge( config[:file].present? ? { log_target: config[:file] } : {} ) )
|
31
|
-
end
|
32
|
-
|
33
|
-
# Builds a standard ruby Logger
|
34
|
-
#
|
35
|
-
# @return Returns the logger at the end of the method
|
36
|
-
def self.create_logger( options = {} )
|
37
|
-
@logger = ::Logger.new(options.fetch(:log_target){ STDOUT })
|
38
|
-
@logger.level = options.fetch(:log_level){ ::Logger::INFO }
|
39
|
-
@logger
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/spec/logger_spec.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Banter::Logger do
|
4
|
-
let(:routing_key) { "test/logger" }
|
5
|
-
let(:subject) { Banter::Logger.new }
|
6
|
-
let(:payload) { {"hit"=>"me"} }
|
7
|
-
let(:message) { Banter::Message.new.serialize(context, routing_key, payload) }
|
8
|
-
let(:config_buffer) { StringIO.new }
|
9
|
-
let(:output_string) { config_buffer.string }
|
10
|
-
let(:config_override) { {} }
|
11
|
-
let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
|
12
|
-
|
13
|
-
before do
|
14
|
-
Banter::Logging.instance_variable_set(:"@logger", nil)
|
15
|
-
directory = File::dirname(__FILE__)
|
16
|
-
full_name = File.join(directory, "config.yml")
|
17
|
-
@config_data = HashWithIndifferentAccess.new( YAML.load_file(full_name)[ENV["RAILS_ENV"]] )
|
18
|
-
@config_data.merge!(config_override)
|
19
|
-
@config_data[:logger][:file] = config_buffer
|
20
|
-
allow(Banter::Configuration).to receive(:configuration).at_least(1).and_return(@config_data)
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "#log_publish" do
|
24
|
-
let(:result) { subject.log_publish(routing_key, message)}
|
25
|
-
|
26
|
-
|
27
|
-
it "should not fail" do
|
28
|
-
expect{ result }.not_to raise_error()
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should write a row to the log" do
|
32
|
-
result
|
33
|
-
expect(output_string.length).not_to eq(0)
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "#failed_publish" do
|
39
|
-
let(:result) { subject.failed_publish(routing_key, {}, message)}
|
40
|
-
|
41
|
-
context "warning log level" do
|
42
|
-
|
43
|
-
it "should not fail" do
|
44
|
-
expect{ result }.not_to raise_error()
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should respect the log level of the file" do
|
48
|
-
result
|
49
|
-
expect(output_string.length).not_to eq(0)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context "fatal log level" do
|
54
|
-
let(:config_override) { { 'logger'=> { 'level'=>'fatal' } } }
|
55
|
-
|
56
|
-
it "should not fail" do
|
57
|
-
expect{ result }.not_to raise_error()
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should respect the log level of the file" do
|
61
|
-
result
|
62
|
-
expect(output_string.length).to eq(0)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe "#log_receive" do
|
68
|
-
let(:result) { subject.log_receive(routing_key, message)}
|
69
|
-
|
70
|
-
it "should not fail" do
|
71
|
-
expect{ result }.not_to raise_error()
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should respect the log level of the file" do
|
75
|
-
result
|
76
|
-
expect(output_string.length).not_to eq(0)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe "#log_service" do
|
81
|
-
let(:service_name) { "test" }
|
82
|
-
let(:result) { subject.log_service(service_name, log_level, message)}
|
83
|
-
|
84
|
-
context "debugger log" do
|
85
|
-
let(:log_level) { :debug }
|
86
|
-
it "should not fail" do
|
87
|
-
expect{ result }.not_to raise_error()
|
88
|
-
end
|
89
|
-
|
90
|
-
it "should respect the log level of the file" do
|
91
|
-
result
|
92
|
-
expect(output_string.length).to eq(0)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context "warn log" do
|
97
|
-
let(:log_level) { :warn }
|
98
|
-
it "should not fail" do
|
99
|
-
expect{ result }.not_to raise_error()
|
100
|
-
end
|
101
|
-
|
102
|
-
it "should respect the log level of the file" do
|
103
|
-
result
|
104
|
-
expect(output_string.length).not_to eq(0)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|