logstash-output-file 3.0.2 → 4.0.0

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: 816218ba76195ff85b4c30a0876ec027daa7ae89
4
- data.tar.gz: 67ef9d3a53d5b0d94906da6ffa3cc110048edf6e
3
+ metadata.gz: 7519db3db3ff8bc30ac81db904b4141f518f326b
4
+ data.tar.gz: 4e0736f6ec2d53be5b3675ac0b2a3015d5a6ac6b
5
5
  SHA512:
6
- metadata.gz: 16d18bc194e55a7de0e1195db43e4984cc09d872982d9c6df64600e2e02cef4f78b3afa4f0a73692411d14e1ee3b358963e73aa5bd3b3c2f273acd53e146eaee
7
- data.tar.gz: 2f51482a17ad47a5889a29c01e87656f3e4ef22534704a70874ddc0a6b55d0fe9afacbf90e854b96bc32757e16f428078a03aeeeea40bbaa30c948f5f96a11d0
6
+ metadata.gz: 3fdd1d4e5edcb5025969a6298ec457d315386d345ebc961d36c34455ebfa1d0d4b7bc0c3791a83ac5a0dbadc674773fb73a6bb3981b442a42f03858f602d72bf
7
+ data.tar.gz: 2e357366f22791bd8024845f2ad6ee25e69bd06803cad29957d28c2bdc7899a9ef945bc2c61a123ef79fa4f449865548495ad78b51ef7b17634fc14acf6a0a0f
@@ -1,3 +1,7 @@
1
+ ## 4.0.0
2
+ - Make 'message_format' option obsolete
3
+ - Use new Logsash 2.4/5.0 APIs for working batchwise and with shared concurrency
4
+
1
5
  ## 3.0.2
2
6
  - Relax constraint on logstash-core-plugin-api to >= 1.60 <= 2.99
3
7
 
@@ -17,6 +17,8 @@ require "zlib"
17
17
  # }
18
18
  # }
19
19
  class LogStash::Outputs::File < LogStash::Outputs::Base
20
+ concurrency :shared
21
+
20
22
  FIELD_REF = /%\{[^}]+\}/
21
23
 
22
24
  config_name "file"
@@ -35,13 +37,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
35
37
  # E.g: `/%{myfield}/`, `/test-%{myfield}/` are not valid paths
36
38
  config :path, :validate => :string, :required => true
37
39
 
38
- # The format to use when writing events to the file. This value
39
- # supports any string and can include `%{name}` and other dynamic
40
- # strings.
41
- #
42
- # If this setting is omitted, the full json representation of the
43
- # event will be written as a single line.
44
- config :message_format, :validate => :string, :deprecated => "You can achieve the same behavior with the 'line' codec"
40
+ config :message_format, :validate => :string, :obsolete => "You can achieve the same behavior with the 'line' codec"
45
41
 
46
42
  # Flush interval (in seconds) for flushing writes to log files.
47
43
  # 0 will flush on every message.
@@ -76,10 +72,9 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
76
72
  def register
77
73
  require "fileutils" # For mkdir_p
78
74
 
79
- workers_not_supported
80
-
81
75
  @files = {}
82
-
76
+ @io_mutex = Mutex.new
77
+
83
78
  @path = File.expand_path(path)
84
79
 
85
80
  validate_path
@@ -91,6 +86,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
91
86
  end
92
87
  @failure_path = File.join(@file_root, @filename_failure)
93
88
 
89
+
94
90
  now = Time.now
95
91
  @last_flush_cycle = now
96
92
  @last_stale_cleanup_cycle = now
@@ -101,8 +97,6 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
101
97
  @codec = LogStash::Plugin.lookup("codec", "line").new
102
98
  @codec.format = @message_format
103
99
  end
104
-
105
- @codec.on_event(&method(:write_event))
106
100
  end # def register
107
101
 
108
102
  private
@@ -125,20 +119,37 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
125
119
  end
126
120
 
127
121
  public
