logstash-input-bunny 0.1.11 → 0.1.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a3d026a10ffe2b4a072e4a5584ef2bd1003db28
4
- data.tar.gz: fd96ffef182ced185b47afffaf8a6693272c04db
3
+ metadata.gz: b5c4dbbe89ddbb17aeacca3faecf3647eecced04
4
+ data.tar.gz: 645754ffc7b545fd97188364d5e556e1fd0474c1
5
5
  SHA512:
6
- metadata.gz: 478a7b649be84c2b471638313432e8af954d2901aaa748c4c30c4f3324423d39d4fc4d5ff0bbf432723061e9a2eb97e1560bedfbca32c0fc23587e8a1202201c
7
- data.tar.gz: 0510b6343762f258e8ad974d1d560d378fe829da547810df178b5c24595073b8f4082ab2606a2c588a62cd79b517e2a47c6463f03d2115ed6fb07a8ea01cdbe8
6
+ metadata.gz: f64cf8c7e46897dee3c0245452045deb1589f1bd7d08e9e1f21ab8c78848177ccb6876d67a32cf99c342f787f04b66fa7e8bc71f275392285d27fd6a7e454bc4
7
+ data.tar.gz: b2371829cd2ea89ea4d5e8c9e51f40414bf1cac3dca0ed7bcaf96b658a29715e35b51ced95b581fd2a74b2b9d72854c53cac3804378408309441b5eca8437740
@@ -13,222 +13,213 @@ require "logstash/inputs/rabbitmq/bunny"
13
13
  #
14
14
  # * Bunny - <https://github.com/ruby-amqp/bunny>
15
15
  class LogStash::Inputs::Bunny < LogStash::Inputs::Threadable
16
-
17
16
  config_name "bunny"
18
17
 
19
18
  #
20
19
  # Connection
21
20
  #
22
21
 
23
- # RabbitMQ server address
24
- config :host, :validate => :string, :required => true
25
-
26
- # RabbitMQ port to connect on
27
- config :port, :validate => :number, :default => 5672
28
-
29
- # RabbitMQ username
30
- config :user, :validate => :string, :default => "guest"
31
-
32
- # RabbitMQ password
33
- config :password, :validate => :password, :default => "guest"
22
+ config :host, validate: :string, required: true
23
+ config :port, validate: :number, default: 5672
24
+ config :user, validate: :string, default: "guest"
25
+ config :password, validate: :password, default: "guest"
26
+ config :vhost, validate: :string, default: "/"
34
27
 
35
- # The vhost to use. If you don't know what this is, leave the default.
36
- config :vhost, :validate => :string, :default => "/"
28
+ config :ssl, validate: :boolean, :default => false
29
+ config :ssl_cert, validate: :string
30
+ config :ssl_key, validate: :string
31
+ config :ssl_ca_cert, validate: :string
32
+ config :verify_peer, validate: :boolean, default: false
37
33
 
38
- # Enable or disable SSL
39
- config :ssl, :validate => :boolean, :default => false
40
- config :ssl_cert, :validate => :string
41
- config :ssl_key, :validate => :string
42
- config :ssl_ca_cert, :validate => :string
43
-
44
- # Validate SSL certificate
45
- config :verify_ssl, :validate => :boolean, :default => false
46
- config :verify_peer, :validate => :boolean, :default => false
34
+ #
35
+ # Queue & Consumer
36
+ #
47
37
 
48
- # Enable or disable logging
49
- config :debug, :validate => :boolean, :default => false, :deprecated => "Use the logstash --debug flag for this instead."
38
+ #
39
+ # The Bunny plugin's approach differs from Logstash's RabbitMQ plugin in that
40
+ # we require an exchange so that we can create & bind multiple queues based
41
+ # on routing keys.
42
+ #
43
+ # In addition, rather than accepting a single string for the queue, we now
44
+ # accept a hash of queue > routing_key pairs.
45
+ #
50
46
 
47
+ config :exchange, validate: :string, required: true
48
+ config :queues, validate: :hash, required: true
51
49
 
