logstash-input-s3-sns-sqs 2.0.3 → 2.0.4

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
  SHA256:
3
- metadata.gz: 037f46eeeb1316d0e6a108b5f573ebc9a56649a3ddc0d97276898defbf1e3c5e
4
- data.tar.gz: f5b670848c3c7c4da6d00a9a8e224bfe11447eafda3dc0d4f9e03a90e1890562
3
+ metadata.gz: 131941a4c12a2dc0399793b18b90ea9ac40bfe741d8fca8b63e6a3d257863693
4
+ data.tar.gz: 1ae77c566d3421cf36ed2e5a16039f49a526a83b09db106f537fe94d893d45f7
5
5
  SHA512:
6
- metadata.gz: d8be989f95a4651d4283b4770da33544bfe30d417b44d9de982fbf203a37e310cdb9cc7d3ebfe1be7d798bdc843ce9033762798f2bad2f5b62d6b09657cdbc4c
7
- data.tar.gz: e57bed2000ed438e4ee2b543251f2757dd9fea2c8ad6d7d7aa93c9d9083c726e9fcc12c51eaa8bda2322ac12cb975d5c26e7bda34a922123561698e846f28f25
6
+ metadata.gz: cc602eddfdb4adcf5a08244b4e392042a250773b11f98020712a9fcb3988710355ec9b1931834d7fd0e8d0a15eb0227c94578f7c4da7a146b3451a7cded8c4a5
7
+ data.tar.gz: 6f3cd9970544a86a621a0be5cb004edd14498fd3f6a3208cb6d183dde172ec98ef24b6235c4b47cf4fe2b09c14c3d8a992372669e944346517ff725606d7fd83
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ##2.0.4
2
+ - Fix minor bugs
3
+ ##2.0.3
4
+ - Increase max parsing time -> drop event if reached
5
+ - watcher thread should raise an error in poller loop if timeout reached.
6
+ - remove some debug logs
7
+
1
8
  ##2.0.2
2
9
  FIX:
3
10
  - Terminate every input line by \n (BufferedReader does not)
@@ -25,7 +25,7 @@ class CodecFactory
25
25
  def find_codec(record)
26
26
  bucket, key, folder = record[:bucket], record[:key], record[:folder]
27
27
  unless @codec_by_folder[bucket].nil?
28
- @logger.debug("trying to find codec for folder #{folder}", :codec => @codec_by_folder[bucket][folder])
28
+ @logger.debug("Looking up codec for folder #{folder}", :codec => @codec_by_folder[bucket][folder])
29
29
  return @codec_by_folder[bucket][folder] unless @codec_by_folder[bucket][folder].nil?
30
30
  end
31
31
  return 'default'
@@ -13,7 +13,6 @@ class S3ClientFactory
13
13
  @logger.debug("Credentials by Bucket", :credentials => @credentials_by_bucket)
14
14
  @default_session_name = options[:s3_role_session_name]
15
15
  @clients_by_bucket = {}
16
- #@mutexes_by_bucket = {}
17
16
  @creation_mutex = Mutex.new
18
17
  end
19
18
 
@@ -27,18 +26,13 @@ class S3ClientFactory
27
26
  end
28
27
  @clients_by_bucket[bucket_symbol] = Aws::S3::Client.new(options)
29
28
  @logger.debug("Created a new S3 Client", :bucket_name => bucket_name, :client => @clients_by_bucket[bucket_symbol], :used_options => options)
30
- #@mutexes_by_bucket[bucket_symbol] = Mutex.new
31
29
  end
32
30
  end
33
31
  # to be thread-safe, one uses this method like this:
34
32
  # s3_client_factory.get_s3_client(my_s3_bucket) do
35
33
  # ... do stuff ...
36
34
  # end
37
- # FIXME: this does not allow concurrent downloads from the same bucket!
38
- #@mutexes_by_bucket[bucket_symbol].synchronize do
39
- # So we are testing this without this mutex.
40
35
  yield @clients_by_bucket[bucket_symbol]
41
- #end
42
36
  end
43
37
 
44
38
  private
