rabbitmq_client 0.0.0.pre → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.reek.yml +21 -0
- data/.travis.yml +12 -1
- data/README.md +129 -17
- data/bin/setup +4 -0
- data/lib/rabbitmq_client/callback.rb +47 -0
- data/lib/rabbitmq_client/exchange.rb +25 -0
- data/lib/rabbitmq_client/exchange_registry.rb +33 -0
- data/lib/rabbitmq_client/json_formatter.rb +29 -0
- data/lib/rabbitmq_client/json_log_subscriber.rb +90 -0
- data/lib/rabbitmq_client/lifecycle.rb +53 -0
- data/lib/rabbitmq_client/log_subscriber_base.rb +16 -0
- data/lib/rabbitmq_client/logger_builder.rb +35 -0
- data/lib/rabbitmq_client/message_publisher.rb +57 -0
- data/lib/rabbitmq_client/plain_log_subscriber.rb +64 -0
- data/lib/rabbitmq_client/plugin.rb +31 -0
- data/lib/rabbitmq_client/publisher.rb +79 -0
- data/lib/rabbitmq_client/tags_filter.rb +16 -0
- data/lib/rabbitmq_client/text_formatter.rb +42 -0
- data/lib/rabbitmq_client/version.rb +2 -1
- data/lib/rabbitmq_client.rb +99 -2
- data/rabbitmq_client.gemspec +1 -0
- data/script/travis.sh +2 -0
- data/spec/callback_spec.rb +31 -0
- data/spec/exchange_registry_spec.rb +32 -0
- data/spec/json_formatter_spec.rb +43 -0
- data/spec/json_log_subscriber_spec.rb +145 -0
- data/spec/lifecycle_spec.rb +78 -0
- data/spec/plain_log_subscriber_spec.rb +115 -0
- data/spec/plugin_spec.rb +12 -0
- data/spec/publisher_spec.rb +150 -0
- data/spec/rabbitmq_client_spec.rb +83 -0
- data/spec/support/dummy_rabbitmq_client_plugin.rb +13 -0
- data/spec/tags_filter_spec.rb +37 -0
- data/spec/text_formatter_spec.rb +45 -0
- metadata +57 -5
- data/Gemfile.lock +0 -156
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2b060865b600b3377cbb16799ecdc82fedbd874b8b9f158c58a57604925c7b8
|
4
|
+
data.tar.gz: 211d0b536583982f7248a188daab96c77ec2a37765f840bdf85b4dfb9bf27930
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ca283805742bb671e6f43818ba3c9ad4f2ad8587f708c1d41fd472ccfaefa6e50f8d615feffeb4699a7a5f1b8957ebe5eda770f77229cab60a8283c066640b5
|
7
|
+
data.tar.gz: 5431a7e2bfe7a6ef70ab1bdcdb9e79069178b1071f09a481c656c431d7e7174291efbf4861d6e4ee35fc7cfc6d0d3620e1fff7a188b5d83a6bc3c3765cbb2224
|
data/.gitignore
CHANGED
data/.reek.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
detectors:
|
3
|
+
LongParameterList:
|
4
|
+
max_params: 5
|
5
|
+
TooManyStatements:
|
6
|
+
enabled: true
|
7
|
+
max_statements: 10
|
8
|
+
UncommunicativeVariableName:
|
9
|
+
accept:
|
10
|
+
- e
|
11
|
+
Attribute:
|
12
|
+
exclude:
|
13
|
+
- "RabbitmqClient::Plugin#callback_block"
|
14
|
+
UtilityFunction:
|
15
|
+
exclude:
|
16
|
+
- 'RabbitmqClient::Publisher#notify'
|
17
|
+
- 'LogSubscriberBase#logger'
|
18
|
+
- 'RabbitmqClient::JsonLogSubscriber'
|
19
|
+
FeatureEnvy:
|
20
|
+
exclude:
|
21
|
+
- 'RabbitmqClient::PlainLogSubscriber'
|
data/.travis.yml
CHANGED
@@ -6,12 +6,23 @@ branches:
|
|
6
6
|
|
7
7
|
rvm:
|
8
8
|
- 2.6.4
|
9
|
+
|
9
10
|
cache: bundler
|
10
11
|
bundler_args: "--jobs=3 --retry=3 --without production"
|
12
|
+
|
11
13
|
matrix:
|
12
14
|
fast_finish: true
|
15
|
+
|
13
16
|
script:
|
14
17
|
- sh script/travis.sh
|
18
|
+
|
19
|
+
notifications:
|
20
|
+
email: false # default notification method
|
21
|
+
slack:
|
22
|
+
if: branch = master
|
23
|
+
rooms:
|
24
|
+
secure: S5yfvL5slwkKqkIkUSEz3jcdMUGcaZ6tSlAL3h59p+Mlj40ezvKPvG3ZsSYw170+BQK0sxouxm1BpIGZ/aPu3ou6cK28eaaHrtS/CIVDM/6exobruWFAoidkjyyXyolzXAn/htAqOt3S21mCLfYDafUL2LeAnsheGf+TfPdKoBVwXzjn14RBsxW9qMOZ9rOgwxAikBtKELZyEr9MhBnLiJCSWFG/+sCvdYfpmAPvIlobZS65/943jbmUDMt0gu/AaW3De/97CaYF0rcxiiJVw/foDCJ5mX2UbzfGearxPzyF6F0ZjxGKHBEDQ+mqOE+cSPEKnkCc+YsBT4RhiyCWJupoEiCKbk3jy06n+8sT/PKvv+wDy4CUtYnqSXxSTVrUTWyqItxVEXUjK/+H8A7YosTC3FtVfYyZlVnmur0VitOCF9SKVUi+RtIrExGu5lI/Jvy6gUw6J2+tjWcDIoUuJ6iAKe84cRf1DUY4um3/M8QFI1QH21s9DT2bBfi5ombKeB4GkVqF7r26qbt18cDrYJwdUCVdRlYsXVyocHbrsBydBicUoTdbLLJlFI1PY4EiXNVlgVQKpnWdsodCu73xnLKxazqWX77gucXkvfIAKcu3br/onIU8IRGSPWKl7Bxo64/sNym4ZHFUYUS+ODFXUwiyykQcnyc25YnlgmHSDSo=
|
25
|
+
|
15
26
|
deploy:
|
16
27
|
provider: pages
|
17
28
|
skip-cleanup: true
|
@@ -19,4 +30,4 @@ deploy:
|
|
19
30
|
keep-history: true
|
20
31
|
verbose: true
|
21
32
|
on:
|
22
|
-
branch: master
|
33
|
+
branch: master
|
data/README.md
CHANGED
@@ -1,15 +1,44 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
```sh
|
3
|
+
____ _ _ _ _ __ __ ___ ____ _ _ _
|
4
|
+
| _ \ __ _| |__ | |__ (_) |_| \/ |/ _ \ / ___| (_) ___ _ __ | |_
|
5
|
+
| |_) / _` | '_ \| '_ \| | __| |\/| | | | | | | | | |/ _ \ '_ \| __|
|
6
|
+
| _ < (_| | |_) | |_) | | |_| | | | |_| | | |___| | | __/ | | | |_
|
7
|
+
|_| \_\__,_|_.__/|_.__/|_|\__|_| |_|\__\_\ \____|_|_|\___|_| |_|\__|
|
8
|
+
|
9
|
+
```
|
10
|
+
|
11
|
+
[![Gem Version](https://badge.fury.io/rb/rabbitmq_client.svg)](https://badge.fury.io/rb/rabbitmq_client)
|
12
|
+
[![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT)
|
2
13
|
[![Build Status](https://travis-ci.com/wshihadeh/rabbitmq_client.svg?branch=master)](https://travis-ci.com/wshihadeh/rabbitmq_client)
|
3
14
|
[![Depfu](https://badges.depfu.com/badges/b7ffc2788d24431bf85864706c5f9fb2/count.svg)](https://depfu.com/github/wshihadeh/rabbitmq_client?project_id=9862)
|
4
|
-
[![Coverage](https://wshihadeh.github.io/rabbitmq_client/badges/coverage_badge_total.svg)](https://wshihadeh.github.io/rabbitmq_client/
|
5
|
-
[![RubyCritic](https://wshihadeh.github.io/rabbitmq_client/badges/rubycritic_badge_score.svg)](https://wshihadeh.github.io/rabbitmq_client/
|
15
|
+
[![Coverage](https://wshihadeh.github.io/rabbitmq_client/badges/coverage_badge_total.svg)](https://wshihadeh.github.io/rabbitmq_client/coverage_info/index.html)
|
16
|
+
[![RubyCritic](https://wshihadeh.github.io/rabbitmq_client/badges/rubycritic_badge_score.svg)](https://wshihadeh.github.io/rabbitmq_client/rubycritic/overview.html)
|
17
|
+
|
18
|
+
Simplifying RabbitMQ for Ruby apps!
|
19
|
+
|
20
|
+
RabbitMQ Client is a ruby client library for applications dealing with [RabbitMQ](https://www.rabbitmq.com/).
|
21
|
+
- It wraps common behaviors needed by publishers and subscribers in an easy and convenient API.
|
22
|
+
- It uses both `bunny` and `connection_pool` to manage RabbitMQ communications.
|
23
|
+
- It is extendable using plugins.
|
24
|
+
|
25
|
+
Why RabbitMQ Client? Why not `bunny` and `connection_pool` directly? Well, gems are just a clients. You still need to write a lot of code to manage proper subscribing and publishing of messages. You need to do error handling, passing request headers to RabbitMQ and maybe logging/instrumenting the message management process. Finally, you also need to consider how to deploy your app and how to start it.
|
26
|
+
|
27
|
+
With RabbitMQ Client by your side, all this becomes smooth and easy.
|
6
28
|
|
29
|
+
## Table of Contents
|
7
30
|
|
8
|
-
|
31
|
+
1. [Installation](#installation)
|
32
|
+
1. [Usage](#usage)
|
33
|
+
1. [Configurations](#usage-configuration)
|
34
|
+
1. [Publishing messages to RabbitMQ](#usage-publish-messages-to-rmq)
|
35
|
+
1. [Plugins](#plugins)
|
36
|
+
1. [Development and Testing](#development)
|
37
|
+
1. [Contributing](#contributing)
|
38
|
+
1. [License](#license)
|
9
39
|
|
10
|
-
TODO: Delete this and the text above, and describe your gem
|
11
40
|
|
12
|
-
## Installation
|
41
|
+
## <a name="installation"></a> Installation
|
13
42
|
|
14
43
|
Add this line to your application's Gemfile:
|
15
44
|
|
@@ -25,24 +54,107 @@ Or install it yourself as:
|
|
25
54
|
|
26
55
|
$ gem install rabbitmq_client
|
27
56
|
|
28
|
-
## Usage
|
57
|
+
## <a name="usage"></a> Usage
|
58
|
+
RabbitMQ Client can be used to publsih and subscribe RabbitMQ messages.
|
29
59
|
|
30
|
-
|
60
|
+
### <a name="usage-configuration"></a> Configurations
|
61
|
+
RabbitMQ Client support the following configurations
|
31
62
|
|
32
|
-
|
63
|
+
| Configuration | Description | Default Value |
|
64
|
+
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
|
65
|
+
| rabbitmq_url | RabbitMQ server URL | amqp://guest:guest@127.0.0.1:5672 |
|
66
|
+
| logger_configs.logs_format | RabbitmqClient logs format.<br>values can be either json or plain | plain |
|
67
|
+
| logger_configs.logs_level | RabbitmqClient logs level.<br>values can be one of :debug, :info, :error | info |
|
68
|
+
| logger_configs.logs_filename | Logs file name, if nil STOUT will be used | nil |
|
69
|
+
| logger_configs.logger | Logger object, if nil STOUT logger will created | nil |
|
70
|
+
| session_params.heartbeat_publisher | Heartbeat interval for publisher sessions.<br>0 means no heartbeat | 0 |
|
71
|
+
| session_params.session_pool | Number of sessions with rabbitmq | 1 |
|
72
|
+
| plugins | Array of used plugins | [] |
|
73
|
+
| global_store | Global Store used to store tags and headers: <br>[RequestHeaderMiddleware](https://github.com/fidor/request_headers_middleware)<br>[RequestStore](https://github.com/steveklabnik/request_store) | nil |
|
74
|
+
| whitelist | List of whitelisted headers | ['x-request-id'.to_sym] |
|
33
75
|
|
34
|
-
|
76
|
+
#### Full Configuration Example
|
35
77
|
|
36
|
-
|
78
|
+
```ruby
|
79
|
+
RabbitmqClient.configure do |config|
|
80
|
+
config.rabbitmq_url = "${rabbitmq_url}"
|
81
|
+
config.logger_configs = {
|
82
|
+
logs_format: 'plain',
|
83
|
+
logs_level: :info,
|
84
|
+
logs_filename: nil,
|
85
|
+
logger: nil
|
86
|
+
}
|
87
|
+
config.session_params = {
|
88
|
+
heartbeat_publisher: 0,
|
89
|
+
session_pool: 1
|
90
|
+
}
|
91
|
+
config.plugins = []
|
92
|
+
config.global_store = RequestHeaderMiddleware
|
93
|
+
config.whitelist = ['x-request-id'.to_sym]
|
94
|
+
end
|
95
|
+
```
|
37
96
|
|
38
|
-
|
97
|
+
#### Append plungins or whitelist items
|
39
98
|
|
40
|
-
|
99
|
+
```ruby
|
100
|
+
RabbitmqClient.plugins << MYRabbitmqClientPlugin
|
101
|
+
RabbitmqClient.whitelist << :white_listed_header_key
|
102
|
+
```
|
41
103
|
|
42
|
-
|
104
|
+
### <a name="usage-publish-messages-to-rmq"></a> Publishing messages to RabbitMQ
|
105
|
+
- Publish messages using RabbitmqClient singleton publisher
|
43
106
|
|
44
|
-
|
107
|
+
```ruby
|
108
|
+
# RabbitmqClient.add_exchange(exchange, type, options)
|
109
|
+
RabbitmqClient.add_exchange('default.rabbitmq_client', :topic, {})
|
110
|
+
# RabbitmqClient.publish(payload, options)
|
111
|
+
RabbitmqClient.publish({id: 10, name: 'rabbitmq_client'}, { exchange_name: 'default.rabbitmq_client' })
|
112
|
+
```
|
113
|
+
|
114
|
+
- Create a publisher and use it
|
45
115
|
|
46
|
-
|
116
|
+
```ruby
|
117
|
+
config = {
|
118
|
+
rabbitmq_url: val,
|
119
|
+
exchange_registry: val,
|
120
|
+
session_params: { heartbeat_publisher: val }
|
121
|
+
}
|
47
122
|
|
48
|
-
|
123
|
+
publisher = RabbitmqClient::Publisher.new(config)
|
124
|
+
publisher.publish(payload, options)
|
125
|
+
```
|
126
|
+
|
127
|
+
## <a name="plugins"></a> Plugins
|
128
|
+
RabbitmqClient plugins are classes that define a callbacks that can be executed before or after events that occuers during RabbitmqClient lifecycle.
|
129
|
+
Current supprted lifecycle events are:
|
130
|
+
- publish: occure when publisgin a massage is triggered.
|
131
|
+
|
132
|
+
Here is an example where we define a plugin to add some headers to message options before publishing the message.
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
class MQTestPlugin < RabbitmqClient::Plugin
|
136
|
+
callbacks do |lifecycle|
|
137
|
+
lifecycle.before(:publish) do |_message, options|
|
138
|
+
options[:headers] = { test_key: 'test'}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
RabbitmqClient.plugins << MQTestPlugin
|
143
|
+
```
|
144
|
+
## <a name="development"></a> Development and Testing
|
145
|
+
|
146
|
+
After checking out the repo:
|
147
|
+
* run `bin/setup` to install dependencies
|
148
|
+
* run `script/travis.sh` to run the tests.
|
149
|
+
|
150
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
151
|
+
|
152
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
153
|
+
|
154
|
+
## <a name="contributing"></a> Contributing
|
155
|
+
|
156
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/wshihadeh/rabbitmq_client.
|
157
|
+
|
158
|
+
## <a name="license"></a> License
|
159
|
+
|
160
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/bin/setup
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitmqClient
|
4
|
+
# Custom error thrown when an unsupported callback type is used
|
5
|
+
class InvalidCallback < RuntimeError
|
6
|
+
def initialize(name)
|
7
|
+
super("The Callback '#{name}' is an invalid callback and cannot be used")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Callback Object Store all plugins clallbacks
|
12
|
+
# Supported callback types are before and adter
|
13
|
+
class Callback
|
14
|
+
def initialize
|
15
|
+
@before = []
|
16
|
+
@after = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute(*args, &block)
|
20
|
+
execute_before_callbacks(*args)
|
21
|
+
result = block.call(*args)
|
22
|
+
execute_after_callbacks(*args)
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(type, &callback)
|
27
|
+
case type
|
28
|
+
when :before
|
29
|
+
@before << callback
|
30
|
+
when :after
|
31
|
+
@after << callback
|
32
|
+
else
|
33
|
+
raise InvalidCallback, "Invalid callback type: #{type}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def execute_before_callbacks(*args)
|
40
|
+
@before.each { |callback| callback.call(*args) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute_after_callbacks(*args)
|
44
|
+
@after.each { |callback| callback.call(*args) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitmqClient
|
4
|
+
# ExchangeRegistry is a store for all managed exchanges and their details
|
5
|
+
class Exchange
|
6
|
+
attr_reader :name, :type, :options
|
7
|
+
|
8
|
+
def initialize(name, type, options)
|
9
|
+
@name = name
|
10
|
+
@type = type
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(channel)
|
15
|
+
exhange_obj = Bunny::Exchange.new(channel, @type, @name,
|
16
|
+
@options)
|
17
|
+
ActiveSupport::Notifications.instrument(
|
18
|
+
'created_exhange.rabbitmq_client',
|
19
|
+
name: @name,
|
20
|
+
type: @type
|
21
|
+
)
|
22
|
+
exhange_obj
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'exchange'
|
4
|
+
|
5
|
+
module RabbitmqClient
|
6
|
+
# ExchangeRegistry is a store for all managed exchanges and their details
|
7
|
+
class ExchangeRegistry
|
8
|
+
# Custom Eroor thrown when trying to find unkown exchange
|
9
|
+
class ExchangeNotFound < StandardError
|
10
|
+
def initialize(name)
|
11
|
+
super("The Exchange '#{name}' cannot be found")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@exchanges = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(name, type, options = {})
|
20
|
+
@exchanges[name] = Exchange.new(name, type, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(name)
|
24
|
+
@exchanges.fetch(name) do
|
25
|
+
ActiveSupport::Notifications.instrument(
|
26
|
+
'exhange_not_found.rabbitmq_client',
|
27
|
+
name: name
|
28
|
+
)
|
29
|
+
raise ExchangeNotFound, name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module RabbitmqClient
|
6
|
+
# Formatter for json log messages
|
7
|
+
class JsonFormatter
|
8
|
+
TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%6N '
|
9
|
+
def initialize
|
10
|
+
@json = {}
|
11
|
+
@msg = ''
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(severity, timestamp, progname, msg)
|
15
|
+
@json = build_new_json(msg)
|
16
|
+
@json = @json.merge(progname: progname.to_s, level: severity,
|
17
|
+
timestamp: timestamp.strftime(TIME_FORMAT))
|
18
|
+
@json = @json.reject { |_key, value| value.to_s.empty? }
|
19
|
+
|
20
|
+
@json.to_json + "\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_new_json(msg)
|
24
|
+
@msg = msg
|
25
|
+
@json = @msg.is_a?(Hash) ? @msg : { message: @msg.strip }
|
26
|
+
@json.merge(TagsFilter.tags || {})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'log_subscriber_base'
|
4
|
+
|
5
|
+
module RabbitmqClient
|
6
|
+
# Manage RabbitmqClient plain text logs
|
7
|
+
class JsonLogSubscriber < LogSubscriberBase
|
8
|
+
def publisher_created(event)
|
9
|
+
debug(action: 'publisher_created',
|
10
|
+
message: 'The RabbitmqClient publisher is created',
|
11
|
+
publisher_configs: event.payload)
|
12
|
+
end
|
13
|
+
|
14
|
+
def network_error(event)
|
15
|
+
payload = event.payload
|
16
|
+
error({ action: 'network_error',
|
17
|
+
message: 'Failed to publish a message',
|
18
|
+
error_message: payload.fetch(:error).message }.merge(
|
19
|
+
process_payload(payload)
|
20
|
+
))
|
21
|
+
end
|
22
|
+
|
23
|
+
def overriding_configs(event)
|
24
|
+
debug(action: 'overriding_configs',
|
25
|
+
message: 'Overriding publisher configs',
|
26
|
+
publisher_configs: event.payload)
|
27
|
+
end
|
28
|
+
|
29
|
+
def publishing_message(event)
|
30
|
+
debug({ action: 'publishing_message',
|
31
|
+
message: 'Publishing a new message' }.merge(
|
32
|
+
process_payload(event.payload)
|
33
|
+
))
|
34
|
+
end
|
35
|
+
|
36
|
+
def published_message(event)
|
37
|
+
info({ action: 'published_message',
|
38
|
+
message: 'Published a message' }.merge(
|
39
|
+
process_payload(event.payload)
|
40
|
+
))
|
41
|
+
end
|
42
|
+
|
43
|
+
def confirming_message(event)
|
44
|
+
debug({ action: 'confirming_message',
|
45
|
+
message: 'Confirming a message' }.merge(
|
46
|
+
process_payload(event.payload)
|
47
|
+
))
|
48
|
+
end
|
49
|
+
|
50
|
+
def message_confirmed(event)
|
51
|
+
debug({ action: 'message_confirmed',
|
52
|
+
message: 'Confirmed a message' }.merge(
|
53
|
+
process_payload(event.payload)
|
54
|
+
))
|
55
|
+
end
|
56
|
+
|
57
|
+
def exhange_not_found(event)
|
58
|
+
error(action: 'exhange_not_found',
|
59
|
+
message: 'Exhange Not Found',
|
60
|
+
exchange_name: event.payload.fetch(:name))
|
61
|
+
end
|
62
|
+
|
63
|
+
def created_exhange(event)
|
64
|
+
debug(action: 'created_exhange',
|
65
|
+
message: 'Exhange is created successfuly',
|
66
|
+
exchange_name: event.payload.fetch(:name))
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
%w[info debug warn error fatal unknown].each do |level|
|
72
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
73
|
+
def #{level}(progname = nil, &block)
|
74
|
+
logger.#{level} rabbitmq_client_event(progname, &block) if logger
|
75
|
+
end
|
76
|
+
METHOD
|
77
|
+
end
|
78
|
+
|
79
|
+
def rabbitmq_client_event(event)
|
80
|
+
{ source: 'rabbitmq_client' }.merge(event)
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_payload(payload)
|
84
|
+
{
|
85
|
+
exchange_name: payload.fetch(:exchange, 'undefined'),
|
86
|
+
message_id: payload.fetch(:message_id, 'undefined')
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'callback'
|
4
|
+
|
5
|
+
module RabbitmqClient
|
6
|
+
# Lifecycle defines the rabbitmq_client lifecycle events,
|
7
|
+
# callbacks and manage the execution of these callbacks
|
8
|
+
class Lifecycle
|
9
|
+
EVENTS = {
|
10
|
+
publish: %i[message options]
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
attr_reader :callbacks
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@callbacks = EVENTS.keys.each_with_object({}) do |key, hash|
|
17
|
+
hash[key] = Callback.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def before(event, &block)
|
22
|
+
add(:before, event, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def after(event, &block)
|
26
|
+
add(:after, event, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_callbacks(event, *args, &block)
|
30
|
+
missing_callback(event) unless @callbacks.key?(event)
|
31
|
+
event_obj = EVENTS[event]
|
32
|
+
event_size = event_obj.size
|
33
|
+
|
34
|
+
unless event_size == args.size
|
35
|
+
raise ArgumentError, "Callback #{event} expects\
|
36
|
+
#{event_size} parameter(s): #{event_obj.join(', ')}"
|
37
|
+
end
|
38
|
+
|
39
|
+
@callbacks[event].execute(*args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def add(type, event, &block)
|
45
|
+
missing_callback(event) unless @callbacks.key?(event)
|
46
|
+
@callbacks[event].add(type, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def missing_callback(event)
|
50
|
+
raise InvalidCallback, "Unknown callback event: #{event}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitmqClient
|
4
|
+
# Log Subscriber base class
|
5
|
+
class LogSubscriberBase < ActiveSupport::LogSubscriber
|
6
|
+
class << self
|
7
|
+
def logger
|
8
|
+
@logger ||= RabbitmqClient.logger
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def logger
|
13
|
+
LogSubscriberBase.logger
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitmqClient
|
4
|
+
# ExchangeRegistry is a store for all managed exchanges and their details
|
5
|
+
class LoggerBuilder
|
6
|
+
def initialize(config)
|
7
|
+
@logger = config[:logger].clone
|
8
|
+
@format = config[:logs_format]
|
9
|
+
@level = config[:logs_level].to_sym
|
10
|
+
@filename = config[:logs_filename]
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_logger
|
14
|
+
@logger ||= ::Logger.new(@filename || STDOUT)
|
15
|
+
@logger.level = @level
|
16
|
+
@logger.formatter = create_logger_formatter
|
17
|
+
log_subscriber.attach_to(:rabbitmq_client)
|
18
|
+
@logger
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def create_logger_formatter
|
24
|
+
json? ? JsonFormatter.new : TextFormatter.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_subscriber
|
28
|
+
json? ? JsonLogSubscriber : PlainLogSubscriber
|
29
|
+
end
|
30
|
+
|
31
|
+
def json?
|
32
|
+
__method__.to_s == "#{@format}?"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitmqClient
|
4
|
+
# ExchangeRegistry is a store for all managed exchanges and their details
|
5
|
+
class MessagePublisher
|
6
|
+
# Custom error is thrown when rabbitmq do not confirm publishing an event
|
7
|
+
class ConfirmationFailed < StandardError
|
8
|
+
def initialize(exchange, nacked, unconfirmed)
|
9
|
+
msg = 'Message confirmation on the exchange ' \
|
10
|
+
"#{exchange} has failed (#{nacked}/#{unconfirmed})."
|
11
|
+
super(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(data, exchange, channel, options)
|
16
|
+
@data = data.to_json
|
17
|
+
@exchange = exchange
|
18
|
+
@channel = channel
|
19
|
+
@options = { headers: {} }.merge(options)
|
20
|
+
@options[:headers][:tags] = TagsFilter.tags
|
21
|
+
end
|
22
|
+
|
23
|
+
def publish
|
24
|
+
exchange = @exchange.create(@channel)
|
25
|
+
|
26
|
+
notify('publishing_message')
|
27
|
+
exchange.publish(@data, **@options)
|
28
|
+
notify('published_message')
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_for_confirms
|
32
|
+
notify('confirming_message')
|
33
|
+
if @channel.wait_for_confirms
|
34
|
+
notify('message_confirmed')
|
35
|
+
return
|
36
|
+
end
|
37
|
+
raise ConfirmationFailed.new(@exchange.name, @channel.nacked_set,
|
38
|
+
@channel.unconfirmed_set)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def notify(event)
|
44
|
+
ActiveSupport::Notifications.instrument(
|
45
|
+
"#{event}.rabbitmq_client",
|
46
|
+
message_payload
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def message_payload
|
51
|
+
{
|
52
|
+
exchange: @exchange.name,
|
53
|
+
message_id: @options[:message_id]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|