50
+ config :prefetch, validate: :number, default: 20
51
+ config :ack, validate: :boolean, default: true
52
52
 
53
53
  #
54
- # Queue & Consumer
54
+ # Queue Durability / Persistence
55
55
  #
56
56
 
57
- # The name of the queue Logstash will consume events from.
58
- config :queue, :validate => :string, :default => ""
57
+ # Create durable queues so that they survive broker restarts
58
+ config :durable, validate: :boolean, default: false
59
59
 
60
- # Is this queue durable? (aka; Should it survive a broker restart?)
61
- config :durable, :validate => :boolean, :default => false
62
-
63
- # Should the queue be deleted on the broker when the last consumer
64
- # disconnects? Set this option to `false` if you want the queue to remain
65
- # on the broker, queueing up messages until a consumer comes along to
66
- # consume them.
67
- config :auto_delete, :validate => :boolean, :default => false
60
+ # Delete queues once the last consumer disconnects.
61
+ config :auto_delete, validate: :boolean, default: false
68
62
 
69
63
  # Is the queue exclusive? Exclusive queues can only be used by the connection
70
64
  # that declared them and will be deleted when it is closed (e.g. due to a Logstash
71
65
  # restart).
72
- config :exclusive, :validate => :boolean, :default => false
73
-
74
- # Extra queue arguments as an array.
75
- # To make a RabbitMQ queue mirrored, use: `{"x-ha-policy" => "all"}`
76
- config :arguments, :validate => :array, :default => {}
66
+ config :exclusive, validate: :boolean, default: false
77
67
 
78
- # Prefetch count. Number of messages to prefetch
79
- config :prefetch_count, :validate => :number, :default => 256
68
+ # Passive queue creation? Useful for checking queue existance without modifying server state
69
+ config :passive, validate: :boolean, default: false
80
70
 
81
- # Enable message acknowledgement
82
- config :ack, :validate => :boolean, :default => true
71
+ #
72
+ # Initialization
73
+ #
83
74
 
84
- # Passive queue creation? Useful for checking queue existance without modifying server state
85
- config :passive, :validate => :boolean, :default => false
75
+ def initialize(params)
76
+ params["codec"] = "json" if !params["codec"]
77
+ super
78
+ end
86
79
 
80
+ def input_tag
81
+ "#{@vhost}/#{@exchange}"
82
+ end
87
83
 
84
+ def message(severity, message, data={})
85
+ message = "Bunny[#{input_tag}] #{message}"
86
+ @logger.send(severity, message, data)
87
+ end
88
88
 
89
- #
90
- # (Optional) Exchange binding
91
- #
89
+ def register
90
+ require "bunny"
92
91
 
93
- # Optional.
94
- #
95
- # The name of the exchange to bind the queue to.
96
- config :exchange, :validate => :string
92
+ message(:info, "registering input", host: @host)
97
93
 
98
- # Optional.
99
- #
100
- # The routing key to use when binding a queue to the exchange.
101
- # This is only relevant for direct or topic exchanges.
102
- #
103
- # * Routing keys are ignored on fanout exchanges.
104
- # * Wildcards are not valid on direct exchanges.
105
- config :key, :validate => :string, :default => "logstash"
94
+ @vhost ||= Bunny::DEFAULT_HOST
95
+ @port ||= AMQ::Protocol::DEFAULT_PORT
106
96
 
97
+ @settings = {
98
+ vhost: @vhost,
99
+ host: @host,
100
+ port: @port,
101
+ user: @user,
102
+ pass: @password.value,
103
+ tls: @ssl,
104
+ automatically_recover: false
105
+ }
107
106
 
108
- def initialize(params)
109
- params["codec"] = "json" if !params["codec"]
107
+ if @ssl
108
+ @settings[:verify_peer] = @verify_peer
109
+ end
110
110
 
111
- super
111
+ if @ssl and @ssl_cert
112
+ @settings[:tls_cert] = @ssl_cert
113
+ @settings[:tls_key] = @ssl_key
114
+ @settings[:tls_ca_certificates] = [@ssl_ca_cert] if @ssl_ca_cert
115
+ end
112
116
  end