@@ -15,14 +15,12 @@ class S3Downloader
15
15
  # (from docs) WARNING:
16
16
  # yielding data to a block disables retries of networking errors!
17
17
  begin
18
- #@logger.info("Download File", :file => record)
19
18
  @factory.get_s3_client(record[:bucket]) do |s3|
20
19
  response = s3.get_object(
21
20
  bucket: record[:bucket],
22
21
  key: record[:key],
23
22
  response_target: record[:local_file]
24
23
  )
25
- #@logger.info("READY: File", :file => record, :response => response)
26
24
  end
27
25
  rescue Aws::S3::Errors::ServiceError => e
28
26
  @logger.error("Unable to download file. Requeuing the message", :error => e, :record => record)
@@ -34,7 +32,6 @@ class S3Downloader
34
32
  end
35
33
 
36
34
  def cleanup_local_object(record)
37
- #@logger.info("Cleaning up file", :file => record[:local_file])
38
35
  FileUtils.remove_entry_secure(record[:local_file], true) if ::File.exists?(record[:local_file])
39
36
  rescue Exception => e
40
37
  @logger.warn("Could not delete file", :file => record[:local_file], :error => e)
@@ -160,6 +160,7 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
160
160
  config :s3_options_by_bucket, :validate => :array, :required => false # TODO: true
161
161
  # Session name to use when assuming an IAM role
162
162
  config :s3_role_session_name, :validate => :string, :default => "logstash"
163
+ config :delete_on_success, :validate => :boolean, :default => false
163
164
 
164
165
  ### sqs
165
166
  # Name of the SQS Queue to pull messages from. Note that this is just the name of the queue, not the URL or ARN.
@@ -168,8 +169,8 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
168
169
  # Whether the event is processed though an SNS to SQS. (S3>SNS>SQS = true |S3>SQS=false)
169
170
  config :from_sns, :validate => :boolean, :default => true
170
171
  config :sqs_skip_delete, :validate => :boolean, :default => false
171
- config :delete_on_success, :validate => :boolean, :default => false
172
- config :visibility_timeout, :validate => :number, :default => 600
172
+ config :visibility_timeout, :validate => :number, :default => 120
173
+ config :max_processing_time, :validate => :number, :default => 8000
173
174
 
174
175
  ### system
175
176
  config :temporary_directory, :validate => :string, :default => File.join(Dir.tmpdir, "logstash")
@@ -185,7 +186,7 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
185
186
  def register
186
187
  # prepare system
187
188
  FileUtils.mkdir_p(@temporary_directory) unless Dir.exist?(@temporary_directory)
188
-
189
+ @id ||= "Unknown" #Use INPUT{ id => name} for thread identifier
189
190
  @credentials_by_bucket = hash_key_is_regex({})
190
191
  # create the bucket=>folder=>codec lookup from config options
191
192
  @codec_by_folder = hash_key_is_regex({})
@@ -238,12 +239,18 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
238
239
  @received_stop = Concurrent::AtomicBoolean.new(false)
239
240
 
240
241
  # instantiate helpers