128
- def receive(event)
129
- @codec.encode(event)
130
- close_stale_files
122
+ def multi_receive_encoded(events_and_encoded)
123
+ encoded_by_path = Hash.new {|h,k| h[k] = []}
124
+
125
+ events_and_encoded.each do |event,encoded|
126
+ file_output_path = event_path(event)
127
+ encoded_by_path[file_output_path] << encoded
128
+ end
129
+
130
+ @io_mutex.synchronize do
131
+ encoded_by_path.each do |path,chunks|
132
+ fd = open(path)
133
+ chunks.each {|chunk| fd.write(chunk) }
134
+ fd.flush
135
+ end
136
+
137
+ close_stale_files
138
+ end
131
139
  end # def receive
132
140
 
133
141
  public
134
142
  def close
135
- @logger.debug("Close: closing files")
136
- @files.each do |path, fd|
137
- begin
138
- fd.close
139
- @logger.debug("Closed file #{path}", :fd => fd)
140
- rescue Exception => e
141
- @logger.error("Exception while flushing and closing files.", :exception => e)
143
+ @io_mutex.synchronize do
144
+ @logger.debug("Close: closing files")
145
+
146
+ @files.each do |path, fd|
147
+ begin
148
+ fd.close
149
+ @logger.debug("Closed file #{path}", :fd => fd)
150
+ rescue Exception => e
151
+ @logger.error("Exception while flushing and closing files.", :exception => e)
152
+ end
142
153
  end
143
154
  end
144
155
  end
@@ -150,7 +161,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
150
161
  end
151
162
 
152
163
  private
153
- def write_event(event, data)
164
+ def event_path(event)
154
165
  file_output_path = generate_filepath(event)
155
166
  if path_with_field_ref? && !inside_file_root?(file_output_path)
156
167
  @logger.warn("File: the event tried to write outside the files root, writing the event to the failure file", :event => event, :filename => @failure_path)
@@ -159,10 +170,8 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
159
170
  file_output_path = @failure_path
160
171
  end
161
172
  @logger.debug("File, writing event to file.", :filename => file_output_path)
162
- fd = open(file_output_path)
163
- # TODO(sissel): Check if we should rotate the file.
164
- fd.write(data)
165
- flush(fd)
173
+
174
+ file_output_path
166
175
  end
167
176
 
168
177
  private
@@ -195,10 +204,12 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
195
204
  def flush_pending_files
196
205
  return unless Time.now - @last_flush_cycle >= flush_interval
197
206
  @logger.debug("Starting flush cycle")
207
+
198
208
  @files.each do |path, fd|
199
209
  @logger.debug("Flushing file", :path => path, :fd => fd)
200
210
  fd.flush
201
211
  end
212
+
202
213
  @last_flush_cycle = Time.now
203
214
  end
204
215
 
@@ -207,6 +218,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
207
218
  def close_stale_files
208
219
  now = Time.now
209
220
  return unless now - @last_stale_cleanup_cycle >= @stale_cleanup_interval
221
+
210
222
  @logger.info("Starting stale files cleanup cycle", :files => @files)
211
223
  inactive_files = @files.select { |path, fd| not fd.active }
212
224
  @logger.debug("%d stale files found" % inactive_files.count, :inactive_files => inactive_files)
@@ -222,7 +234,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
222
234
 
223
235
  private
224
236
  def cached?(path)
225
- @files.include?(path) && !@files[path].nil?
237
+ @files.include?(path) && !@files[path].nil?
226
238
  end
227
239
 
228
240
  private
@@ -234,7 +246,9 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
234
246
  def open(path)
235
247
  if !deleted?(path) && cached?(path)
236
248
  return @files[path]
237
- elsif deleted?(path)
249
+ end
250
+
251
+ if deleted?(path)
238
252
  if @create_if_deleted
239
253
  @logger.debug("Required path was deleted, creating the file again", :path => path)
240
254
  @files.delete(path)
@@ -242,8 +256,9 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
242
256
  return @files[path] if cached?(path)
243
257
  end
244
258
  end
245
- @logger.info("Opening file", :path => path)
246
259
 
260
+ @logger.info("Opening file", :path => path)
261
+
247
262
  dir = File.dirname(path)
248
263
  if !Dir.exist?(dir)
249
264
  @logger.info("Creating directory", :directory => dir)
@@ -253,6 +268,7 @@ class LogStash::Outputs::File < LogStash::Outputs::Base
253
268
  FileUtils.mkdir_p(dir)
254
269
  end
255
270
  end
271
+
256
272
  # work around a bug opening fifos (bug JRUBY-6280)
257
273
  stat = File.stat(path) rescue nil
258
274
  if stat && stat.ftype == "fifo" && LogStash::Environment.jruby?
@@ -288,6 +304,7 @@ class IOWriter
288
304
  end
289
305
  def method_missing(method_name, *args, &block)
290
306
  if @io.respond_to?(method_name)
307
+
291
308
  @io.send(method_name, *args, &block)
292
309
  else
293
310
  super
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-file'
4
- s.version = '3.0.2'
4
+ s.version = '4.0.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "This output will write events to files on disk"
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"
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
21
21
 
22
22
  # Gem dependencies
23
- s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
23
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 2.0.0", "< 2.99"
24
24
  s.add_runtime_dependency 'logstash-codec-json_lines'
25
25
  s.add_runtime_dependency 'logstash-codec-line'
26
26
 
@@ -47,7 +47,7 @@ describe LogStash::Outputs::File do
47
47
 
48
48
  describe "ship lots of events to a file gzipped" do
49
49
  Stud::Temporary.file('logstash-spec-output-file') do |tmp_file|
50
- event_count = 10000 + rand(500)
50
+ event_count = 100000 + rand(500)
51
51
 
52
52
  config <<-CONFIG
53
53
  input {
@@ -125,13 +125,14 @@ describe LogStash::Outputs::File do
125
125
 
126
126
  10.times do |i|
127
127
  event = LogStash::Event.new("event_id" => i)
128
- output.receive(event)
128
+ output.multi_receive([event])
129
129
  end
130
130
  FileUtils.rm(temp_file)
131
131
  10.times do |i|
132
132
  event = LogStash::Event.new("event_id" => i+10)
133
- output.receive(event)
133
+ output.multi_receive([event])
134
134
  end
135
+
135
136
  expect(FileTest.size(temp_file.path)).to be > 0
136
137
  end
137
138
 
@@ -147,12 +148,12 @@ describe LogStash::Outputs::File do
147
148
 
148
149
  10.times do |i|
149
150
  event = LogStash::Event.new("event_id" => i)
150
- output.receive(event)
151
+ output.multi_receive([event])
151
152
  end
152
153
  FileUtils.rm(temp_file)
153
154
  10.times do |i|
154
155
  event = LogStash::Event.new("event_id" => i+10)
155
- output.receive(event)
156
+ output.multi_receive([event])
156
157
  end
157
158
  expect(FileTest.exist?(temp_file.path)).to be_falsey
158
159
  expect(FileTest.size(output.failure_path)).to be > 0
@@ -184,7 +185,7 @@ describe LogStash::Outputs::File do
184
185
 
185
186
  output = LogStash::Outputs::File.new(config)
186
187
  output.register
187
- output.receive(bad_event)
188
+ output.multi_receive([bad_event])
188
189
 
189
190
  error_file = File.join(path, config["filename_failure"])
190
191
 
@@ -202,10 +203,10 @@ describe LogStash::Outputs::File do
202
203
  output.register
203
204
 
204
205
  bad_event.set('error', encoded_once)
205
- output.receive(bad_event)
206
+ output.multi_receive([bad_event])
206
207
 
207
208
  bad_event.set('error', encoded_twice)
208
- output.receive(bad_event)
209
+ output.multi_receive([bad_event])
209
210
 
210
211
  expect(Dir.glob(File.join(path, "*")).size).to eq(2)
211
212
  output.close
@@ -218,7 +219,7 @@ describe LogStash::Outputs::File do
218
219
  output.register
219
220
 
220
221
  bad_event.set('error', '../..//test')
221
- output.receive(bad_event)
222
+ output.multi_receive([bad_event])
222
223
 
223
224
  expect(Dir.glob(File.join(path, "*")).size).to eq(1)
224
225
  output.close
@@ -235,7 +236,7 @@ describe LogStash::Outputs::File do
235
236
  config = { "path" => "#{path}/%{error}" }
236
237
  output = LogStash::Outputs::File.new(config)
237
238
  output.register
238
- output.receive(good_event)
239
+ output.multi_receive([good_event])
239
240
 
240
241
  good_file = File.join(path, good_event.get('error'))
241
242
  expect(File.exist?(good_file)).to eq(true)
@@ -254,7 +255,7 @@ describe LogStash::Outputs::File do
254
255
  config = { "path" => dynamic_path }
255
256
  output = LogStash::Outputs::File.new(config)
256
257
  output.register
257
- output.receive(good_event)
258
+ output.multi_receive([good_event])
258
259
 
259
260
  expect(File.exist?(expected_path)).to eq(true)
260
261
  output.close
@@ -276,7 +277,7 @@ describe LogStash::Outputs::File do
276
277
 
277
278
  output = LogStash::Outputs::File.new(config)
278
279
  output.register
279
- output.receive(good_event)
280
+ output.multi_receive([good_event])
280
281
 
281
282
  expect(File.exist?(expected_path)).to eq(true)
282
283
  output.close
@@ -291,7 +292,7 @@ describe LogStash::Outputs::File do
291
292
  config = { "path" => "#{path}/%{error}" }
292
293
  output = LogStash::Outputs::File.new(config)
293
294
  output.register
294
- output.receive(good_event)
295
+ output.multi_receive([good_event])
295
296
 
296
297
  good_file = File.join(path, good_event.get('error'))
297
298
  expect(File.exist?(good_file)).to eq(true)
@@ -310,7 +311,7 @@ describe LogStash::Outputs::File do
310
311
  config = { "path" => "#{path}/output.txt" }
311
312
  output = LogStash::Outputs::File.new(config)
312
313
  output.register
313
- output.receive(good_event)
314
+ output.multi_receive([good_event])
314
315
  good_file = File.join(path, 'output.txt')
315
316
  expect(File.exist?(good_file)).to eq(true)
316
317
  output.close #teardown first to allow reading the file
@@ -328,30 +329,9 @@ describe LogStash::Outputs::File do
328
329
 
329
330
  Stud::Temporary.directory do |path|
330
331
  config = { "path" => "#{path}/output.txt" }
331
- output = LogStash::Outputs::File.new(config)
332
- output.codec = LogStash::Codecs::Line.new({ "format" => "Custom format: %{message}"})
333
- output.register
334
- output.receive(good_event)
335
- good_file = File.join(path, 'output.txt')
336
- expect(File.exist?(good_file)).to eq(true)
337
- output.close #teardown first to allow reading the file
338
- File.open(good_file) {|f|
339
- line = f.readline
340
- expect(line).to eq("Custom format: hello world\n")
341
- }
342
- end
343
- end
344
- end
345
- context "when using deprecated message_format config" do
346
- it 'falls back to line codec' do
347
- good_event = LogStash::Event.new
348
- good_event.set('message', 'hello world')
349
-
350
- Stud::Temporary.directory do |path|
351
- config = { "path" => "#{path}/output.txt", "message_format" => "Custom format: %{message}" }
352
- output = LogStash::Outputs::File.new(config)
332
+ output = LogStash::Outputs::File.new(config.merge("codec" => LogStash::Codecs::Line.new({ "format" => "Custom format: %{message}"})))
353
333
  output.register
354
- output.receive(good_event)
334
+ output.multi_receive([good_event])
355
335
  good_file = File.join(path, 'output.txt')
356
336
  expect(File.exist?(good_file)).to eq(true)
357
337
  output.close #teardown first to allow reading the file
@@ -375,7 +355,7 @@ describe LogStash::Outputs::File do
375
355
  }
376
356
  output = LogStash::Outputs::File.new(config)
377
357
  output.register
378
- output.receive(good_event)
358
+ output.multi_receive([good_event])
379
359
  good_file = File.join(path, 'is/nested/output.txt')
380
360
  expect(File.exist?(good_file)).to eq(true)
381
361
  expect(File.stat(good_file).mode.to_s(8)[-3..-1]).to eq('610')
metadata CHANGED
@@ -1,22 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-file
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-14 00:00:00.000000000 Z
11
+ date: 2016-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '1.60'
19
- - - "<="
18
+ version: 2.0.0
19
+ - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
22
  name: logstash-core-plugin-api
@@ -26,8 +26,8 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '1.60'
30
- - - "<="
29
+ version: 2.0.0
30
+ - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2.99'
33
33
  - !ruby/object:Gem::Dependency
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  version: '0'
124
124
  requirements: []
125
125
  rubyforge_project:
126
- rubygems_version: 2.6.3
126
+ rubygems_version: 2.4.8
127
127
  signing_key:
128
128
  specification_version: 4
129
129
  summary: This output will write events to files on disk