logstash-output-file 3.0.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|