241
- @sqs_poller = SqsPoller.new(@logger, @received_stop, @queue, {
242
- queue_owner_aws_account_id: @queue_owner_aws_account_id,
243
- from_sns: @from_sns,
244
- sqs_explicit_delete: @sqs_explicit_delete,
245
- visibility_timeout: @visibility_timeout
246
- }, aws_options_hash)
242
+ @sqs_poller = SqsPoller.new(@logger, @received_stop,
243
+ {
244
+ visibility_timeout: @visibility_timeout,
245
+ skip_delete: @sqs_skip_delete
246
+ },
247
+ {
248
+ sqs_queue: @queue,
249
+ queue_owner_aws_account_id: @queue_owner_aws_account_id,
250
+ from_sns: @from_sns,
251
+ max_processing_time: @max_processing_time
252
+ },
253
+ aws_options_hash)
247
254
  @s3_client_factory = S3ClientFactory.new(@logger, {
248
255
  aws_region: @region,
249
256
  s3_default_options: @s3_default_options,
@@ -271,7 +278,10 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
271
278
  @queue_mutex = Mutex.new
272
279
  #@consumer_threads= 1
273
280
  @worker_threads = @consumer_threads.times.map do |thread_id|
274
- run_worker_thread(logstash_event_queue, thread_id)
281
+ t = run_worker_thread(logstash_event_queue, thread_id)
282
+ #make thead start async to prevent polling the same message from sqs
283
+ sleep 0.5
284
+ t
275
285
  end
276
286
  # and wait (possibly infinitely) for them to shut down
277
287
  @worker_threads.each { |t| t.join }
@@ -280,13 +290,16 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
280
290
  # shutdown
281
291
  def stop
282
292
  @received_stop.make_true
283
- @worker_threads.each do |worker|
284
- begin
285
- @logger.info("Stopping thread ... ", :thread => worker.inspect)
286
- worker.wakeup
287
- rescue
288
- @logger.error("Cannot stop thread ... try to kill him", :thread => worker.inspect)
289
- worker.kill
293
+
294
+ unless @worker_threads.nil?
295
+ @worker_threads.each do |worker|
296
+ begin
297
+ @logger.info("Stopping thread ... ", :thread => worker.inspect)
298
+ worker.wakeup
299
+ rescue
300
+ @logger.error("Cannot stop thread ... try to kill him", :thread => worker.inspect)
301
+ worker.kill
302
+ end
290
303
  end
291
304
  end
292
305
  end
@@ -301,14 +314,13 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
301
314
 
302
315
  def run_worker_thread(queue, thread_id)
303
316
  Thread.new do
304
- @logger.info("Starting new worker thread")
317
+ LogStash::Util.set_thread_name("Worker #{@id}/#{thread_id}")
318
+ @logger.info("[#{Thread.current[:name]}] started (#{Time.now})") #PROFILING
305
319
  temporary_directory = Dir.mktmpdir("#{@temporary_directory}/")
306
320
  @sqs_poller.run do |record|
307
321
  throw :skip_delete if stop?
308
- @logger.debug("Outside Poller: got a record", :record => record)
309
322
  # record is a valid object with the keys ":bucket", ":key", ":size"
310
323
  record[:local_file] = File.join(temporary_directory, File.basename(record[:key]))
311
- LogStash::Util.set_thread_name("[Processor #{thread_id} - Working on: #{record[:key]}")
312
324
  if @s3_downloader.copy_s3object_to_disk(record)
313
325
  completed = catch(:skip_delete) do
314
326
  process(record, queue)
@@ -322,19 +334,18 @@ class LogStash::Inputs::S3SNSSQS < LogStash::Inputs::Threadable
322
334
  end
323
335
  end
324
336
 
325
- # Will be remove in further releases...
337
+ # Will be removed in further releases:
326
338
  def get_object_folder(key)
327
339
  if match = /#{s3_key_prefix}\/?(?<type_folder>.*?)\/.*/.match(key)
328
340
  return match['type_folder']
329
341
  else
330
- #FIXME should be NIL instedt of empty sting?
331
342
  return ""
332
343
  end
333
344
  end
334
345
 
335
346
  def hash_key_is_regex(myhash)
336
347
  myhash.default_proc = lambda do |hash, lookup|
337
- result=nil
348
+ result = nil
338
349
  hash.each_pair do |key, value|
339
350
  if %r[#{key}] =~ lookup
340
351
  result = value
@@ -14,33 +14,39 @@ module LogProcessor
14
14
  file = record[:local_file]
15
15
  codec = @codec_factory.get_codec(record)
16
16
  folder = record[:folder]
17
- type = @type_by_folder[folder] #if @type_by_folder.key?(folder)
17
+ type = @type_by_folder[folder]
18
18
  metadata = {}
19
- #@logger.info("processing file",:thread => Thread.current[:name], :local_file => record[:local_file])
20
19
  line_count = 0
21
20
  event_count = 0
21
+ #start_time = Time.now
22
+ file_t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) #PROFILING
22
23
  read_file(file) do |line|
23
24
  line_count += 1
24
- #@logger.info("got a yielded line", :line_count => line_count) if line_count < 10
25
25
  if stop?
26
- @logger.warn("Abort reading in the middle of the file, we will read it again when logstash is started")
26
+ @logger.warn("[#{Thread.current[:name]}] Abort reading in the middle of the file, we will read it again when logstash is started")
27
27
  throw :skip_delete
28
28
  end
29
29
  line = line.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: "\u2370")
30
- #@logger.info("ENcoded line", :line_count => line_count) if line_count < 10
30
+ # Potentially dangerous! See https://medium.com/@adamhooper/in-ruby-dont-use-timeout-77d9d4e5a001
31
+ # Decoding a line must not last longer than a few seconds. Otherwise, the file is probably corrupt.
31
32
  codec.decode(line) do |event|
32
- decorate_event(event, metadata, type, record[:key], record[:bucket], folder)
33
33
  event_count += 1
34
+ decorate_event(event, metadata, type, record[:key], record[:bucket], folder)
35
+ #event_time = Time.now #PROFILING
36
+ #event.set("[@metadata][progress][begin]", start_time)
37
+ #event.set("[@metadata][progress][index_time]", event_time)
38
+ #event.set("[@metadata][progress][line]", line_count)
34
39
  logstash_event_queue << event
35
- #@logger.info("queued event ", :lines => line_count, :events => event_count, :thread => Thread.current[:name]) if event_count < 10
36
40
  end
37
- #@logger.info("DEcoded line", :line_count => line_count) if line_count < 10
38
41
  end
39
- #@logger.info("queued all events ", :lines => line_count, :events => event_count, :thread => Thread.current[:name])
42
+ file_t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC) #PROFILING
43
+ processing_time = (file_t1 - file_t0)
44
+ #@logger.warn("[#{Thread.current[:name]}] Completed long running File ( took #{processing_time} ) s", file: record[:key], events: event_count, processing_time: processing_time ) if processing_time > 600.0 #PROFILING
40
45
  # ensure any stateful codecs (such as multi-line ) are flushed to the queue
