logstash-input-jms 3.0.6-java → 3.1.0-java

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.
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/inputs/base"
3
3
  require "logstash/inputs/threadable"
4
+ require 'java'
4
5
  require "logstash/namespace"
5
6
 
6
7
  # Read events from a Jms Broker. Supports both Jms Queues and Topics.
@@ -34,6 +35,13 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
34
35
  config :include_header, :validate => :boolean, :default => true
35
36
  # Include JMS Message Properties Field values in the event
36
37
  config :include_properties, :validate => :boolean, :default => true
38
+
39
+ # List of headers to skip from the event if headers are included
40
+ config :skip_headers, :validate => :array, :default => []
41
+
42
+ # List of properties to skip from the event if properties are included
43
+ config :skip_properties, :validate => :array, :default => []
44
+
37
45
  # Include JMS Message Body in the event
38
46
  # Supports TextMessage, MapMessage and ByteMessage
39
47
  # If the JMS Message is a TextMessage or ByteMessage, then the value will be in the "message" field of the event
@@ -50,7 +58,7 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
50
58
  config :use_jms_timestamp, :validate => :boolean, :default => false
51
59
 
52
60
  # Choose an implementation of the run block. Value can be either consumer, async or thread
53
- config :runner, :validate => [ "consumer", "async", "thread" ], :default => "consumer"
61
+ config :runner, :deprecated => true
54
62
 
55
63
  # Set the selector to use to get messages off the queue or topic
56
64
  config :selector, :validate => :string
@@ -59,13 +67,18 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
59
67
  config :timeout, :validate => :number, :default => 60
60
68
 
61
69
  # Polling interval in seconds.
62
- # This is the time sleeping between asks to a consumed Queue.
63
- # This parameter has non influence in the case of a subcribed Topic.
64
70
  config :interval, :validate => :number, :default => 10
65
71
 
66
72
  # If pub-sub (topic) style should be used.
67
73
  config :pub_sub, :validate => :boolean, :default => false
68
74
 
75
+ # Durable subscriber settings.
76
+ # By default the `durable_subscriber_name` will be set to the topic, and `durable_subscriber_client_id` will be set
77
+ # to 'Logstash'
78
+ config :durable_subscriber, :validate => :boolean, :default => false
79
+ config :durable_subscriber_client_id, :validate => :string, :required => false
80
+ config :durable_subscriber_name, :validate => :string, :required => false
81
+
69
82
  # Name of the destination queue or topic to use.
70
83
  config :destination, :validate => :string, :required => true
71
84
 
@@ -88,7 +101,7 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
88
101
  # Username to connect to JMS provider with
89
102
  config :username, :validate => :string
90
103
  # Password to use when connecting to the JMS provider
91
- config :password, :validate => :string
104
+ config :password, :validate => :password
92
105
  # Url to use when connecting to the JMS provider
93
106
  config :broker_url, :validate => :string
94
107
 
@@ -98,6 +111,18 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
98
111
  # contains details on how to connect to JNDI server
99
112
  config :jndi_context, :validate => :hash
100
113
 
114
+ # System properties
115
+ config :system_properties, :validate => :hash
116
+
117
+ # Factory settings
118
+ config :factory_settings, :validate => :hash
119
+
120
+ config :keystore, :validate => :path
121
+ config :keystore_password, :validate => :password
122
+ config :truststore, :validate => :path
123
+ config :truststore_password, :validate => :password
124
+
125
+
101
126
  # :yaml_file, :factory and :jndi_name are mutually exclusive, both cannot be supplied at the
102
127
  # same time. The priority order is :yaml_file, then :jndi_name, then :factory
103
128
  #
@@ -109,34 +134,125 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
109
134
  public
110
135
  def register
111
136
  require "jms"
112
- @connection = nil
113
137
 
