eventhub-processor2 1.0.0
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/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker/Dockerfile +6 -0
- data/docker/README.md +14 -0
- data/docker/definitions.json +87 -0
- data/docker/rabbitmq.config +16 -0
- data/eventhub-processor2.gemspec +34 -0
- data/example/README.md +19 -0
- data/example/config/example.json +19 -0
- data/example/crasher.rb +57 -0
- data/example/example.rb +25 -0
- data/example/publisher.rb +95 -0
- data/lib/eventhub/actor_heartbeat.rb +121 -0
- data/lib/eventhub/actor_listener.rb +161 -0
- data/lib/eventhub/actor_watchdog.rb +41 -0
- data/lib/eventhub/configuration.rb +114 -0
- data/lib/eventhub/constant.rb +35 -0
- data/lib/eventhub/consumer.rb +9 -0
- data/lib/eventhub/hash_extensions.rb +49 -0
- data/lib/eventhub/helper.rb +55 -0
- data/lib/eventhub/logger.rb +14 -0
- data/lib/eventhub/message.rb +158 -0
- data/lib/eventhub/processor2.rb +121 -0
- data/lib/eventhub/statistics.rb +53 -0
- data/lib/eventhub/version.rb +3 -0
- data/lib/eventhub.rb +25 -0
- metadata +190 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Heartbeat class
|
4
|
+
class ActorHeartbeat
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize(processor_instance)
|
10
|
+
@processor_instance = processor_instance
|
11
|
+
async.start
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
EventHub.logger.info('Heartbeat is starting...')
|
16
|
+
|
17
|
+
every(60) { EventHub.logger.info("Running actors: #{Celluloid::Actor.all.size}: #{Celluloid::Actor.all.map{ |a| a.class }.join(', ') }") }
|
18
|
+
|
19
|
+
publish(heartbeat(action: 'started'))
|
20
|
+
loop do
|
21
|
+
sleep Configuration.processor[:heartbeat_cycle_in_s]
|
22
|
+
EventHub.logger.info('Running heartbeat...')
|
23
|
+
publish(heartbeat)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def cleanup
|
28
|
+
EventHub.logger.info('Heartbeat is cleanig up...')
|
29
|
+
publish(heartbeat(action: 'stopped'))
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def publish(message)
|
35
|
+
connection = Bunny.new(bunny_connection_properties)
|
36
|
+
connection.start
|
37
|
+
channel = connection.create_channel
|
38
|
+
channel.confirm_select
|
39
|
+
exchange = channel.direct(EventHub::EH_X_INBOUND, durable: true)
|
40
|
+
exchange.publish(message, persistent: true)
|
41
|
+
success = channel.wait_for_confirms
|
42
|
+
|
43
|
+
unless success
|
44
|
+
raise 'Published heartbeat message has '\
|
45
|
+
'not been confirmed by the server'
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
connection.close if connection
|
49
|
+
end
|
50
|
+
|
51
|
+
def heartbeat(args = { action: 'running' })
|
52
|
+
message = EventHub::Message.new
|
53
|
+
message.origin_module_id = EventHub::Configuration.name
|
54
|
+
message.origin_type = 'processor'
|
55
|
+
message.origin_site_id = 'global'
|
56
|
+
|
57
|
+
message.process_name = 'event_hub.heartbeat'
|
58
|
+
|
59
|
+
now = Time.now
|
60
|
+
|
61
|
+
# message structure needs more changes
|
62
|
+
message.body = {
|
63
|
+
version: @processor_instance.send(:version),
|
64
|
+
action: args[:action],
|
65
|
+
pid: Process.pid,
|
66
|
+
process_name: 'event_hub.heartbeat',
|
67
|
+
heartbeat: {
|
68
|
+
started: now_stamp(started_at),
|
69
|
+
stamp_last_beat: now_stamp(now),
|
70
|
+
uptime_in_ms: (now - started_at) * 1000,
|
71
|
+
heartbeat_cycle_in_ms: Configuration.processor[:heartbeat_cycle_in_s] * 1000,
|
72
|
+
queues_consuming_from: EventHub::Configuration.processor[:listener_queues],
|
73
|
+
queues_publishing_to: [EventHub::EH_X_INBOUND], # needs more dynamic in the future
|
74
|
+
host: Socket.gethostname,
|
75
|
+
addresses: addresses,
|
76
|
+
messages: messages_statistics
|
77
|
+
}
|
78
|
+
}
|
79
|
+
message.to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
def started_at
|
83
|
+
@processor_instance.started_at
|
84
|
+
end
|
85
|
+
|
86
|
+
def statistics
|
87
|
+
@processor_instance.statistics
|
88
|
+
end
|
89
|
+
|
90
|
+
def addresses
|
91
|
+
interfaces = Socket.getifaddrs.select do |interface|
|
92
|
+
!interface.addr.ipv4_loopback? && !interface.addr.ipv6_loopback?
|
93
|
+
end
|
94
|
+
|
95
|
+
interfaces.map do |interface|
|
96
|
+
begin
|
97
|
+
{
|
98
|
+
interface: interface.name,
|
99
|
+
host_name: Socket.gethostname,
|
100
|
+
ip_address: interface.addr.ip_address
|
101
|
+
}
|
102
|
+
rescue
|
103
|
+
nil # will be ignored
|
104
|
+
end
|
105
|
+
end.compact
|
106
|
+
end
|
107
|
+
|
108
|
+
def messages_statistics
|
109
|
+
{
|
110
|
+
total: statistics.messages_total,
|
111
|
+
successful: statistics.messages_successful,
|
112
|
+
unsuccessful: statistics.messages_unsuccessful,
|
113
|
+
average_size: statistics.messages_average_size,
|
114
|
+
average_process_time_in_ms:
|
115
|
+
statistics.messages_average_process_time * 1000,
|
116
|
+
total_process_time_in_ms:
|
117
|
+
statistics.messages_total_process_time * 1000
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Listner Class
|
4
|
+
class ActorListener
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize(processor_instance)
|
10
|
+
@actor_watchdog = ActorWatchdog.new_link
|
11
|
+
@connections= {}
|
12
|
+
@processor_instance = processor_instance
|
13
|
+
start
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
EventHub.logger.info('Listener is starting...')
|
18
|
+
EventHub::Configuration.processor[:listener_queues].each_with_index do |queue_name, index|
|
19
|
+
async.listen(queue_name: queue_name, index: index)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def restart
|
24
|
+
raise 'Listener is restarting...'
|
25
|
+
end
|
26
|
+
|
27
|
+
def listen(args = {})
|
28
|
+
with_listen(args) do |connection, channel, consumer, queue, queue_name|
|
29
|
+
EventHub.logger.info("Listening to queue [#{queue_name}]")
|
30
|
+
consumer.on_delivery do |delivery_info, metadata, payload|
|
31
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]"\
|
32
|
+
' delivery')
|
33
|
+
|
34
|
+
# EventHub::logger.debug("delivery_info: #{delivery_info.inspect}")
|
35
|
+
# EventHub::logger.debug("metadata: #{metadata.inspect}")
|
36
|
+
|
37
|
+
@processor_instance.statistics.measure(payload.size) do
|
38
|
+
handle_payload(payload: payload,
|
39
|
+
connection: connection,
|
40
|
+
queue_name: queue_name,
|
41
|
+
content_type: metadata[:content_type],
|
42
|
+
priority: metadata[:priority],
|
43
|
+
delivery_tag: delivery_info.delivery_tag
|
44
|
+
)
|
45
|
+
channel.acknowledge(delivery_info.delivery_tag, false)
|
46
|
+
end
|
47
|
+
|
48
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]"\
|
49
|
+
' acknowledged')
|
50
|
+
end
|
51
|
+
queue.subscribe_with(consumer, block: false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_listen(args = {}, &block)
|
56
|
+
connection = Bunny.new(bunny_connection_properties)
|
57
|
+
connection.start
|
58
|
+
queue_name = args[:queue_name]
|
59
|
+
@connections[queue_name] = connection
|
60
|
+
channel = connection.create_channel
|
61
|
+
channel.prefetch(1)
|
62
|
+
queue = channel.queue(queue_name, durable: true)
|
63
|
+
consumer = EventHub::Consumer.new(channel,
|
64
|
+
queue,
|
65
|
+
EventHub::Configuration.name +
|
66
|
+
'-' +
|
67
|
+
args[:index].to_s,
|
68
|
+
false)
|
69
|
+
yield connection, channel, consumer, queue, queue_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_payload(args = {})
|
73
|
+
response_messages = []
|
74
|
+
connection = args[:connection]
|
75
|
+
|
76
|
+
# convert to EventHub message
|
77
|
+
message = EventHub::Message.from_json(args[:payload])
|
78
|
+
|
79
|
+
# append to execution history
|
80
|
+
message.append_to_execution_history(EventHub::Configuration.name)
|
81
|
+
|
82
|
+
# return invalid messages to dispatcher
|
83
|
+
if message.invalid?
|
84
|
+
response_messages << message
|
85
|
+
EventHub.logger.info("-> #{message.to_s} => return invalid to dispatcher")
|
86
|
+
else
|
87
|
+
begin
|
88
|
+
response_messages = @processor_instance.send(:handle_message,
|
89
|
+
message,
|
90
|
+
pass_arguments(args))
|
91
|
+
rescue => exception
|
92
|
+
# this catches unexpected exceptions in handle message method
|
93
|
+
# deadletter the message via dispatcher
|
94
|
+
message.status_code = EventHub::STATUS_DEADLETTER
|
95
|
+
message.status_message = exception
|
96
|
+
EventHub.logger.info("-> #{message.to_s} => return exception to diaptcher")
|
97
|
+
response_messages << message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Array(response_messages).each do |message|
|
102
|
+
publish(message: message.to_json, connection: connection)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def pass_arguments(args = {})
|
107
|
+
keys_to_pass = [:queue_name, :content_type, :priority, :delivery_tag]
|
108
|
+
args.select{ |key| keys_to_pass.include?(key) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def publish(args = {})
|
112
|
+
with_publish(args) do |connection, exchange_name, message|
|
113
|
+
begin
|
114
|
+
channel = connection.create_channel
|
115
|
+
channel.confirm_select
|
116
|
+
exchange = channel.direct(exchange_name, durable: true)
|
117
|
+
exchange.publish(message, persistent: true)
|
118
|
+
|
119
|
+
success = channel.wait_for_confirms
|
120
|
+
|
121
|
+
unless success
|
122
|
+
raise 'Published message from Listener actor '\
|
123
|
+
'has not been confirmed by the server'
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
channel.close if channel
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def with_publish(args = {}, &block)
|
133
|
+
message = args[:message]
|
134
|
+
return if message.nil?
|
135
|
+
|
136
|
+
need_to_close = false
|
137
|
+
connection = args[:connection]
|
138
|
+
if connection.nil?
|
139
|
+
connection = Bunny.new(bunny_connection_properties)
|
140
|
+
connection.start
|
141
|
+
need_to_close = true
|
142
|
+
end
|
143
|
+
|
144
|
+
exchange_name = args[:exchange_name] || EH_X_INBOUND
|
145
|
+
|
146
|
+
yield connection, exchange_name, message
|
147
|
+
ensure
|
148
|
+
connection.close if connection && need_to_close
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def cleanup
|
153
|
+
EventHub.logger.info('Listener is cleanig up...')
|
154
|
+
# close all open connections
|
155
|
+
@connections.values.each do |connection|
|
156
|
+
connection.close if connection
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Watchdog class
|
4
|
+
class ActorWatchdog
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
async.start
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
loop do
|
15
|
+
EventHub.logger.info('Running watchdog...')
|
16
|
+
watch
|
17
|
+
sleep Configuration.processor[:watchdog_cycle_in_s]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cleanup
|
22
|
+
EventHub.logger.info('Watchdog is cleanig up...')
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def watch
|
28
|
+
connection = Bunny.new(bunny_connection_properties)
|
29
|
+
connection.start
|
30
|
+
|
31
|
+
EventHub::Configuration.processor[:listener_queues].each do |queue_name|
|
32
|
+
unless connection.queue_exists?(queue_name)
|
33
|
+
EventHub.logger.warn("Queue [#{queue_name}] is missing")
|
34
|
+
raise "Queue [#{queue_name}] is missing"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
ensure
|
38
|
+
connection.close if connection
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Configuraiton module
|
4
|
+
module Configuration
|
5
|
+
# it's a singleton (don't allow to instantiate this class)
|
6
|
+
extend self
|
7
|
+
|
8
|
+
attr_reader :name # name of processor
|
9
|
+
attr_reader :environment # environment the processor is running
|
10
|
+
attr_reader :detached # run processor run as a daemon
|
11
|
+
attr_reader :config_file # name of configuration file
|
12
|
+
attr_reader :config_data # data from configuration file
|
13
|
+
|
14
|
+
@name = 'undefined'
|
15
|
+
@environment = 'development'
|
16
|
+
@detached = false
|
17
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
18
|
+
@config_data = {}
|
19
|
+
|
20
|
+
# set name of processor
|
21
|
+
def name=(value)
|
22
|
+
@name = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset
|
26
|
+
@name = 'undefined'
|
27
|
+
@environment = 'development'
|
28
|
+
@detached = false
|
29
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
30
|
+
@config_data = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# parse options from argument list
|
34
|
+
def parse_options(argv = ARGV)
|
35
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
36
|
+
|
37
|
+
OptionParser.new do |opts|
|
38
|
+
note = 'Define environment'
|
39
|
+
opts.on('-e', '--environment ENVIRONMENT', note) do |environment|
|
40
|
+
@environment = environment
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-d', '--detached', 'Run processor detached as a daemon') do
|
44
|
+
@detached = true
|
45
|
+
end
|
46
|
+
|
47
|
+
note = 'Define configuration file'
|
48
|
+
opts.on('-c', '--config CONFIG', note) do |config|
|
49
|
+
@config_file = config
|
50
|
+
end
|
51
|
+
end.parse!(argv)
|
52
|
+
|
53
|
+
true
|
54
|
+
rescue OptionParser::InvalidOption => e
|
55
|
+
EventHub.logger.warn("Argument Parsing: #{e}")
|
56
|
+
false
|
57
|
+
rescue OptionParser::MissingArgument => e
|
58
|
+
EventHub.logger.warn("Argument Parsing: #{e}")
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
# load configuration from file
|
63
|
+
def load!(args = {})
|
64
|
+
# for better rspec testing
|
65
|
+
@config_file = args[:config_file] if args[:config_file]
|
66
|
+
@environment = args[:environment] if args[:environment]
|
67
|
+
|
68
|
+
new_data = {}
|
69
|
+
begin
|
70
|
+
new_data = JSON.parse(File.read(@config_file), symbolize_names: true)
|
71
|
+
rescue => e
|
72
|
+
EventHub.logger.warn("Exception while loading configuration file: #{e}")
|
73
|
+
EventHub.logger.info('Using default configuration values')
|
74
|
+
end
|
75
|
+
|
76
|
+
deep_merge!(@config_data, default_configuration)
|
77
|
+
new_data = new_data[@environment.to_sym]
|
78
|
+
deep_merge!(@config_data, new_data)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Deep merging of hashes
|
82
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
83
|
+
def deep_merge!(target, data)
|
84
|
+
return if data.nil?
|
85
|
+
merger = proc do |_, v1, v2|
|
86
|
+
v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2
|
87
|
+
end
|
88
|
+
target.merge! data, &merger
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_missing(name, *_args, &_block)
|
92
|
+
@config_data[name.to_sym] ||
|
93
|
+
fail(NoMethodError, "unknown configuration [#{name}]", caller)
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_configuration
|
97
|
+
{
|
98
|
+
server: {
|
99
|
+
user: 'guest',
|
100
|
+
password: 'guest',
|
101
|
+
host: 'localhost',
|
102
|
+
vhost: 'event_hub',
|
103
|
+
port: 5672,
|
104
|
+
tls: false
|
105
|
+
},
|
106
|
+
processor: {
|
107
|
+
heartbeat_cycle_in_s: 300,
|
108
|
+
watchdog_cycle_in_s: 15,
|
109
|
+
listener_queues: [@name]
|
110
|
+
}
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EventHub
|
2
|
+
EH_X_INBOUND = 'event_hub.inbound'
|
3
|
+
|
4
|
+
STATUS_INITIAL = 0 # To be set when dispatcher needs to dispatch to first process step.
|
5
|
+
|
6
|
+
STATUS_SUCCESS = 200 # To be set to indicate successful processed message. Dispatcher will routes message to the next step.
|
7
|
+
|
8
|
+
STATUS_RETRY = 300 # To be set to trigger retry cycle controlled by the dispatcher
|
9
|
+
STATUS_RETRY_PENDING = 301 # Set and used by the dispatcher only.
|
10
|
+
# Set before putting the message into a retry queue.
|
11
|
+
# Once message has been retried it will sent do the same step with status.code = STATUS_SUCCESS
|
12
|
+
|
13
|
+
STATUS_INVALID = 400 # To be set to indicate invalid message (not json, invalid Event Hub Message).
|
14
|
+
# Dispatcher will publish message to the invalid queue.
|
15
|
+
|
16
|
+
STATUS_DEADLETTER = 500 # To be set by dispatcher, processor or channel adapters to indicate
|
17
|
+
# that message needs to be dead-lettered. Rejected messages could miss the
|
18
|
+
# status.code = STATUS_DEADLETTER due to the RabbitMQ deadletter exchange mechanism.
|
19
|
+
|
20
|
+
STATUS_SCHEDULE = 600 # To be set to trigger scheduler based on schedule block, proceses next process step
|
21
|
+
STATUS_SCHEDULE_RETRY = 601 # To be set to trigger scheduler based on schedule block, retry actual process step
|
22
|
+
STATUS_SCHEDULE_PENDING = 602 # Set and used by the dispatcher only. Set before putting the scheduled message to the schedule queue.
|
23
|
+
|
24
|
+
STATUS_CODE_TRANSLATION = {
|
25
|
+
STATUS_INITIAL => 'STATUS_INITIAL',
|
26
|
+
STATUS_SUCCESS => 'STATUS_SUCCESS',
|
27
|
+
STATUS_RETRY => 'STATUS_RETRY',
|
28
|
+
STATUS_RETRY_PENDING => 'STATUS_RETRY_PENDING',
|
29
|
+
STATUS_INVALID => 'STATUS_INVALID',
|
30
|
+
STATUS_DEADLETTER => 'STATUS_DEADLETTER',
|
31
|
+
STATUS_SCHEDULE => 'STATUS_SCHEDULE',
|
32
|
+
STATUS_SCHEDULE_RETRY => 'STATUS_SCHEDULE_RETRY',
|
33
|
+
STATUS_SCHEDULE_PENDING =>'STATUS_SCHEDULE_PENDING',
|
34
|
+
}
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# HashExtensions module
|
2
|
+
module HashExtensions
|
3
|
+
# ClassMethods module
|
4
|
+
module ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
# InstanceMethods module
|
8
|
+
module InstanceMethods
|
9
|
+
# get value from provided key path
|
10
|
+
# e.g. hash.get(%w(event_hub plate.queue1 retry_s))
|
11
|
+
# "a" => { "b" => { "c" => { "value"}}}
|
12
|
+
def get(arg)
|
13
|
+
path = arg.is_a?(String) ? arg.split('.') : arg
|
14
|
+
path.inject(self, :[])
|
15
|
+
end
|
16
|
+
|
17
|
+
# set value from provided key path, e.h. hash.set('a.b.c','new value')
|
18
|
+
# if overwrite is false, value will be set if it was nil previously
|
19
|
+
def set(arg, value, overwrite = true)
|
20
|
+
*key_path, last = arg.is_a?(String) ? arg.split('.') : arg
|
21
|
+
if overwrite
|
22
|
+
key_path.inject(self) { |h, key| h.key?(key) ? h[key] : h[key] = {} } [last] = value
|
23
|
+
else
|
24
|
+
key_path.inject(self) { |h, key| h.key?(key) ? h[key] : h[key] = {} } [last] ||= value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# get all keys with path,
|
29
|
+
# { 'a' => 'v1', 'b' => { 'c' => 'v2'}}.all_keys_with_path => ['a','b.c']
|
30
|
+
def all_keys_with_path(parent = nil)
|
31
|
+
a = []
|
32
|
+
each do |k, v|
|
33
|
+
if v.is_a?(Hash)
|
34
|
+
a << v.all_keys_with_path([parent, k].compact.join('.'))
|
35
|
+
else
|
36
|
+
a << [parent, k].compact.join('.').to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
a.flatten
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.included(receiver)
|
44
|
+
receiver.extend ClassMethods
|
45
|
+
receiver.send :include, InstanceMethods
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
HashExtensions.included(Hash)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
# EventHub module
|
4
|
+
module EventHub
|
5
|
+
# Helper module
|
6
|
+
module Helper
|
7
|
+
# Extracts processor name from given class instance.
|
8
|
+
# Removes 'EventHub' module from name.
|
9
|
+
|
10
|
+
# Examples:
|
11
|
+
# EventHub::Namespace::Demo => namespace.demo
|
12
|
+
# EventHub::NameSpace::Demo => name_space.demo
|
13
|
+
# EventHub::NameSpace::DemoProcessor => name_space.demo_processor
|
14
|
+
# NameSpace::Demo => name_space.demo
|
15
|
+
def get_name_from_class(instance)
|
16
|
+
instance.class.to_s.split('::').map do |element|
|
17
|
+
next if element == 'EventHub'
|
18
|
+
element.split(/(?=[A-Z])/).join('_').downcase
|
19
|
+
end.compact.join('.')
|
20
|
+
end
|
21
|
+
|
22
|
+
def bunny_connection_properties
|
23
|
+
server = EventHub::Configuration.server
|
24
|
+
|
25
|
+
if Configuration.server[:tls]
|
26
|
+
{
|
27
|
+
user: server[:user],
|
28
|
+
password: server[:password],
|
29
|
+
host: server[:host],
|
30
|
+
vhost: server[:vhost],
|
31
|
+
port: server[:port],
|
32
|
+
tls: server[:tls],
|
33
|
+
logger: Logger.new('/dev/null'), # logs from Bunny not required
|
34
|
+
network_recovery_interval: 15
|
35
|
+
}
|
36
|
+
else
|
37
|
+
{
|
38
|
+
user: server[:user],
|
39
|
+
password: server[:password],
|
40
|
+
host: server[:host],
|
41
|
+
vhost: server[:vhost],
|
42
|
+
port: server[:port],
|
43
|
+
logger: Logger.new('/dev/null'), # logs from Bunny not required
|
44
|
+
network_recovery_interval: 15
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Formats stamp into UTC format
|
50
|
+
def now_stamp(now=nil)
|
51
|
+
now ||= Time.now
|
52
|
+
now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
def self.logger
|
4
|
+
unless @logger
|
5
|
+
@logger = ::EventHub::Components::MultiLogger.new
|
6
|
+
@logger.add_device(Logger.new(STDOUT))
|
7
|
+
@logger.add_device(
|
8
|
+
EventHub::Components::Logger.logstash(Configuration.name,
|
9
|
+
Configuration.environment)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
@logger
|
13
|
+
end
|
14
|
+
end
|