113
117
 
114
- include ::LogStash::Inputs::RabbitMQ::BunnyImpl
115
-
116
118
  def run(output_queue)
117
119
  @output_queue = output_queue
120
+ delay = Bunny::Session::DEFAULT_NETWORK_RECOVERY_INTERVAL * 2
118
121
 
119
122
  begin
120
123
  setup
121
124
  consume
122
- rescue Bunny::NetworkFailure, Bunny::ConnectionClosedError,
123
- Bunny::ConnectionLevelException, Bunny::TCPConnectionFailed => e
124
- n = Bunny::Session::DEFAULT_NETWORK_RECOVERY_INTERVAL * 2
125
-
126
- # Because we manually reconnect instead of letting Bunny
127
- # handle failures,
128
- # make sure we don't leave any consumer work pool
129
- # threads behind. MK.
130
- @ch.maybe_kill_consumer_work_pool!
131
- @logger.error("RabbitMQ[#{input_name}] connection error: #{e.message}. Will attempt to reconnect in #{n} seconds...")
132
-
133
- sleep n
134
- retry
135
-
136
- rescue OpenSSL::SSL::SSLError => e
137
- n = Bunny::Session::DEFAULT_NETWORK_RECOVERY_INTERVAL * 2
125
+ rescue Bunny::NetworkFailure, Bunny::ConnectionClosedError,
126
+ Bunny::ConnectionLevelException, Bunny::TCPConnectionFailed,
127
+ OpenSSL::SSL::SSLError => e
128
+
129
+ @consumers.each do |name, consumer|
130
+ error = "connection error: #{e.message}. Retrying in #{delay} seconds."
131
+ message(:error, error)
132
+ consumer.channel.maybe_kill_consumer_work_pool!
133
+ end
138
134
 
139
- @logger.error("RabbitMQ[#{input_name}] SSL connection error: #{e.message}. Will attempt to reconnect in #{n} seconds...")
140
- sleep n
135
+ sleep delay
141
136
  retry
142
137
 
143
138
  rescue LogStash::ShutdownSignal
144
- # ignore and quit
145
-
146
- rescue Exception => e
147
- @logger.error("RabbitMQ[#{input_name}] unhandled exception: #{e.inspect}")
148
- @logger.error(e.backtrace)
149
- end
150
- end
139
+ release
151
140
 
152
- def register
153
- super
141
+ rescue Exception => e
142
+ message(:error, "unhandled exception: #{e.inspect}", backtrace: e.backtrace)
154
143
 
155
- if @ssl and @ssl_cert
156
- @settings[:tls_cert] = @ssl_cert
157
- @settings[:tls_key] = @ssl_key
158
- @settings[:tls_ca_certificates] = [@ssl_ca_cert] if @ssl_ca_cert
159
- @settings[:verify_peer] = @verify_peer
160
144
  end
161
145
  end
162
146
 
163
147
  def teardown
164
- @consumer.cancel if @consumer
165
- @ch.close if @ch && @ch.open?
166
- @conn.close if @conn && @conn.open?
148
+ release
167
149
  finished
168
150
  end
169
151
 
170
- protected
171
-
172
- def consumer_id
173
- @consumer_id ||= "#{`hostname -s`.strip}-#{Thread.current.object_id}"
174
- end
175
-
176
- def input_name
177
- @exchange.nil? or @key.nil? ? queue : "#{@exchange}:#{@key}"
178
- end
179
-
180
152
  def setup
181
153
  return if terminating?
182
154
 
155
+ #
156
+ # With multiple threads, Logstash/Jruby/Bunny combo seems to fail ssl
157
+ # handshake on the first 1-2 connection attempts when restarting Logstash.
158
+ #
159
+ # optionally, look at : settings[:connect_timeout]
160
+ #
183
161
  begin