41
46
  codec.flush do |event|
47
+ event_count += 1
42
48
  decorate_event(event, metadata, type, record[:key], record[:bucket], folder)
43
- @logger.debug("Flushing an incomplete event", :event => event.to_s)
49
+ @logger.debug("[#{Thread.current[:name]}] Flushing an incomplete event", :event => event.to_s)
44
50
  logstash_event_queue << event
45
51
  end
46
52
  # signal completion:
@@ -55,7 +61,7 @@ module LogProcessor
55
61
  update_metadata(metadata, event)
56
62
  else
57
63
  # type by folder - set before "decorate()" enforces default
58
- event.set('type', type) if type && !event.include?('type')
64
+ event.set('type', type) if type and ! event.include?('type')
59
65
  decorate(event)
60
66
 
61
67
  event.set("cloudfront_version", metadata[:cloudfront_version]) unless metadata[:cloudfront_version].nil?
@@ -63,7 +69,6 @@ module LogProcessor
63
69
 
64
70
  event.set("[@metadata][s3][object_key]", key)
65
71
  event.set("[@metadata][s3][bucket_name]", bucket)
66
- event.set("[@metadata][s3][full_folder]", folder)
67
72
  event.set("[@metadata][s3][object_folder]", get_object_folder(key))
68
73
  end
69
74
  end
@@ -99,9 +104,13 @@ module LogProcessor
99
104
  decoder.close unless decoder.nil?
100
105
  gzip_stream.close unless gzip_stream.nil?
101
106
  file_stream.close unless file_stream.nil?
102
- throw :skip_delete unless completed
103
- end
104
107
 
108
+ unless completed
109
+ @logger.warn("[#{Thread.current[:name]}] Incomplete message in read_file. We´ll throw skip_delete.", :filename => filename)
110
+ throw :skip_delete
111
+ end
112
+
113
+ end
105
114
 
106
115
  def event_is_metadata?(event)
107
116
  return false unless event.get("message").class == String
@@ -15,11 +15,13 @@ class SqsPoller
15
15
  # "DefaultVisibilityTimeout" for your queue so that there's enough time
16
16
  # to process the log files!)
17
17
  max_number_of_messages: 1,
