honest_pubsub 0.2.2
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.
- checksums.yaml +7 -0
- data/#.ruby-gemset# +0 -0
- data/.gitignore +22 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +1 -0
- data/bin/start_subscribers +14 -0
- data/config/pubsub.yml +17 -0
- data/honest_pubsub.gemspec +38 -0
- data/lib/honest_pubsub/cli.rb +94 -0
- data/lib/honest_pubsub/configuration.rb +38 -0
- data/lib/honest_pubsub/context.rb +70 -0
- data/lib/honest_pubsub/db_logger.rb +52 -0
- data/lib/honest_pubsub/exceptions/payload_error.rb +2 -0
- data/lib/honest_pubsub/logger.rb +49 -0
- data/lib/honest_pubsub/logging.rb +42 -0
- data/lib/honest_pubsub/message.rb +50 -0
- data/lib/honest_pubsub/middleware.rb +14 -0
- data/lib/honest_pubsub/publisher.rb +93 -0
- data/lib/honest_pubsub/railtie.rb +27 -0
- data/lib/honest_pubsub/server/client_queue_listener.rb +54 -0
- data/lib/honest_pubsub/server/client_worker.rb +86 -0
- data/lib/honest_pubsub/server/subscriber_server.rb +84 -0
- data/lib/honest_pubsub/server.rb +8 -0
- data/lib/honest_pubsub/subscriber.rb +69 -0
- data/lib/honest_pubsub/version.rb +3 -0
- data/lib/honest_pubsub.rb +46 -0
- data/spec/config.yml +20 -0
- data/spec/honest_pubsub/#subscriber_spec.rb# +9 -0
- data/spec/honest_pubsub/cli_spec.rb +145 -0
- data/spec/honest_pubsub/server/client_queue_listener_spec.rb +76 -0
- data/spec/honest_pubsub/server/client_worker_spec.rb +161 -0
- data/spec/honest_pubsub/subscriber_spec.rb +5 -0
- data/spec/logger_spec.rb +110 -0
- data/spec/message_spec.rb +65 -0
- data/spec/spec_helper.rb +49 -0
- metadata +259 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cc6a19fcd7590a3594ce7dce30c525ffad8d862e
|
4
|
+
data.tar.gz: 3904cd4bde4b1498a993d868a0fc7c8b8bd0cec6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 07e917724a4763ceea12384d38ecd153fbc22d9055b648eb6ead02832512491394b7d1ecb4690b66c670cf0afd17a907dba57ccb9fcdc6981ef662621c9565ad
|
7
|
+
data.tar.gz: b28f6207461e9c0f0b09d31fda4c2819fb92cdab523f0e96294198a27e2d41895ea63d0f90310c5d4fa1907d54f6b15b194ddca0a51c5ae217cd64955d0b07ea
|
data/#.ruby-gemset#
ADDED
File without changes
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
.project
|
7
|
+
.ruby-gemset
|
8
|
+
Gemfile.lock
|
9
|
+
InstalledFiles
|
10
|
+
_yardoc
|
11
|
+
coverage
|
12
|
+
doc/
|
13
|
+
lib/bundler/man
|
14
|
+
pkg
|
15
|
+
rdoc
|
16
|
+
spec/reports
|
17
|
+
test/tmp
|
18
|
+
test/version_tmp
|
19
|
+
tmp
|
20
|
+
.rspec
|
21
|
+
.idea
|
22
|
+
test_pubsub.log
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Thanh Lim
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# HonestPubsub
|
2
|
+
|
3
|
+
Simple Publishers and subscribers for Ruby using RabbitMQ
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'honest_pubsub'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install honest_pubsub
|
18
|
+
|
19
|
+
You also need to install RabbitMQ
|
20
|
+
|
21
|
+
$ brew install rabbitmq
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
There are two sides to this gem, publishers and subscribers
|
26
|
+
|
27
|
+
### Publishers
|
28
|
+
|
29
|
+
Publishing to a message is super simple
|
30
|
+
|
31
|
+
HonestPubsub.publish('user.created', { id: 3, name: 'Foo Bar', email: 'foobar@email.com')
|
32
|
+
|
33
|
+
Additionally you can setup a context that is passed down to the subscribers. This context can be set up in a `before_filter`
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class ApplicationController < ActionController::Base
|
37
|
+
before_filter :setup_pubsub_context
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def setup_pubsub_context
|
42
|
+
HonestPubsub::Context.setup_context
|
43
|
+
unique_id: request.uuid,
|
44
|
+
orig_ip_address: request.remote_ip,
|
45
|
+
application: "my_app/web:#{self.class.name.underscore}/#{action_name}",
|
46
|
+
user_id: current_user.try(:id)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Context is automatically cleared up by the gem for a Rails Application. Otherwise, you can call
|
52
|
+
`HonestPubsub::Context.clear!`
|
53
|
+
|
54
|
+
### Subscribers
|
55
|
+
|
56
|
+
You can declare a subscriber class as follows
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class UserWelcomeSubscriber < HonestPubsub::Server::ClientWorker
|
60
|
+
subscribe_to "user_created" # The message prefix that you're subscribing to
|
61
|
+
# subscribe_to "user_created", on: 'welcome_emails_queue' # If you want to specify the queue name
|
62
|
+
|
63
|
+
def perform(context, payload)
|
64
|
+
# context is a hash that was put into HonestPubsub::Context
|
65
|
+
# Payload contains the data that was published
|
66
|
+
# My awesome logic goes here...
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
You also run subscribers in a separate process to consume the message using the provided executable
|
72
|
+
|
73
|
+
`bundle exec start_subscribers`
|
74
|
+
|
75
|
+
You can execute `bundle exec start_subscribers --help` to see all the various options that it providers
|
76
|
+
```
|
77
|
+
Usage: bundle exec start_subscribers [options]
|
78
|
+
-P, --pidfile PATH path to pidfile
|
79
|
+
-o, --only [SUBSCRIBERS] comma separated name of subsriber classes that should be run
|
80
|
+
-r, --require [PATH|DIR] Location of Rails application with workers or file to require
|
81
|
+
-v, --version Print version and exit
|
82
|
+
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
## Contributing
|
87
|
+
|
88
|
+
1. Fork it
|
89
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
90
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
91
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
92
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/config/pubsub.yml
ADDED
@@ -0,0 +1,17 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
# require all files in the folder?
|
6
|
+
require 'honest_pubsub/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = "honest_pubsub"
|
10
|
+
spec.version = HonestPubsub::VERSION
|
11
|
+
spec.authors = ["Thanh Lim"]
|
12
|
+
spec.email = ["thanh@thehonestcompany.com"]
|
13
|
+
spec.description = "Pub sub gem for Honest Company"
|
14
|
+
spec.summary = "Pub sub gem for Honest Company"
|
15
|
+
spec.homepage = ""
|
16
|
+
spec.license = "Private"
|
17
|
+
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake", ">= 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
27
|
+
spec.add_development_dependency "debugger"
|
28
|
+
spec.add_runtime_dependency "activesupport", ">= 3.2"
|
29
|
+
spec.add_runtime_dependency "awesome_print", "~> 1.2.0"
|
30
|
+
spec.add_runtime_dependency "bunny", ">= 1.2"
|
31
|
+
spec.add_runtime_dependency "airbrake", ">= 3.1"
|
32
|
+
spec.add_runtime_dependency "hashie", ">= 1.2"
|
33
|
+
spec.add_runtime_dependency "json", ">= 1.8"
|
34
|
+
spec.add_runtime_dependency "celluloid", ">= 0.15"
|
35
|
+
spec.add_runtime_dependency "celluloid-io", ">= 0.15"
|
36
|
+
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# This class handles parsing and running the command line interface for executing subscribers
|
2
|
+
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
require 'singleton'
|
6
|
+
require 'optparse'
|
7
|
+
require 'honest_pubsub/server'
|
8
|
+
|
9
|
+
module HonestPubsub
|
10
|
+
class CLI
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_accessor :pidfile, :subscribers
|
14
|
+
|
15
|
+
# Method to support parsing of arguments passed through the command line
|
16
|
+
def parse(args = ARGV)
|
17
|
+
optparse = OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: bundle exec start_subscribers [options]"
|
19
|
+
opts.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
|
20
|
+
@pidfile = arg
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-o", "--only [SUBSCRIBERS]", "comma separated name of subsriber classes that should be run") do |subscribers|
|
24
|
+
@subscribers = subscribers.split(/\,/)
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on '-r', '--require [PATH|DIR]', "Location of Rails application with workers or file to require" do |arg|
|
28
|
+
@require_path = arg
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on '-v', '--version', "Print version and exit" do |arg|
|
32
|
+
puts "HonestPubsub #{HonestPubsub::VERSION}"
|
33
|
+
abort
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
optparse.parse!(args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
load_environment
|
42
|
+
write_pidfile
|
43
|
+
load_subscribers
|
44
|
+
end
|
45
|
+
|
46
|
+
def require_path
|
47
|
+
@require_path || "."
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# @return [Array] returns array of subscriber classes that will be executed by the CLI
|
52
|
+
def subscriber_classes
|
53
|
+
if subscribers.present?
|
54
|
+
subscribers.map(&:constantize)
|
55
|
+
else
|
56
|
+
HonestPubsub::Server::ClientWorker.class_variable_get(:@@registered_subscribers)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_pid
|
61
|
+
return unless pidfile
|
62
|
+
File.delete(pidfile) if File.exist?(pidfile)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def load_environment
|
68
|
+
if require_path
|
69
|
+
raise ArgumentError, "#{require_path} does not exist" unless File.exist?(require_path)
|
70
|
+
end
|
71
|
+
|
72
|
+
if File.directory?(require_path)
|
73
|
+
require 'rails'
|
74
|
+
require File.expand_path("#{require_path}/config/environment.rb")
|
75
|
+
::Rails.application.eager_load!
|
76
|
+
else
|
77
|
+
require require_path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_pidfile
|
82
|
+
return unless pidfile
|
83
|
+
File.open(pidfile, 'w') do |f|
|
84
|
+
f.puts Process.pid
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def load_subscribers
|
89
|
+
HonestPubsub::Server::SubscriberServer.new(subscriber_classes).start
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module HonestPubsub
|
2
|
+
class Configuration
|
3
|
+
@@conf = nil
|
4
|
+
|
5
|
+
def self.configure_with(environment_name, yaml_file = nil)
|
6
|
+
@@yaml_file = yaml_file.nil? ? "config/pubsub.yml" : yaml_file
|
7
|
+
@@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
|
8
|
+
@@conf = @@all_conf[environment_name.to_sym]
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
self.configure_with(self.environment) if @@conf.nil?
|
14
|
+
@@conf
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.environment
|
18
|
+
val = ENV["RAILS_ENV"] || ENV["RACK_ENV"]
|
19
|
+
val = if val.present?
|
20
|
+
val.to_sym
|
21
|
+
else
|
22
|
+
if defined?(Rails)
|
23
|
+
Rails.env
|
24
|
+
else
|
25
|
+
raise "No environment can be found for configuration!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.application_name
|
31
|
+
@application_name ||= if defined?(Rails)
|
32
|
+
Rails.application.class.name.to_s.gsub("::Application", '')
|
33
|
+
else
|
34
|
+
'honest_pubsub'
|
35
|
+
end.downcase
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module HonestPubsub
|
2
|
+
class Context
|
3
|
+
# acceptable values
|
4
|
+
# employee - employee id that the context could have
|
5
|
+
# user_id - ender user id of call
|
6
|
+
# unique_id - unique identifier (could be request.uuid most of the time)
|
7
|
+
# orig_ip_address - ip address of originating requester
|
8
|
+
def initialize(params_hash = {})
|
9
|
+
@data = Hashie::Mash.new(params_hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
def user_id
|
13
|
+
@data.user_id
|
14
|
+
end
|
15
|
+
|
16
|
+
def employee_id
|
17
|
+
@data.employee_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_json
|
21
|
+
@data.as_json
|
22
|
+
end
|
23
|
+
|
24
|
+
def unique_id
|
25
|
+
@data.unique_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def originating_ip_address
|
29
|
+
@data.orig_ip_address
|
30
|
+
end
|
31
|
+
|
32
|
+
def serialize
|
33
|
+
@data.as_json
|
34
|
+
end
|
35
|
+
|
36
|
+
def application
|
37
|
+
@data.application
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.instance
|
41
|
+
Thread.current['pubsub_context'] || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.setup_context(attrs = {})
|
45
|
+
Thread.current['pubsub_context'] = self.new(attrs)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.clear!
|
49
|
+
Thread.current['pubsub_context'] = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# USE AS INFREQUENTLY AS POSSIBLE. Only use it if you absolutely must
|
53
|
+
# as really, we want context in the object, and not the kitchen sink,
|
54
|
+
# which is what this will turn out to be if we use regularly use this
|
55
|
+
# mechanism.
|
56
|
+
def method_missing(method_name, *arguments, &block)
|
57
|
+
if @data.key?(method_name)
|
58
|
+
@data[method_name]
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.from_json(data_hash)
|
66
|
+
::HonestPubsub::Context.new(data_hash)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
# Internal database logger that saves the publish message before it is send
|
3
|
+
# and also acts as a sink for messages.
|
4
|
+
module HonestPubsub
|
5
|
+
class DbLogger
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@config = Configuration.configuration[:mongo]
|
9
|
+
if @config.present?
|
10
|
+
@client = Mongo::MongoClient.new(@config[:host], @config[:port])
|
11
|
+
else
|
12
|
+
@client = Mongo::MongoClient.new
|
13
|
+
end
|
14
|
+
|
15
|
+
@db = @client.db("logger")
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_subscribe
|
20
|
+
subscriber = ::HonestPubsub::Subscriber.new("")
|
21
|
+
subscriber.start("logger") do |info, properties, contents|
|
22
|
+
# Write to mongo the contents and the routing_key, with the routing_key being indexed
|
23
|
+
routing_key = info[:routing_key]
|
24
|
+
chunks = routing_key.split(".")
|
25
|
+
# Logger will split the data into collections based upon the first parameter of the routing_key (initial roll out will only use 'honest')
|
26
|
+
collection_name = chunks[1] || chunks[0]
|
27
|
+
collection = @db.collection(collection_name)
|
28
|
+
# no need for safe writes. need to write to a file log as well.
|
29
|
+
collection.insert({routing_key: routing_key, cts: contents, ts: properties[:timestamp]})
|
30
|
+
# make sure that we always index these two items in mongo
|
31
|
+
# it's a small price of a call, so no big deal
|
32
|
+
collection.ensure_index( {routing_key:1, ts:1} )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def log_publish(routing_key, message)
|
37
|
+
collection_name = "publisher"
|
38
|
+
collection = @db.collection(collection_name)
|
39
|
+
|
40
|
+
# no need for safe writes. need to write to a file log as well.
|
41
|
+
collection.insert({routing_key: routing_key, cts: message[:payload], ts: message[:ts], pub: message[:pub]})
|
42
|
+
# make sure that we always index these two items in mongo
|
43
|
+
# it's a small price of a call, so no big deal
|
44
|
+
collection.ensure_index( {routing_key:1, ts:1} )
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def teardown
|
49
|
+
@subscriber.teardown
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Internal log class that is used to log messages before sending, after receiving, and failure to send
|
2
|
+
module HonestPubsub
|
3
|
+
class Logger
|
4
|
+
def initialize(enable_publish_logging = true)
|
5
|
+
@enabled = HonestPubsub::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(HonestPubsub.logger)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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
|
+
# HonestPubsub.logger = Rails.logger for example
|
9
|
+
|
10
|
+
module HonestPubsub
|
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 = HonestPubsub::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
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
require "hashie"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module HonestPubsub
|
6
|
+
class Message
|
7
|
+
@@message_version = "1"
|
8
|
+
attr_reader :ts
|
9
|
+
attr_reader :pub
|
10
|
+
attr_reader :version
|
11
|
+
attr_reader :payload
|
12
|
+
attr_reader :context
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
# Context object should be passed into the call.
|
18
|
+
def serialize(context, routing_key, payload)
|
19
|
+
@ts = Time.now.to_i
|
20
|
+
@pub = "#{routing_key}:#{Socket.gethostname()}:#{Process::pid}"
|
21
|
+
@version = @@message_version
|
22
|
+
@payload = payload
|
23
|
+
@context = context
|
24
|
+
to_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse(envelope)
|
28
|
+
contents = Hashie::Mash.new(::JSON.parse(envelope))
|
29
|
+
@ts = contents[:ts]
|
30
|
+
@pub = contents[:pub]
|
31
|
+
# Version used for messaging can get updated if we need to add extra header information, and need
|
32
|
+
# to move each section of the library separately.
|
33
|
+
@version = contents[:v]
|
34
|
+
@payload = contents[:payload]
|
35
|
+
@context = ::HonestPubsub::Context.from_json(contents[:context])
|
36
|
+
to_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
::Hashie::Mash.new({
|
41
|
+
ts: @ts,
|
42
|
+
pub: @pub,
|
43
|
+
v: @@message_version,
|
44
|
+
payload: @payload,
|
45
|
+
context: @context.as_json
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|