logstash-input-bunny 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
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