114
- if @yaml_file
115
- @jms_config = YAML.load_file(@yaml_file)[@yaml_section]
138
+ check_config
139
+ load_ssl_properties
140
+ load_system_properties if @system_properties
141
+ @jms_config = jms_config
142
+
143
+ @logger.debug("JMS Config being used ", :context => obfuscate_jms_config(@jms_config))
144
+ end # def register
145
+
146
+ def obfuscate_jms_config(config)
147
+ config.each_with_object({}) { |(k, v), h| h[k] = obfuscatable?(k) ? 'xxxxx' : v }
148
+ end
149
+
150
+ def obfuscatable?(setting)
151
+ [:password, :keystore_password, :truststore_password].include?(setting)
152
+ end
153
+
154
+ def jms_config
155
+ return jms_config_from_yaml(@yaml_file, @yaml_section) if @yaml_file
156
+ return jms_config_from_jndi if @jndi_name
157
+ jms_config_from_configuration
158
+ end
116
159
 
117
- elsif @jndi_name
118
- @jms_config = {
119
- :require_jars => @require_jars,
120
- :jndi_name => @jndi_name,
121
- :jndi_context => @jndi_context}
122
160
 
123
- elsif @factory
124
- @jms_config = {
161
+ def jms_config_from_configuration
162
+ config = {
125
163
  :require_jars => @require_jars,
126
164
  :factory => @factory,
127
165
  :username => @username,
128
- :password => @password,
129
166
  :broker_url => @broker_url,
130
167
  :url => @broker_url # "broker_url" is named "url" with Oracle AQ
131
- }
168
+ }
169
+
170
+ config[:password] = @password.value unless @password.nil?
171
+ correct_factory_hash(config, @factory_settings) unless @factory_settings.nil?
172
+ config
173
+ end
174
+
175
+ def correct_factory_hash(original, value)
176
+ if hash.is_a?(String)
177
+ return true if value.downcase == "true"
178
+ return false if value.downcase == "false"
132
179
  end
133
180
 
134
- @logger.debug("JMS Config being used", :context => @jms_config)
181
+ if value.is_a?(Hash)
182
+ value.each { |key, value| original[key.to_sym] = correct_factory_hash({}, value) }
183
+ return original
184
+ end
185
+ value
186
+ end
135
187
 
136
- end # def register
188
+ def jms_config_from_jndi
189
+ {
190
+ :require_jars => @require_jars,
191
+ :jndi_name => @jndi_name,
192
+ :jndi_context => @jndi_context
193
+ }
194
+ end
195
+
196
+ def jms_config_from_yaml(file, section)
197
+ YAML.load_file(file)[section]
198
+ end
199
+
200
+ def load_ssl_properties
201
+ java.lang.System.setProperty("javax.net.ssl.keyStore", @keystore) if @keystore
202
+ java.lang.System.setProperty("javax.net.ssl.keyStorePassword", @keystore_password.value) if @keystore_password
203
+ java.lang.System.setProperty("javax.net.ssl.trustStore", @truststore) if @truststore
204
+ java.lang.System.setProperty("javax.net.ssl.trustStorePassword", @truststore_password.value) if @truststore_password
205
+ end
206
+
207
+ def load_system_properties
208
+ @system_properties.each { |k,v| java.lang.System.set_property(k,v.to_s) }
209
+ end
210
+
211
+ def check_config
212
+ check_durable_subscription_config
213
+ raise(LogStash::ConfigurationError, "Threads cannot be > 1 if pub_sub is set") if @threads > 1 && @pub_sub
214
+ end
215
+
216
+ def check_durable_subscription_config
217
+ return unless @durable_subscriber
218
+ raise(LogStash::ConfigurationError, "pub_sub must be true if durable_subscriber is set") unless @pub_sub
219
+ @durable_subscriber_client_id ||= 'Logstash'
220
+ @durable_subscriber_name ||= destination
221
+ end
222
+
223
+ def run(output_queue)
224
+ begin
225
+ connection = JMS::Connection.new(@jms_config)
226
+ connection.client_id = @durable_subscriber_client_id if @durable_subscriber_client_id
227
+ session = connection.create_session(@jms_config)
228
+ connection.start
229
+ params = {:timeout => @timeout * 1000, :selector => @selector}
230
+ subscriber = subscriber(session, params)
231
+ until stop?
232
+ # This will read from the queue/topic until :timeout is breached, or messages are available whichever comes
233
+ # first.
234
+ subscriber.each({:timeout => @interval * 1000}) do |message|
235
+ queue_event(message, output_queue)
236
+ break if stop?
237
+ end
238
+ end
239
+ rescue => e
240
+ logger.warn("JMS Consumer Died", error_hash(e))
241
+ unless stop?
242
+ sleep(5)
243
+ subscriber && subscriber.close
244
+ session && session.close
245
+ connection && connection.close
246
+ retry
247
+ end
248
+ ensure
249
+ subscriber && subscriber.close
250
+ session && session.close
251
+ connection && connection.close
252
+ end
253
+ end # def run_consumer
137
254
 
