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

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
  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