18
- visibility_timeout: 600,
18
+ visibility_timeout: 10,
19
19
  # long polling; by default we use the queue's setting.
20
20
  # A good value is 10 seconds to to balance between a quick logstash
21
21
  # shutdown and fewer api calls.
22
22
  wait_time_seconds: nil,
23
+ #attribute_names: ["All"], # Receive all available built-in message attributes.
24
+ #message_attribute_names: ["All"], # Receive any custom message attributes.
23
25
  skip_delete: false,
24
26
  }
25
27
 
@@ -33,20 +35,19 @@ class SqsPoller
33
35
 
34
36
  # initialization and setup happens once, outside the threads:
35
37
  #
36
- def initialize(logger, stop_semaphore, sqs_queue, options = {}, aws_options_hash)
38
+ def initialize(logger, stop_semaphore, poller_options = {}, client_options = {}, aws_options_hash)
37
39
  @logger = logger
38
40
  @stopped = stop_semaphore
39
- @queue = sqs_queue
40
- # @stopped = false # FIXME: needed per thread?
41
- @from_sns = options[:from_sns]
42
- @options = DEFAULT_OPTIONS.merge(options.reject { |k| [:sqs_explicit_delete, :from_sns, :queue_owner_aws_account_id, :sqs_skip_delete].include? k })
43
- @options[:skip_delete] = options[:sqs_skip_delete]
41
+ @queue = client_options[:sqs_queue]
42
+ @from_sns = client_options[:from_sns]
43
+ @max_processing_time = client_options[:max_processing_time]
44
+ @options = DEFAULT_OPTIONS.merge(poller_options)
44
45
  begin
45
46
  @logger.info("Registering SQS input", :queue => @queue)
46
47
  sqs_client = Aws::SQS::Client.new(aws_options_hash)
47
48
  queue_url = sqs_client.get_queue_url({
48
49
  queue_name: @queue,
49
- queue_owner_aws_account_id: @options[:queue_owner_aws_account_id]
50
+ queue_owner_aws_account_id: client_options[:queue_owner_aws_account_id]
50
51
  }).queue_url # is a method according to docs. Was [:queue_url].
51
52
  @poller = Aws::SQS::QueuePoller.new(queue_url,
52
53
  :client => sqs_client
@@ -57,13 +58,11 @@ class SqsPoller
57
58
  end
58
59
  end
59
60
 
60
- #
61
61
  # this is called by every worker thread:
62
- #
63
62
  def run() # not (&block) - pass explicitly (use yield below)
64
63
  # per-thread timer to extend visibility if necessary
65
64
  extender = nil
66
- message_backoff = (@options[:visibility_timeout] * 90).to_f / 100.0
65
+ message_backoff = (@options[:visibility_timeout] * 95).to_f / 100.0
67
66
  new_visibility = 2 * @options[:visibility_timeout]
68
67
 
69
68
  # "shutdown handler":
@@ -79,19 +78,39 @@ class SqsPoller
79
78
  end
80
79
 
81
80
  run_with_backoff do
81
+ message_count = 0 #PROFILING
82
82
  @poller.poll(@options) do |message|
83
- @logger.debug("Inside Poller: polled message", :message => message)
84
- # auto-double the timeout if processing takes too long:
83
+ message_count += 1 #PROFILING
84
+ message_t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) #PROFILING
85
+ # auto-increase the timeout if processing takes too long:
86
+ poller_thread = Thread.current
85
87
  extender = Thread.new do
86
- sleep message_backoff
87
- @logger.info("Extending visibility for message", :message => message)
88
- @poller.change_message_visibility_timeout(message, new_visibility)
88
+ while new_visibility < @max_processing_time do
89
+ sleep message_backoff
90
+ begin
91
+ @poller.change_message_visibility_timeout(message, new_visibility)
92
+ @logger.warn("[#{Thread.current[:name]}] Extended visibility for a long running message", :visibility => new_visibility) if new_visibility > 600.0
93
+ new_visibility += message_backoff
94
+ rescue Aws::SQS::Errors::InvalidParameterValue => e
95
+ @logger.debug("Extending visibility failed for message", :error => e)
96
+ else
97
+ @logger.debug("[#{Thread.current[:name]}] Extended visibility for message", :visibility => new_visibility) #PROFILING
98
+ end
99
+ end
100
+ @logger.error("[#{Thread.current[:name]}] Maximum visibility reached! We will delete this message from queue!")
101
+ @poller.delete_message(message)
102
+ poller_thread.raise "[#{poller_thread[:name]}] Maximum visibility reached...!".freeze
89
103
  end