138
255
 
139
- private
140
256
  def queue_event(msg, output_queue)
141
257
  begin
142
258
  if @include_body
@@ -146,13 +262,13 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
146
262
  event.set(field.to_s, value) # TODO(claveau): needs codec.decode or converter.convert ?
147
263
  end
148
264
  elsif msg.java_kind_of?(JMS::TextMessage) || msg.java_kind_of?(JMS::BytesMessage)
149
- if !msg.to_s.nil?
265
+ unless msg.to_s.nil?
150
266
  @codec.decode(msg.to_s) do |event_message|
151
267
  event = event_message
152
268
  end
153
269
  end
154
270
  else
155
- @logger.error( "Unknown data type #{msg.data.class.to_s} in Message" )
271
+ @logger.error( "Unsupported message type #{msg.data.class.to_s}" )
156
272
  end
157
273
  end
158
274
 
@@ -164,14 +280,14 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
164
280
  end
165
281
 
166
282
  if @include_header
167
- msg.attributes.each do |field, value|
168
- event.set(field.to_s, value)
283
+ msg.attributes && msg.attributes.each do |field, value|
284
+ event.set(field.to_s, value) unless @skip_headers.include?(field.to_s)
169
285
  end
170
286
  end
171
287
 
172
288
  if @include_properties
173
- msg.properties.each do |field, value|
174
- event.set(field.to_s, value)
289
+ msg.properties && msg.properties.each do |field, value|
290
+ event.set(field.to_s, value) unless @skip_properties.include?(field.to_s)
175
291
  end
176
292
  end
177
293
 
@@ -180,102 +296,55 @@ class LogStash::Inputs::Jms < LogStash::Inputs::Threadable
180
296
 
181
297
  rescue => e # parse or event creation error
182
298
  @logger.error("Failed to create event", :message => msg, :exception => e,
183
- :backtrace => e.backtrace);
299
+ :backtrace => e.backtrace)
184
300
  end
185
301
  end
186
302
 
187
- # Consume all available messages on the queue
188
- # sleeps some time, then consume again
189
- private
190
- def run_consumer(output_queue)
191
- JMS::Connection.session(@jms_config) do |session|
192
- destination_key = @pub_sub ? :topic_name : :queue_name
193
- while !stop?
194
- session.consume(destination_key => @destination, :timeout=>@timeout, :selector => @selector, :buffered_message => @oracle_aq_buffered_messages) do |message|
195
- queue_event message, output_queue
196
- break if stop?
197
- end
198
- sleep @interval
199
- end
200
- end
201
- rescue => e
202
- @logger.warn("JMS Consumer died", :exception => e, :backtrace => e.backtrace)
203
- sleep(10)
204
- retry unless stop?
205
- end # def run_consumer
206
-
207
- # Consume all available messages on the queue through a listener
208
- private
209
- def run_thread(output_queue)
210
- connection = JMS::Connection.new(@jms_config)
211
- connection.on_exception do |jms_exception|
212
- @logger.warn("JMS Exception has occurred: #{jms_exception}")
213
- end
214
303
 
304
+ def subscriber(session, params)
215
305
  destination_key = @pub_sub ? :topic_name : :queue_name