184
- attempts ||= 3
185
-
186
- @logger.debug("RabbitMQ[#{input_name}] connecting.")
187
- @conn = Bunny.new(@settings)
188
- @conn.start
162
+ connection_attempt ||= 1
163
+ message(:debug, "connecting")
164
+ @connection = Bunny.new(@settings)
165
+ @connection.start
189
166
  rescue OpenSSL::SSL::SSLError
190
- @logger.warn("SSL Handshake failed, retrying.")
191
- attempts -= 1
192
- retry unless attempts == 0
167
+ message(:warn, "ssl handshake failed, retrying",
168
+ attempt: connection_attempt)
169
+ connection_attempt += 1
170
+ retry unless connection_attempt > 3
193
171
  end
194
172
 
195
- @logger.info("RabbitMQ[#{input_name}] connected at #{@settings[:host]}")
196
- @ch = @conn.create_channel.tap do |ch|
197
- ch.prefetch(@prefetch_count)
198
- end
173
+ @consumers = {}
174
+ @queues.each do |queue_name, routing_key|
175
+ consumer = "#{queue_name}#{routing_key}"
176
+ message(:debug, "creating consumer #{consumer}")
177
+
178
+ channel = @connection.create_channel.tap do |channel|
179
+ channel.prefetch(@prefetch)
180
+ end
181
+
182
+ queue = channel.queue(@queue,
183
+ durable: @durable,
184
+ auto_delete: @auto_delete,
185
+ exclusive: @exclusive,
186
+ passive: @passive)
187
+
188
+ queue.bind(@exchange, routing_key: routing_key)
199
189
 
200
- @arguments_hash = Hash[*@arguments]
201
- @q = @ch.queue(@queue,
202
- :durable => @durable,
203
- :auto_delete => @auto_delete,
204
- :exclusive => @exclusive,
205
- :passive => @passive,
206
- :arguments => @arguments)
207
-
208
- # exchange binding is optional for the input
209
- if @exchange
210
- @q.bind(@exchange, :routing_key => @key)
190
+ @consumers[consumer] = Bunny::Consumer.
191
+ new(channel, queue, nil, !@ack, @exclusive)
211
192
  end
212
193
  end
213
194
 
214
- def consume
215
- @logger.info("Will consume events from queue #{@q.name} as #{consumer_id}")
195
+ def release
196
+ @consumers.each do |name, consumer|
197
+ consumer.cancel
198
+ consumer.channel.close if consumer.channel and consumer.channel.open?
199
+ end
200
+ @consumers = []
216
201
 
217
- no_ack = !@ack
218
- exclusive = @exclusive
202
+ @connection.close if @connection and @connection.open?
203
+ end
219
204
 
220
- @consumer = Bunny::Consumer.new(@ch, @q, consumer_id, no_ack, exclusive)
221
- @consumer.on_delivery do |delivery_info, properties, data|
222
- @codec.decode(data) do |event|
223
- decorate(event)
224
- @output_queue << event
205
+ def consume
206
+ @consumers.each do |name, consumer|
207
+ message(:info, "consuming events from #{name}")
208
+
209
+ consumer.on_delivery do |delivery_info, properties, data|
210
+ @codec.decode(data) do |event|
211
+ decorate(event)
212
+ event["bunny"] ||= {}
213
+ event["bunny"]["key"] = delivery_info.routing_key
214
+ event["bunny"]["queue"] = consumer.queue_name
215
+ @output_queue << event
216
+ end
217
+
218
+ consumer.channel.acknowledge(delivery_info.delivery_tag) if @ack
225
219
  end
226
220
 
227
- @ch.acknowledge(delivery_info.delivery_tag) if @ack
221
+ consumer.queue.subscribe_with(consumer, block: true)
228
222
  end
229
-
230
- @q.subscribe_with(@consumer, block: true)
231
223
  end
232
-
233
224
  end # class LogStash::Inputs::RabbitMQ
234
225
 
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-bunny'
4
- s.version = '0.1.11'
4
+ s.version = '0.1.12'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Pull events from a RabbitMQ exchange."
7
7
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Serafini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-07 00:00:00.000000000 Z
11
+ date: 2015-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core