logstash-input-file 4.0.5 → 4.1.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 +25 -3
- data/JAR_VERSION +1 -0
- data/docs/index.asciidoc +195 -37
- data/lib/filewatch/bootstrap.rb +74 -0
- data/lib/filewatch/discoverer.rb +94 -0
- data/lib/filewatch/helper.rb +65 -0
- data/lib/filewatch/observing_base.rb +97 -0
- data/lib/filewatch/observing_read.rb +23 -0
- data/lib/filewatch/observing_tail.rb +22 -0
- data/lib/filewatch/read_mode/handlers/base.rb +81 -0
- data/lib/filewatch/read_mode/handlers/read_file.rb +47 -0
- data/lib/filewatch/read_mode/handlers/read_zip_file.rb +57 -0
- data/lib/filewatch/read_mode/processor.rb +117 -0
- data/lib/filewatch/settings.rb +67 -0
- data/lib/filewatch/sincedb_collection.rb +215 -0
- data/lib/filewatch/sincedb_record_serializer.rb +70 -0
- data/lib/filewatch/sincedb_value.rb +87 -0
- data/lib/filewatch/tail_mode/handlers/base.rb +124 -0
- data/lib/filewatch/tail_mode/handlers/create.rb +17 -0
- data/lib/filewatch/tail_mode/handlers/create_initial.rb +21 -0
- data/lib/filewatch/tail_mode/handlers/delete.rb +11 -0
- data/lib/filewatch/tail_mode/handlers/grow.rb +11 -0
- data/lib/filewatch/tail_mode/handlers/shrink.rb +20 -0
- data/lib/filewatch/tail_mode/handlers/timeout.rb +10 -0
- data/lib/filewatch/tail_mode/handlers/unignore.rb +37 -0
- data/lib/filewatch/tail_mode/processor.rb +209 -0
- data/lib/filewatch/watch.rb +107 -0
- data/lib/filewatch/watched_file.rb +226 -0
- data/lib/filewatch/watched_files_collection.rb +84 -0
- data/lib/filewatch/winhelper.rb +65 -0
- data/lib/jars/filewatch-1.0.0.jar +0 -0
- data/lib/logstash/inputs/delete_completed_file_handler.rb +9 -0
- data/lib/logstash/inputs/file.rb +162 -107
- data/lib/logstash/inputs/file_listener.rb +61 -0
- data/lib/logstash/inputs/log_completed_file_handler.rb +13 -0
- data/logstash-input-file.gemspec +5 -4
- data/spec/filewatch/buftok_spec.rb +24 -0
- data/spec/filewatch/reading_spec.rb +128 -0
- data/spec/filewatch/sincedb_record_serializer_spec.rb +71 -0
- data/spec/filewatch/spec_helper.rb +120 -0
- data/spec/filewatch/tailing_spec.rb +440 -0
- data/spec/filewatch/watched_file_spec.rb +38 -0
- data/spec/filewatch/watched_files_collection_spec.rb +73 -0
- data/spec/filewatch/winhelper_spec.rb +22 -0
- data/spec/fixtures/compressed.log.gz +0 -0
- data/spec/fixtures/compressed.log.gzip +0 -0
- data/spec/fixtures/invalid_utf8.gbk.log +2 -0
- data/spec/fixtures/no-final-newline.log +2 -0
- data/spec/fixtures/uncompressed.log +2 -0
- data/spec/{spec_helper.rb → helpers/spec_helper.rb} +14 -41
- data/spec/inputs/file_read_spec.rb +155 -0
- data/spec/inputs/{file_spec.rb → file_tail_spec.rb} +55 -52
- metadata +96 -28
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch
|
4
|
+
# Tracks the position and expiry of the offset of a file-of-interest
|
5
|
+
class SincedbValue
|
6
|
+
attr_reader :last_changed_at, :watched_file, :path_in_sincedb
|
7
|
+
|
8
|
+
def initialize(position, last_changed_at = nil, watched_file = nil)
|
9
|
+
@position = position # this is the value read from disk
|
10
|
+
@last_changed_at = last_changed_at
|
11
|
+
@watched_file = watched_file
|
12
|
+
touch if @last_changed_at.nil? || @last_changed_at.zero?
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_path_in_sincedb(path)
|
16
|
+
@path_in_sincedb = path # can be nil
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def last_changed_at_expires(duration)
|
21
|
+
@last_changed_at + duration
|
22
|
+
end
|
23
|
+
|
24
|
+
def position
|
25
|
+
# either the value from disk or the current wf position
|
26
|
+
@watched_file.nil? ? @position : @watched_file.bytes_read
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_position(pos)
|
30
|
+
touch
|
31
|
+
if @watched_file.nil?
|
32
|
+
@position = pos
|
33
|
+
else
|
34
|
+
@watched_file.update_bytes_read(pos)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment_position(pos)
|
39
|
+
touch
|
40
|
+
if watched_file.nil?
|
41
|
+
@position += pos
|
42
|
+
else
|
43
|
+
@watched_file.increment_bytes_read(pos)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_watched_file(watched_file)
|
48
|
+
touch
|
49
|
+
@watched_file = watched_file
|
50
|
+
end
|
51
|
+
|
52
|
+
def touch
|
53
|
+
@last_changed_at = Time.now.to_f
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
# consider serializing the watched_file state as well
|
58
|
+
"#{position} #{last_changed_at}".tap do |s|
|
59
|
+
if @watched_file.nil?
|
60
|
+
s.concat(" ").concat(@path_in_sincedb) unless @path_in_sincedb.nil?
|
61
|
+
else
|
62
|
+
s.concat(" ").concat(@watched_file.path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear_watched_file
|
68
|
+
@watched_file = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def unset_watched_file
|
72
|
+
# cache the position
|
73
|
+
# we don't cache the path here because we know we are done with this file.
|
74
|
+
# either due via the `delete` handling
|
75
|
+
# or when read mode is done with a file.
|
76
|
+
# in the case of `delete` if the file was renamed then @watched_file is the
|
77
|
+
# watched_file of the previous path and the new path will be discovered and
|
78
|
+
# it should have the same inode as before.
|
79
|
+
# The key from the new watched_file should then locate this entry and we
|
80
|
+
# can resume from the cached position
|
81
|
+
return if @watched_file.nil?
|
82
|
+
wf = @watched_file
|
83
|
+
@watched_file = nil
|
84
|
+
@position = wf.bytes_read
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/util/loggable"
|
3
|
+
|
4
|
+
module FileWatch module TailMode module Handlers
|
5
|
+
class Base
|
6
|
+
include LogStash::Util::Loggable
|
7
|
+
attr_reader :sincedb_collection
|
8
|
+
|
9
|
+
def initialize(sincedb_collection, observer, settings)
|
10
|
+
@settings = settings
|
11
|
+
@sincedb_collection = sincedb_collection
|
12
|
+
@observer = observer
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle(watched_file)
|
16
|
+
logger.debug("handling: #{watched_file.path}")
|
17
|
+
unless watched_file.has_listener?
|
18
|
+
watched_file.set_listener(@observer)
|
19
|
+
end
|
20
|
+
handle_specifically(watched_file)
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_specifically(watched_file)
|
24
|
+
# some handlers don't need to define this method
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_existing_specifically(watched_file, sincedb_value)
|
28
|
+
# when a handler subclass does not implement this then do nothing
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def read_to_eof(watched_file)
|
34
|
+
changed = false
|
35
|
+
# from a real config (has 102 file inputs)
|
36
|
+
# -- This cfg creates a file input for every log file to create a dedicated file pointer and read all file simultaneously
|
37
|
+
# -- If we put all log files in one file input glob we will have indexing delay, because Logstash waits until the first file becomes EOF
|
38
|
+
# by allowing the user to specify a combo of `file_chunk_count` X `file_chunk_size`...
|
39
|
+
# we enable the pseudo parallel processing of each file.
|
40
|
+
# user also has the option to specify a low `stat_interval` and a very high `discover_interval`to respond
|
41
|
+
# quicker to changing files and not allowing too much content to build up before reading it.
|
42
|
+
@settings.file_chunk_count.times do
|
43
|
+
begin
|
44
|
+
data = watched_file.file_read(@settings.file_chunk_size)
|
45
|
+
lines = watched_file.buffer_extract(data)
|
46
|
+
logger.warn("read_to_eof: no delimiter found in current chunk") if lines.empty?
|
47
|
+
changed = true
|
48
|
+
lines.each do |line|
|
49
|
+
watched_file.listener.accept(line)
|
50
|
+
sincedb_collection.increment(watched_file.sincedb_key, line.bytesize + @settings.delimiter_byte_size)
|
51
|
+
end
|
52
|
+
rescue EOFError
|
53
|
+
# it only makes sense to signal EOF in "read" mode not "tail"
|
54
|
+
break
|
55
|
+
rescue Errno::EWOULDBLOCK, Errno::EINTR
|
56
|
+
watched_file.listener.error
|
57
|
+
break
|
58
|
+
rescue => e
|
59
|
+
logger.error("read_to_eof: general error reading #{watched_file.path}", "error" => e.inspect, "backtrace" => e.backtrace.take(4))
|
60
|
+
watched_file.listener.error
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
sincedb_collection.request_disk_flush if changed
|
65
|
+
end
|
66
|
+
|
67
|
+
def open_file(watched_file)
|
68
|
+
return true if watched_file.file_open?
|
69
|
+
logger.debug("opening #{watched_file.path}")
|
70
|
+
begin
|
71
|
+
watched_file.open
|
72
|
+
rescue
|
73
|
+
# don't emit this message too often. if a file that we can't
|
74
|
+
# read is changing a lot, we'll try to open it more often, and spam the logs.
|
75
|
+
now = Time.now.to_i
|
76
|
+
logger.warn("open_file OPEN_WARN_INTERVAL is '#{OPEN_WARN_INTERVAL}'")
|
77
|
+
if watched_file.last_open_warning_at.nil? || now - watched_file.last_open_warning_at > OPEN_WARN_INTERVAL
|
78
|
+
logger.warn("failed to open #{watched_file.path}: #{$!.inspect}, #{$!.backtrace.take(3)}")
|
79
|
+
watched_file.last_open_warning_at = now
|
80
|
+
else
|
81
|
+
logger.debug("suppressed warning for `failed to open` #{watched_file.path}: #{$!.inspect}")
|
82
|
+
end
|
83
|
+
watched_file.watch # set it back to watch so we can try it again
|
84
|
+
end
|
85
|
+
if watched_file.file_open?
|
86
|
+
watched_file.listener.opened
|
87
|
+
true
|
88
|
+
else
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_or_update_sincedb_collection(watched_file)
|
94
|
+
sincedb_value = @sincedb_collection.find(watched_file)
|
95
|
+
if sincedb_value.nil?
|
96
|
+
add_new_value_sincedb_collection(watched_file)
|
97
|
+
elsif sincedb_value.watched_file == watched_file
|
98
|
+
update_existing_sincedb_collection_value(watched_file, sincedb_value)
|
99
|
+
else
|
100
|
+
logger.warn? && logger.warn("mismatch on sincedb_value.watched_file, this should have been handled by Discoverer")
|
101
|
+
end
|
102
|
+
watched_file.initial_completed
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_existing_sincedb_collection_value(watched_file, sincedb_value)
|
106
|
+
logger.debug("update_existing_sincedb_collection_value: #{watched_file.path}, last value #{sincedb_value.position}, cur size #{watched_file.last_stat_size}")
|
107
|
+
update_existing_specifically(watched_file, sincedb_value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_new_value_sincedb_collection(watched_file)
|
111
|
+
sincedb_value = get_new_value_specifically(watched_file)
|
112
|
+
logger.debug("add_new_value_sincedb_collection: #{watched_file.path}", "position" => sincedb_value.position)
|
113
|
+
sincedb_collection.set(watched_file.sincedb_key, sincedb_value)
|
114
|
+
end
|
115
|
+
|
116
|
+
def get_new_value_specifically(watched_file)
|
117
|
+
position = @settings.start_new_files_at == :beginning ? 0 : watched_file.last_stat_size
|
118
|
+
value = SincedbValue.new(position)
|
119
|
+
value.set_watched_file(watched_file)
|
120
|
+
watched_file.update_bytes_read(position)
|
121
|
+
value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end end end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class Create < Base
|
5
|
+
def handle_specifically(watched_file)
|
6
|
+
if open_file(watched_file)
|
7
|
+
add_or_update_sincedb_collection(watched_file) unless sincedb_collection.member?(watched_file.sincedb_key)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_existing_specifically(watched_file, sincedb_value)
|
12
|
+
# sincedb_value is the source of truth
|
13
|
+
position = sincedb_value.position
|
14
|
+
watched_file.update_bytes_read(position)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end end end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class CreateInitial < Base
|
5
|
+
def handle_specifically(watched_file)
|
6
|
+
if open_file(watched_file)
|
7
|
+
add_or_update_sincedb_collection(watched_file)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_existing_specifically(watched_file, sincedb_value)
|
12
|
+
position = watched_file.last_stat_size
|
13
|
+
if @settings.start_new_files_at == :beginning
|
14
|
+
position = 0
|
15
|
+
end
|
16
|
+
logger.debug("update_existing_specifically - #{watched_file.path}: seeking to #{position}")
|
17
|
+
watched_file.update_bytes_read(position)
|
18
|
+
sincedb_value.update_position(position)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end end end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class Delete < Base
|
5
|
+
def handle_specifically(watched_file)
|
6
|
+
watched_file.listener.deleted
|
7
|
+
sincedb_collection.unset_watched_file(watched_file)
|
8
|
+
watched_file.file_close
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end end end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class Grow < Base
|
5
|
+
def handle_specifically(watched_file)
|
6
|
+
watched_file.file_seek(watched_file.bytes_read)
|
7
|
+
logger.debug("reading to eof: #{watched_file.path}")
|
8
|
+
read_to_eof(watched_file)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end end end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class Shrink < Base
|
5
|
+
def handle_specifically(watched_file)
|
6
|
+
add_or_update_sincedb_collection(watched_file)
|
7
|
+
watched_file.file_seek(watched_file.bytes_read)
|
8
|
+
logger.debug("reading to eof: #{watched_file.path}")
|
9
|
+
read_to_eof(watched_file)
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_existing_specifically(watched_file, sincedb_value)
|
13
|
+
# we have a match but size is smaller
|
14
|
+
# set all to zero
|
15
|
+
logger.debug("update_existing_specifically: #{watched_file.path}: was truncated seeking to beginning")
|
16
|
+
watched_file.update_bytes_read(0) if watched_file.bytes_read != 0
|
17
|
+
sincedb_value.update_position(0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end end end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FileWatch module TailMode module Handlers
|
4
|
+
class Unignore < Base
|
5
|
+
# a watched file can be put straight into the ignored state
|
6
|
+
# before any other handling has been done
|
7
|
+
# at a minimum we create or associate a sincedb value
|
8
|
+
def handle_specifically(watched_file)
|
9
|
+
add_or_update_sincedb_collection(watched_file) unless sincedb_collection.member?(watched_file.sincedb_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_new_value_specifically(watched_file)
|
13
|
+
# for file initially ignored their bytes_read was set to stat.size
|
14
|
+
# use this value not the `start_new_files_at` for the position
|
15
|
+
# logger.debug("get_new_value_specifically", "watched_file" => watched_file.inspect)
|
16
|
+
SincedbValue.new(watched_file.bytes_read).tap do |val|
|
17
|
+
val.set_watched_file(watched_file)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_existing_specifically(watched_file, sincedb_value)
|
22
|
+
# when this watched_file was ignored it had it bytes_read set to eof
|
23
|
+
# now the file has changed (watched_file.size_changed?)
|
24
|
+
# it has been put into the watched state so when it becomes active
|
25
|
+
# we will handle grow or shrink
|
26
|
+
# for now we seek to where we were before the file got ignored (grow)
|
27
|
+
# or to the start (shrink)
|
28
|
+
position = 0
|
29
|
+
if watched_file.shrunk?
|
30
|
+
watched_file.update_bytes_read(0)
|
31
|
+
else
|
32
|
+
position = watched_file.bytes_read
|
33
|
+
end
|
34
|
+
sincedb_value.update_position(position)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end end end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/util/loggable"
|
3
|
+
require_relative "handlers/base"
|
4
|
+
require_relative "handlers/create_initial"
|
5
|
+
require_relative "handlers/create"
|
6
|
+
require_relative "handlers/delete"
|
7
|
+
require_relative "handlers/grow"
|
8
|
+
require_relative "handlers/shrink"
|
9
|
+
require_relative "handlers/timeout"
|
10
|
+
require_relative "handlers/unignore"
|
11
|
+
|
12
|
+
module FileWatch module TailMode
|
13
|
+
# Must handle
|
14
|
+
# :create_initial - file is discovered and we have no record of it in the sincedb
|
15
|
+
# :create - file is discovered and we have seen it before in the sincedb
|
16
|
+
# :grow - file has more content
|
17
|
+
# :shrink - file has less content
|
18
|
+
# :delete - file can't be read
|
19
|
+
# :timeout - file is closable
|
20
|
+
# :unignore - file was ignored, but have now received new content
|
21
|
+
class Processor
|
22
|
+
include LogStash::Util::Loggable
|
23
|
+
|
24
|
+
attr_reader :watch, :deletable_filepaths
|
25
|
+
|
26
|
+
def initialize(settings)
|
27
|
+
@settings = settings
|
28
|
+
@deletable_filepaths = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_watch(watch)
|
32
|
+
@watch = watch
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize_handlers(sincedb_collection, observer)
|
37
|
+
@create_initial = Handlers::CreateInitial.new(sincedb_collection, observer, @settings)
|
38
|
+
@create = Handlers::Create.new(sincedb_collection, observer, @settings)
|
39
|
+
@grow = Handlers::Grow.new(sincedb_collection, observer, @settings)
|
40
|
+
@shrink = Handlers::Shrink.new(sincedb_collection, observer, @settings)
|
41
|
+
@delete = Handlers::Delete.new(sincedb_collection, observer, @settings)
|
42
|
+
@timeout = Handlers::Timeout.new(sincedb_collection, observer, @settings)
|
43
|
+
@unignore = Handlers::Unignore.new(sincedb_collection, observer, @settings)
|
44
|
+
end
|
45
|
+
|
46
|
+
def create(watched_file)
|
47
|
+
@create.handle(watched_file)
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_initial(watched_file)
|
51
|
+
@create_initial.handle(watched_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
def grow(watched_file)
|
55
|
+
@grow.handle(watched_file)
|
56
|
+
end
|
57
|
+
|
58
|
+
def shrink(watched_file)
|
59
|
+
@shrink.handle(watched_file)
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(watched_file)
|
63
|
+
@delete.handle(watched_file)
|
64
|
+
end
|
65
|
+
|
66
|
+
def timeout(watched_file)
|
67
|
+
@timeout.handle(watched_file)
|
68
|
+
end
|
69
|
+
|
70
|
+
def unignore(watched_file)
|
71
|
+
@unignore.handle(watched_file)
|
72
|
+
end
|
73
|
+
|
74
|
+
def process_closed(watched_files)
|
75
|
+
logger.debug("Closed processing")
|
76
|
+
# Handles watched_files in the closed state.
|
77
|
+
# if its size changed it is put into the watched state
|
78
|
+
watched_files.select {|wf| wf.closed? }.each do |watched_file|
|
79
|
+
path = watched_file.path
|
80
|
+
begin
|
81
|
+
watched_file.restat
|
82
|
+
if watched_file.size_changed?
|
83
|
+
# if the closed file changed, move it to the watched state
|
84
|
+
# not to active state because we want to respect the active files window.
|
85
|
+
watched_file.watch
|
86
|
+
end
|
87
|
+
rescue Errno::ENOENT
|
88
|
+
# file has gone away or we can't read it anymore.
|
89
|
+
common_deleted_reaction(watched_file, "Closed")
|
90
|
+
rescue => e
|
91
|
+
common_error_reaction(path, e, "Closed")
|
92
|
+
end
|
93
|
+
break if watch.quit?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def process_ignored(watched_files)
|
98
|
+
logger.debug("Ignored processing")
|
99
|
+
# Handles watched_files in the ignored state.
|
100
|
+
# if its size changed:
|
101
|
+
# put it in the watched state
|
102
|
+
# invoke unignore
|
103
|
+
watched_files.select {|wf| wf.ignored? }.each do |watched_file|
|
104
|
+
path = watched_file.path
|
105
|
+
begin
|
106
|
+
watched_file.restat
|
107
|
+
if watched_file.size_changed?
|
108
|
+
watched_file.watch
|
109
|
+
unignore(watched_file)
|
110
|
+
end
|
111
|
+
rescue Errno::ENOENT
|
112
|
+
# file has gone away or we can't read it anymore.
|
113
|
+
common_deleted_reaction(watched_file, "Ignored")
|
114
|
+
rescue => e
|
115
|
+
common_error_reaction(path, e, "Ignored")
|
116
|
+
end
|
117
|
+
break if watch.quit?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def process_watched(watched_files)
|
122
|
+
logger.debug("Watched processing")
|
123
|
+
# Handles watched_files in the watched state.
|
124
|
+
# for a slice of them:
|
125
|
+
# move to the active state
|
126
|
+
# and we allow the block to open the file and create a sincedb collection record if needed
|
127
|
+
# some have never been active and some have
|
128
|
+
# those that were active before but are watched now were closed under constraint
|
129
|
+
|
130
|
+
# how much of the max active window is available
|
131
|
+
to_take = @settings.max_active - watched_files.count{|wf| wf.active?}
|
132
|
+
if to_take > 0
|
133
|
+
watched_files.select {|wf| wf.watched?}.take(to_take).each do |watched_file|
|
134
|
+
path = watched_file.path
|
135
|
+
begin
|
136
|
+
watched_file.restat
|
137
|
+
watched_file.activate
|
138
|
+
if watched_file.initial?
|
139
|
+
create_initial(watched_file)
|
140
|
+
else
|
141
|
+
create(watched_file)
|
142
|
+
end
|
143
|
+
rescue Errno::ENOENT
|
144
|
+
# file has gone away or we can't read it anymore.
|
145
|
+
common_deleted_reaction(watched_file, "Watched")
|
146
|
+
rescue => e
|
147
|
+
common_error_reaction(path, e, "Watched")
|
148
|
+
end
|
149
|
+
break if watch.quit?
|
150
|
+
end
|
151
|
+
else
|
152
|
+
now = Time.now.to_i
|
153
|
+
if (now - watch.lastwarn_max_files) > MAX_FILES_WARN_INTERVAL
|
154
|
+
waiting = watched_files.size - @settings.max_active
|
155
|
+
logger.warn(@settings.max_warn_msg + ", files yet to open: #{waiting}")
|
156
|
+
watch.lastwarn_max_files = now
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def process_active(watched_files)
|
162
|
+
logger.debug("Active processing")
|
163
|
+
# Handles watched_files in the active state.
|
164
|
+
# it has been read once - unless they were empty at the time
|
165
|
+
watched_files.select {|wf| wf.active? }.each do |watched_file|
|
166
|
+
path = watched_file.path
|
167
|
+
begin
|
168
|
+
watched_file.restat
|
169
|
+
rescue Errno::ENOENT
|
170
|
+
# file has gone away or we can't read it anymore.
|
171
|
+
common_deleted_reaction(watched_file, "Active")
|
172
|
+
next
|
173
|
+
rescue => e
|
174
|
+
common_error_reaction(path, e, "Active")
|
175
|
+
next
|
176
|
+
end
|
177
|
+
break if watch.quit?
|
178
|
+
if watched_file.grown?
|
179
|
+
logger.debug("Active - file grew: #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
|
180
|
+
grow(watched_file)
|
181
|
+
elsif watched_file.shrunk?
|
182
|
+
# we don't update the size here, its updated when we actually read
|
183
|
+
logger.debug("Active - file shrunk #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
|
184
|
+
shrink(watched_file)
|
185
|
+
else
|
186
|
+
# same size, do nothing
|
187
|
+
end
|
188
|
+
# can any active files be closed to make way for waiting files?
|
189
|
+
if watched_file.file_closable?
|
190
|
+
logger.debug("Watch each: active: file expired: #{path}")
|
191
|
+
timeout(watched_file)
|
192
|
+
watched_file.close
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def common_deleted_reaction(watched_file, action)
|
198
|
+
# file has gone away or we can't read it anymore.
|
199
|
+
watched_file.unwatch
|
200
|
+
delete(watched_file)
|
201
|
+
deletable_filepaths << watched_file.path
|
202
|
+
logger.debug("#{action} - stat failed: #{watched_file.path}, removing from collection")
|
203
|
+
end
|
204
|
+
|
205
|
+
def common_error_reaction(path, error, action)
|
206
|
+
logger.error("#{action} - other error #{path}: (#{error.message}, #{error.backtrace.take(8).inspect})")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end end
|