216
- connection.on_message(destination_key => @destination, :selector => @selector) do |message|
217
- queue_event message, output_queue
218
- end
219
- connection.start
220
- while !stop?
221
- @logger.debug("JMS Thread sleeping ...")
222
- sleep @interval
223
- end
224
- rescue => e
225
- @logger.warn("JMS Consumer died", :exception => e, :backtrace => e.backtrace)
226
- sleep(10)
227
- retry unless stop?
228
- end # def run_thread
229
-
230
- # Consume all available messages on the queue through a listener
231
- private
232
- def run_async(output_queue)
233
- JMS::Connection.start(@jms_config) do |connection|
234
- # Define exception listener
235
- # The problem here is that we do not handle any exception
236
- connection.on_exception do |jms_exception|
237
- @logger.warn("JMS Exception has occurred: #{jms_exception}")
238
- raise jms_exception
239
- end
240
- # Define Asynchronous code block to be called every time a message is received
241
- destination_key = @pub_sub ? :topic_name : :queue_name
242
- connection.on_message(destination_key => @destination, :selector => @selector) do |message|
243
- queue_event message, output_queue
244
- end
245
- # Since the on_message handler above is in a separate thread the thread needs
246
- # to do some other work. It will just sleep for 10 seconds.
247
- while !stop?
248
- @logger.debug("JMS Thread sleeping ...")
249
- sleep @interval
250
- end
251
- end
252
- rescue => e
253
- @logger.warn("JMS Consumer died", :exception => e, :backtrace => e.backtrace)
254
- sleep(10)
255
- retry unless stop?
256
- end # def run_async
306
+ params[destination_key] = @destination
307
+ queue_or_topic = session.create_destination(params)
308
+ @durable_subscriber ? durable_subscriber(session, queue_or_topic, params) :
309
+ regular_subscriber(session, queue_or_topic, params)
310
+ end
257
311
 
258
- public
259
- def run(output_queue)
260
- case @runner
261
- when "consumer" then
262
- run_consumer(output_queue)
263
- when "async" then
264
- run_async(output_queue)
265
- when "thread" then
266
- run_thread(output_queue)
267
- end
268
- end # def run
269
312
 
270
- public
271
- def close
272
- @logger.info("Closing JMS connection")
273
- @connection.close rescue nil
274
- end # def close
313
+ def durable_subscriber(session, queue_or_topic, params)
314
+ params[:selector] ? session.create_durable_subscriber(queue_or_topic, @durable_subscriber_name, params[:selector], false) :
315
+ session.create_durable_subscriber(queue_or_topic, @durable_subscriber_name)
316
+ end
275
317
 