104
+ extender[:name] = "#{Thread.current[:name]}/extender" #PROFILING
90
105
  failed = false
106
+ record_count = 0
91
107
  begin
92
- preprocess(message) do |record|
93
- #@logger.info("we got a record", :record => record)
94
- yield(record) #unless record.nil? - unnecessary; implicit
108
+ message_completed = catch(:skip_delete) do
109
+ preprocess(message) do |record|
110
+ record_count += 1
111
+ extender[:name] = "#{Thread.current[:name]}/extender/#{record[:key]}" #PROFILING
112
+ yield(record)
113
+ end
95
114
  end
96
115
  rescue Exception => e
97
116
  @logger.warn("Error in poller loop", :error => e)
@@ -100,9 +119,14 @@ class SqsPoller
100
119
  end
101
120
  # at this time the extender has either fired or is obsolete
102
121
  extender.kill
103
- #@logger.info("Inside Poller: killed background thread", :message => message)
104
122
  extender = nil
105
- throw :skip_delete if failed
123
+ message_t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC) #PROFILING
124
+ unless message_completed
125
+ @logger.debug("[#{Thread.current[:name]}] uncompleted message at the end of poller loop. We´ll throw skip_delete.", :message_count => message_count)
126
+ extender.run
127
+ end
128
+ throw :skip_delete if failed or ! message_completed
129
+ #@logger.info("[#{Thread.current[:name]}] completed message.", :message => message_count)
106
130
  end
107
131
  end
108
132
  end
@@ -114,7 +138,7 @@ class SqsPoller
114
138
  end
115
139
 
116
140
  def preprocess(message)
117
- @logger.debug("Inside Preprocess: Start", :message => message)
141
+ @logger.debug("Inside Preprocess: Start", :event => message)
118
142
  payload = JSON.parse(message.body)
119
143
  payload = JSON.parse(payload['Message']) if @from_sns
120
144
  @logger.debug("Payload in Preprocess: ", :payload => payload)
@@ -132,20 +156,8 @@ class SqsPoller
132
156
  bucket: bucket,
133
157
  key: key,
134
158
  size: size,
135
- folder: get_type_folder(key)
159
+ folder: get_object_path(key)
136
160
  })
137
-
138
- # -v- this stuff goes into s3 and processor handling: -v-
139
-
140
- # type_folder = get_object_folder(key)
141
- # Set input codec by :set_codec_by_folder
142
- # instance_codec = set_codec(type_folder) unless set_codec_by_folder["#{type_folder}"].nil?
143
- # try download and :skip_delete if it fails
144
- #if record['s3']['object']['size'] < 10000000 then
145
- # process_log(bucket, key, type_folder, instance_codec, queue, message, size)
146
- #else
147
- # @logger.info("Your file is too big")
148
- #end
149
161
  end
150
162
  end
151
163
  end
@@ -171,11 +183,7 @@ class SqsPoller
171
183
  end
172
184
  end
173
185
 
174
- def get_type_folder(key)
175
- # TEST THIS!
176
- # if match = /.*\/?(?<type_folder>)\/[^\/]*.match(key)
177
- # return match['type_folder']
178
- # end
186
+ def get_object_path(key)
179
187
  folder = ::File.dirname(key)
180
188
  return '' if folder == '.'
181
189
  return folder
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-s3-sns-sqs'
3
- s.version = '2.0.3'
3
+ s.version = '2.0.4'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "Get logs from AWS s3 buckets as issued by an object-created event via sns -> sqs."
6
6
  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-s3-sns-sqs
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Herweg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-01 00:00:00.000000000 Z
11
+ date: 2019-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement