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 +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/logstash/outputs/file.rb +47 -30
- data/logstash-output-file.gemspec +2 -2
- data/spec/outputs/file_spec.rb +18 -38
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7519db3db3ff8bc30ac81db904b4141f518f326b
|
4
|
+
data.tar.gz: 4e0736f6ec2d53be5b3675ac0b2a3015d5a6ac6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fdd1d4e5edcb5025969a6298ec457d315386d345ebc961d36c34455ebfa1d0d4b7bc0c3791a83ac5a0dbadc674773fb73a6bb3981b442a42f03858f602d72bf
|
7
|
+
data.tar.gz: 2e357366f22791bd8024845f2ad6ee25e69bd06803cad29957d28c2bdc7899a9ef945bc2c61a123ef79fa4f449865548495ad78b51ef7b17634fc14acf6a0a0f
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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
|
129
|
-
|
130
|
-
|
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
|
-
@
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
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
|
-
|
163
|
-
|
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
|
-
|
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
|
-
|
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 = '
|
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", ">=
|
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
|
|
data/spec/outputs/file_spec.rb
CHANGED
@@ -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 =
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
206
|
+
output.multi_receive([bad_event])
|
206
207
|
|
207
208
|
bad_event.set('error', encoded_twice)
|
208
|
-
output.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
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-
|
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:
|
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:
|
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.
|
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
|