276
- public
277
- def stop
278
- @logger.info("Stopping JMS consumer")
279
- @connection.stop rescue nil
280
- end # def stop
318
+ def regular_subscriber(session, queue_or_topic, params)
319
+ params[:selector] ? session.create_consumer(queue_or_topic, params[:selector]) :
320
+ session.create_consumer(queue_or_topic)
321
+ end
322
+
323
+ def error_hash(e)
324
+ error_hash = {:exception => e.class.name, :exception_message => e.message, :backtrace => e.backtrace}
325
+ root_cause = get_root_cause(e)
326
+ error_hash[:root_cause] = root_cause unless root_cause.nil?
327
+ error_hash
328
+ end
329
+
330
+ # JMS Exceptions can contain chains of Exceptions, making it difficult to determine the root cause of an error
331
+ # without knowing the actual root cause behind the problem.
332
+ # This method protects against Java Exceptions where the cause methods loop. If there is a cause loop, the last
333
+ # cause exception before the loop is detected will be returned, along with an entry in the root_cause hash indicating
334
+ # that an exception loop was detected. This will mean that the root cause may not be the actual root cause of the
335
+ # problem, and further investigation is required
336
+ def get_root_cause(e)
337
+ return nil unless e.respond_to?(:get_cause) && !e.get_cause.nil?
338
+ cause = e
339
+ slow_pointer = e
340
+ # Use a slow pointer to avoid cause loops in Java Exceptions
341
+ move_slow = false
342
+ until (next_cause = cause.get_cause).nil?
343
+ cause = next_cause
344
+ return {:exception => cause.class.name, :exception_message => cause.message, :exception_loop => true } if cause == slow_pointer
345
+ slow_pointer = slow_pointer.cause if move_slow
346
+ move_slow = !move_slow
347
+ end
348
+ {:exception => cause.class.name, :exception_message => cause.message }
349
+ end
281
350
  end # class LogStash::Inputs::Jms
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-jms'
4
- s.version = '3.0.6'
4
+ s.version = '3.1.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Reads events from a Jms Broker"
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/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -0,0 +1,132 @@
1
+ <!--
2
+ Licensed to the Apache Software Foundation (ASF) under one or more
3
+ contributor license agreements. See the NOTICE file distributed with
4
+ this work for additional information regarding copyright ownership.
5
+ The ASF licenses this file to You under the Apache License, Version 2.0
6
+ (the "License"); you may not use this file except in compliance with
7
+ the License. You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ -->
17
+ <!-- START SNIPPET: example -->
18
+ <beans
19
+ xmlns="http://www.springframework.org/schema/beans"
20
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
22
+ http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
23
+
24
+ <!-- Allows us to use system properties as variables in this configuration file -->
25
+ <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
26
+ <property name="locations">
27
+ <value>file:${activemq.conf}/credentials.properties</value>
28
+ </property>
29
+ </bean>
30
+
31
+ <!-- Allows accessing the server log -->
32
+ <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
33
+ lazy-init="false" scope="singleton"
34
+ init-method="start" destroy-method="stop">
35
+ </bean>
36
+
37
+ <!--
38
+ The <broker> element is used to configure the ActiveMQ broker.
39
+ -->
40
+ <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
41
+
42
+ <destinationPolicy>
43
+ <policyMap>
44
+ <policyEntries>
45
+ <policyEntry topic=">" >
46
+ <!-- The constantPendingMessageLimitStrategy is used to prevent
47
+ slow topic consumers to block producers and affect other consumers
48
+ by limiting the number of messages that are retained
49
+ For more information, see:
50
+
51
+ http://activemq.apache.org/slow-consumer-handling.html
52
+
53
+ -->
54
+ <pendingMessageLimitStrategy>
55
+ <constantPendingMessageLimitStrategy limit="1000"/>
56
+ </pendingMessageLimitStrategy>
57
+ </policyEntry>
58
+ </policyEntries>
59
+ </policyMap>
60
+ </destinationPolicy>
61
+
62
+
63
+ <!--
64
+ The managementContext is used to configure how ActiveMQ is exposed in
65
+ JMX. By default, ActiveMQ uses the MBean server that is started by
66
+ the JVM. For more information, see:
67
+
68
+ http://activemq.apache.org/jmx.html
69
+ -->
70
+ <managementContext>
71
+ <managementContext createConnector="false"/>
72
+ </managementContext>
73
+
74
+ <!--
75
+ Configure message persistence for the broker. The default persistence
76
+ mechanism is the KahaDB store (identified by the kahaDB tag).
77
+ For more information, see:
78
+
79
+ http://activemq.apache.org/persistence.html
80
+ -->
81
+ <persistenceAdapter>
82
+ <kahaDB directory="${activemq.data}/kahadb"/>
83
+ </persistenceAdapter>
84
+
85
+
86
+ <!--
87
+ The systemUsage controls the maximum amount of space the broker will
88
+ use before disabling caching and/or slowing down producers. For more information, see:
89
+ http://activemq.apache.org/producer-flow-control.html
90
+ -->
91
+ <systemUsage>
92
+ <systemUsage>
93
+ <memoryUsage>
94
+ <memoryUsage percentOfJvmHeap="70" />
95
+ </memoryUsage>
96
+ <storeUsage>
97
+ <storeUsage limit="100 gb"/>
98
+ </storeUsage>
99
+ <tempUsage>
100
+ <tempUsage limit="50 gb"/>
101
+ </tempUsage>
102
+ </systemUsage>
103
+ </systemUsage>
104
+
105
+ <!--
106
+ The transport connectors expose ActiveMQ over a given protocol to
107
+ clients and other brokers. For more information, see:
108
+
109
+ http://activemq.apache.org/configuring-transports.html
110
+ -->
111
+ <transportConnectors>
112
+ <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
113
+ <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
114
+ </transportConnectors>
115
+
116
+ <!-- destroy the spring context on shutdown to stop jetty -->
117
+ <shutdownHooks>
118
+ <bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook" />
119
+ </shutdownHooks>
120
+
121
+ </broker>
122
+
123
+ <!--
124
+ Enable web consoles, REST and Ajax APIs and demos
125
+ The web consoles requires by default login, you can disable this in the jetty.xml file
126
+
127
+ Take a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details
128
+ -->
129
+ <import resource="jetty.xml"/>
130
+
131
+ </beans>
132
+ <!-- END SNIPPET: example -->