logstash-input-file 4.1.16 → 4.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/LICENSE +199 -10
- data/docs/index.asciidoc +23 -7
- data/lib/filewatch/discoverer.rb +9 -8
- data/lib/filewatch/observing_base.rb +1 -12
- data/lib/filewatch/processor.rb +55 -0
- data/lib/filewatch/read_mode/handlers/base.rb +8 -6
- data/lib/filewatch/read_mode/handlers/read_file.rb +26 -8
- data/lib/filewatch/read_mode/handlers/read_zip_file.rb +63 -34
- data/lib/filewatch/read_mode/processor.rb +22 -36
- data/lib/filewatch/settings.rb +3 -2
- data/lib/filewatch/sincedb_collection.rb +23 -21
- data/lib/filewatch/stat/generic.rb +8 -13
- data/lib/filewatch/stat/windows_path.rb +7 -9
- data/lib/filewatch/tail_mode/handlers/delete.rb +2 -4
- data/lib/filewatch/tail_mode/processor.rb +47 -54
- data/lib/filewatch/watch.rb +12 -14
- data/lib/filewatch/watched_file.rb +25 -14
- data/lib/filewatch/watched_files_collection.rb +11 -74
- data/lib/jars/filewatch-1.0.1.jar +0 -0
- data/lib/logstash/inputs/delete_completed_file_handler.rb +5 -0
- data/lib/logstash/inputs/file.rb +32 -11
- data/logstash-input-file.gemspec +3 -2
- data/spec/filewatch/reading_spec.rb +60 -9
- data/spec/filewatch/rotate_spec.rb +2 -1
- data/spec/filewatch/settings_spec.rb +3 -0
- data/spec/filewatch/spec_helper.rb +13 -15
- data/spec/filewatch/tailing_spec.rb +14 -12
- data/spec/filewatch/watched_file_spec.rb +30 -0
- data/spec/filewatch/watched_files_collection_spec.rb +62 -8
- data/spec/helpers/spec_helper.rb +8 -0
- data/spec/inputs/file_read_spec.rb +154 -4
- data/spec/inputs/file_tail_spec.rb +3 -2
- metadata +21 -6
@@ -19,16 +19,21 @@ module FileWatch module ReadMode module Handlers
|
|
19
19
|
watched_file.listener.eof
|
20
20
|
watched_file.file_close
|
21
21
|
key = watched_file.sincedb_key
|
22
|
-
sincedb_collection.
|
23
|
-
|
22
|
+
if sincedb_collection.get(key)
|
23
|
+
sincedb_collection.reading_completed(key)
|
24
|
+
sincedb_collection.clear_watched_file(key)
|
25
|
+
end
|
24
26
|
watched_file.listener.deleted
|
27
|
+
# NOTE: on top of un-watching we should also remove from the watched files collection
|
28
|
+
# if the file is getting deleted (on completion), that part currently resides in
|
29
|
+
# DeleteCompletedFileHandler - triggered above using `watched_file.listener.deleted`
|
25
30
|
watched_file.unwatch
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
35
|
def controlled_read(watched_file, loop_control)
|
31
|
-
logger.trace("reading...",
|
36
|
+
logger.trace? && logger.trace("reading...", :filename => watched_file.filename, :iterations => loop_control.count, :amount => loop_control.size)
|
32
37
|
loop_control.count.times do
|
33
38
|
break if quit?
|
34
39
|
begin
|
@@ -40,22 +45,35 @@ module FileWatch module ReadMode module Handlers
|
|
40
45
|
delta = line.bytesize + @settings.delimiter_byte_size
|
41
46
|
sincedb_collection.increment(watched_file.sincedb_key, delta)
|
42
47
|
end
|
43
|
-
rescue EOFError
|
44
|
-
|
48
|
+
rescue EOFError => e
|
49
|
+
log_error("controlled_read: eof error reading file", watched_file, e)
|
45
50
|
loop_control.flag_read_error
|
46
51
|
break
|
47
|
-
rescue Errno::EWOULDBLOCK, Errno::EINTR
|
48
|
-
|
52
|
+
rescue Errno::EWOULDBLOCK, Errno::EINTR => e
|
53
|
+
log_error("controlled_read: block or interrupt error reading file", watched_file, e)
|
49
54
|
watched_file.listener.error
|
50
55
|
loop_control.flag_read_error
|
51
56
|
break
|
52
57
|
rescue => e
|
53
|
-
|
58
|
+
log_error("controlled_read: general error reading file", watched_file, e)
|
54
59
|
watched_file.listener.error
|
55
60
|
loop_control.flag_read_error
|
56
61
|
break
|
57
62
|
end
|
58
63
|
end
|
59
64
|
end
|
65
|
+
|
66
|
+
def log_error(msg, watched_file, error)
|
67
|
+
details = { :path => watched_file.path,
|
68
|
+
:exception => error.class,
|
69
|
+
:message => error.message,
|
70
|
+
:backtrace => error.backtrace }
|
71
|
+
if logger.debug?
|
72
|
+
details[:file] = watched_file
|
73
|
+
else
|
74
|
+
details[:backtrace] = details[:backtrace].take(8) if details[:backtrace]
|
75
|
+
end
|
76
|
+
logger.error(msg, details)
|
77
|
+
end
|
60
78
|
end
|
61
79
|
end end end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'java'
|
3
|
-
java_import java.io.InputStream
|
4
|
-
java_import java.io.InputStreamReader
|
5
|
-
java_import java.io.FileInputStream
|
6
|
-
java_import java.io.BufferedReader
|
7
|
-
java_import java.util.zip.GZIPInputStream
|
8
|
-
java_import java.util.zip.ZipException
|
9
3
|
|
10
4
|
module FileWatch module ReadMode module Handlers
|
5
|
+
|
6
|
+
java_import java.io.InputStream
|
7
|
+
java_import java.io.InputStreamReader
|
8
|
+
java_import java.io.FileInputStream
|
9
|
+
java_import java.io.BufferedReader
|
10
|
+
java_import java.util.zip.GZIPInputStream
|
11
|
+
java_import java.util.zip.ZipException
|
12
|
+
|
11
13
|
class ReadZipFile < Base
|
12
14
|
def handle_specifically(watched_file)
|
13
15
|
add_or_update_sincedb_collection(watched_file) unless sincedb_collection.member?(watched_file.sincedb_key)
|
@@ -18,34 +20,40 @@ module FileWatch module ReadMode module Handlers
|
|
18
20
|
# fast forward through the lines until we reach unseen content?
|
19
21
|
# meaning that we can quit in the middle of a zip file
|
20
22
|
key = watched_file.sincedb_key
|
21
|
-
|
22
|
-
|
23
|
-
gzip_stream = GZIPInputStream.new(file_stream)
|
24
|
-
decoder = InputStreamReader.new(gzip_stream, "UTF-8")
|
25
|
-
buffered = BufferedReader.new(decoder)
|
26
|
-
while (line = buffered.readLine(false))
|
27
|
-
watched_file.listener.accept(line)
|
28
|
-
# can't quit, if we did then we would incorrectly write a 'completed' sincedb entry
|
29
|
-
# what do we do about quit when we have just begun reading the zipped file (e.g. pipeline reloading)
|
30
|
-
# should we track lines read in the sincedb and
|
31
|
-
# fast forward through the lines until we reach unseen content?
|
32
|
-
# meaning that we can quit in the middle of a zip file
|
33
|
-
end
|
34
|
-
watched_file.listener.eof
|
35
|
-
rescue ZipException => e
|
36
|
-
logger.error("Cannot decompress the gzip file at path: #{watched_file.path}")
|
37
|
-
watched_file.listener.error
|
38
|
-
else
|
39
|
-
sincedb_collection.store_last_read(key, watched_file.last_stat_size)
|
40
|
-
sincedb_collection.request_disk_flush
|
41
|
-
watched_file.listener.deleted
|
23
|
+
|
24
|
+
if @settings.check_archive_validity && corrupted?(watched_file)
|
42
25
|
watched_file.unwatch
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
file_stream = FileInputStream.new(watched_file.path)
|
29
|
+
gzip_stream = GZIPInputStream.new(file_stream)
|
30
|
+
decoder = InputStreamReader.new(gzip_stream, "UTF-8")
|
31
|
+
buffered = BufferedReader.new(decoder)
|
32
|
+
while (line = buffered.readLine(false))
|
33
|
+
watched_file.listener.accept(line)
|
34
|
+
# can't quit, if we did then we would incorrectly write a 'completed' sincedb entry
|
35
|
+
# what do we do about quit when we have just begun reading the zipped file (e.g. pipeline reloading)
|
36
|
+
# should we track lines read in the sincedb and
|
37
|
+
# fast forward through the lines until we reach unseen content?
|
38
|
+
# meaning that we can quit in the middle of a zip file
|
39
|
+
end
|
40
|
+
watched_file.listener.eof
|
41
|
+
rescue ZipException => e
|
42
|
+
logger.error("Cannot decompress the gzip file at path: #{watched_file.path}", :exception => e.class,
|
43
|
+
:message => e.message, :backtrace => e.backtrace)
|
44
|
+
watched_file.listener.error
|
45
|
+
else
|
46
|
+
sincedb_collection.store_last_read(key, watched_file.last_stat_size)
|
47
|
+
sincedb_collection.request_disk_flush
|
48
|
+
watched_file.listener.deleted
|
49
|
+
watched_file.unwatch
|
50
|
+
ensure
|
51
|
+
# rescue each close individually so all close attempts are tried
|
52
|
+
close_and_ignore_ioexception(buffered) unless buffered.nil?
|
53
|
+
close_and_ignore_ioexception(decoder) unless decoder.nil?
|
54
|
+
close_and_ignore_ioexception(gzip_stream) unless gzip_stream.nil?
|
55
|
+
close_and_ignore_ioexception(file_stream) unless file_stream.nil?
|
56
|
+
end
|
49
57
|
end
|
50
58
|
sincedb_collection.clear_watched_file(key)
|
51
59
|
end
|
@@ -56,7 +64,28 @@ module FileWatch module ReadMode module Handlers
|
|
56
64
|
begin
|
57
65
|
closeable.close
|
58
66
|
rescue Exception => e # IOException can be thrown by any of the Java classes that implement the Closable interface.
|
59
|
-
logger.warn("Ignoring an IOException when closing an instance of #{closeable.class.name}",
|
67
|
+
logger.warn("Ignoring an IOException when closing an instance of #{closeable.class.name}",
|
68
|
+
:exception => e.class, :message => e.message, :backtrace => e.backtrace)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def corrupted?(watched_file)
|
73
|
+
begin
|
74
|
+
file_stream = FileInputStream.new(watched_file.path)
|
75
|
+
gzip_stream = GZIPInputStream.new(file_stream)
|
76
|
+
buffer = Java::byte[8192].new
|
77
|
+
start = Time.new
|
78
|
+
until gzip_stream.read(buffer) == -1
|
79
|
+
end
|
80
|
+
return false
|
81
|
+
rescue ZipException => e
|
82
|
+
duration = Time.now - start
|
83
|
+
logger.warn("Detected corrupted archive #{watched_file.path} file won't be processed", :message => e.message,
|
84
|
+
:duration => duration.round(3))
|
85
|
+
return true
|
86
|
+
ensure
|
87
|
+
close_and_ignore_ioexception(gzip_stream) unless gzip_stream.nil?
|
88
|
+
close_and_ignore_ioexception(file_stream) unless file_stream.nil?
|
60
89
|
end
|
61
90
|
end
|
62
91
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
3
|
-
|
2
|
+
require 'filewatch/processor'
|
4
3
|
require_relative "handlers/base"
|
5
4
|
require_relative "handlers/read_file"
|
6
5
|
require_relative "handlers/read_zip_file"
|
@@ -9,20 +8,7 @@ module FileWatch module ReadMode
|
|
9
8
|
# Must handle
|
10
9
|
# :read_file
|
11
10
|
# :read_zip_file
|
12
|
-
class Processor
|
13
|
-
include LogStash::Util::Loggable
|
14
|
-
|
15
|
-
attr_reader :watch, :deletable_filepaths
|
16
|
-
|
17
|
-
def initialize(settings)
|
18
|
-
@settings = settings
|
19
|
-
@deletable_filepaths = []
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_watch(watch)
|
23
|
-
@watch = watch
|
24
|
-
self
|
25
|
-
end
|
11
|
+
class Processor < FileWatch::Processor
|
26
12
|
|
27
13
|
def initialize_handlers(sincedb_collection, observer)
|
28
14
|
# we deviate from the tail mode handler initialization here
|
@@ -48,24 +34,23 @@ module FileWatch module ReadMode
|
|
48
34
|
private
|
49
35
|
|
50
36
|
def process_watched(watched_files)
|
51
|
-
logger.trace(
|
37
|
+
logger.trace(__method__.to_s)
|
52
38
|
# Handles watched_files in the watched state.
|
53
39
|
# for a slice of them:
|
54
40
|
# move to the active state
|
55
41
|
# should never have been active before
|
56
42
|
# how much of the max active window is available
|
57
|
-
to_take = @settings.max_active - watched_files.count{|wf| wf.active?}
|
43
|
+
to_take = @settings.max_active - watched_files.count { |wf| wf.active? }
|
58
44
|
if to_take > 0
|
59
|
-
watched_files.select
|
60
|
-
path = watched_file.path
|
45
|
+
watched_files.select(&:watched?).take(to_take).each do |watched_file|
|
61
46
|
begin
|
62
|
-
watched_file
|
47
|
+
restat(watched_file)
|
63
48
|
watched_file.activate
|
64
49
|
rescue Errno::ENOENT
|
65
|
-
common_deleted_reaction(watched_file,
|
50
|
+
common_deleted_reaction(watched_file, __method__)
|
66
51
|
next
|
67
52
|
rescue => e
|
68
|
-
common_error_reaction(
|
53
|
+
common_error_reaction(watched_file, e, __method__)
|
69
54
|
next
|
70
55
|
end
|
71
56
|
break if watch.quit?
|
@@ -74,7 +59,7 @@ module FileWatch module ReadMode
|
|
74
59
|
now = Time.now.to_i
|
75
60
|
if (now - watch.lastwarn_max_files) > MAX_FILES_WARN_INTERVAL
|
76
61
|
waiting = watched_files.size - @settings.max_active
|
77
|
-
logger.warn(@settings.max_warn_msg
|
62
|
+
logger.warn("#{@settings.max_warn_msg}, files yet to open: #{waiting}")
|
78
63
|
watch.lastwarn_max_files = now
|
79
64
|
end
|
80
65
|
end
|
@@ -83,17 +68,18 @@ module FileWatch module ReadMode
|
|
83
68
|
## TODO add process_rotation_in_progress
|
84
69
|
|
85
70
|
def process_active(watched_files)
|
86
|
-
logger.trace(
|
71
|
+
logger.trace(__method__.to_s)
|
87
72
|
# Handles watched_files in the active state.
|
88
|
-
watched_files.
|
89
|
-
|
73
|
+
watched_files.each do |watched_file|
|
74
|
+
next unless watched_file.active?
|
75
|
+
|
90
76
|
begin
|
91
|
-
watched_file
|
77
|
+
restat(watched_file)
|
92
78
|
rescue Errno::ENOENT
|
93
|
-
common_deleted_reaction(watched_file,
|
79
|
+
common_deleted_reaction(watched_file, __method__)
|
94
80
|
next
|
95
81
|
rescue => e
|
96
|
-
common_error_reaction(
|
82
|
+
common_error_reaction(watched_file, e, __method__)
|
97
83
|
next
|
98
84
|
end
|
99
85
|
break if watch.quit?
|
@@ -114,19 +100,19 @@ module FileWatch module ReadMode
|
|
114
100
|
def common_detach_when_allread(watched_file)
|
115
101
|
watched_file.unwatch
|
116
102
|
watched_file.listener.reading_completed
|
117
|
-
|
118
|
-
logger.trace("
|
103
|
+
add_deletable_path watched_file.path
|
104
|
+
logger.trace? && logger.trace("whole file read, removing from collection", :path => watched_file.path)
|
119
105
|
end
|
120
106
|
|
121
107
|
def common_deleted_reaction(watched_file, action)
|
122
108
|
# file has gone away or we can't read it anymore.
|
123
109
|
watched_file.unwatch
|
124
|
-
|
125
|
-
logger.trace("#{action} - stat failed
|
110
|
+
add_deletable_path watched_file.path
|
111
|
+
logger.trace? && logger.trace("#{action} - stat failed, removing from collection", :path => watched_file.path)
|
126
112
|
end
|
127
113
|
|
128
|
-
def common_error_reaction(
|
129
|
-
logger.error("#{action} - other error
|
114
|
+
def common_error_reaction(watched_file, error, action)
|
115
|
+
logger.error("#{action} - other error", error_details(error, watched_file))
|
130
116
|
end
|
131
117
|
end
|
132
118
|
end end
|
data/lib/filewatch/settings.rb
CHANGED
@@ -6,9 +6,10 @@ module FileWatch
|
|
6
6
|
attr_reader :max_active, :max_warn_msg, :lastwarn_max_files
|
7
7
|
attr_reader :sincedb_write_interval, :stat_interval, :discover_interval
|
8
8
|
attr_reader :exclude, :start_new_files_at, :file_chunk_count, :file_chunk_size
|
9
|
-
attr_reader :sincedb_path, :
|
9
|
+
attr_reader :sincedb_path, :sincedb_expiry_duration
|
10
10
|
attr_reader :file_sort_by, :file_sort_direction
|
11
11
|
attr_reader :exit_after_read
|
12
|
+
attr_reader :check_archive_validity
|
12
13
|
|
13
14
|
def self.from_options(opts)
|
14
15
|
new.add_options(opts)
|
@@ -40,7 +41,6 @@ module FileWatch
|
|
40
41
|
@file_chunk_size = @opts[:file_chunk_size]
|
41
42
|
@close_older = @opts[:close_older]
|
42
43
|
@ignore_older = @opts[:ignore_older]
|
43
|
-
@sincedb_write_interval = @opts[:sincedb_write_interval]
|
44
44
|
@stat_interval = @opts[:stat_interval]
|
45
45
|
@discover_interval = @opts[:discover_interval]
|
46
46
|
@exclude = Array(@opts[:exclude])
|
@@ -52,6 +52,7 @@ module FileWatch
|
|
52
52
|
@file_sort_by = @opts[:file_sort_by]
|
53
53
|
@file_sort_direction = @opts[:file_sort_direction]
|
54
54
|
@exit_after_read = @opts[:exit_after_read]
|
55
|
+
@check_archive_validity = @opts[:check_archive_validity]
|
55
56
|
self
|
56
57
|
end
|
57
58
|
|
@@ -56,12 +56,12 @@ module FileWatch
|
|
56
56
|
logger.trace("open: count of keys read: #{@sincedb.keys.size}")
|
57
57
|
rescue => e
|
58
58
|
#No existing sincedb to load
|
59
|
-
logger.trace("open: error:
|
59
|
+
logger.trace("open: error:", :path => path, :exception => e.class, :message => e.message)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
def associate(watched_file)
|
64
|
-
logger.trace("associate: finding",
|
64
|
+
logger.trace? && logger.trace("associate: finding", :path => watched_file.path, :inode => watched_file.sincedb_key.inode)
|
65
65
|
sincedb_value = find(watched_file)
|
66
66
|
if sincedb_value.nil?
|
67
67
|
# sincedb has no record of this inode
|
@@ -71,7 +71,8 @@ module FileWatch
|
|
71
71
|
logger.trace("associate: unmatched")
|
72
72
|
return true
|
73
73
|
end
|
74
|
-
logger.trace("associate: found sincedb record",
|
74
|
+
logger.trace? && logger.trace("associate: found sincedb record", :filename => watched_file.filename,
|
75
|
+
:sincedb_key => watched_file.sincedb_key, :sincedb_value => sincedb_value)
|
75
76
|
if sincedb_value.watched_file.nil?
|
76
77
|
# not associated
|
77
78
|
if sincedb_value.path_in_sincedb.nil?
|
@@ -106,7 +107,8 @@ module FileWatch
|
|
106
107
|
# after the original is deleted
|
107
108
|
# are not yet in the delete phase, let this play out
|
108
109
|
existing_watched_file = sincedb_value.watched_file
|
109
|
-
logger.trace("----------------- >> associate: the found sincedb_value has a watched_file - this is a rename",
|
110
|
+
logger.trace? && logger.trace("----------------- >> associate: the found sincedb_value has a watched_file - this is a rename",
|
111
|
+
:this_watched_file => watched_file.details, :existing_watched_file => existing_watched_file.details)
|
110
112
|
watched_file.rotation_in_progress
|
111
113
|
true
|
112
114
|
end
|
@@ -149,8 +151,8 @@ module FileWatch
|
|
149
151
|
end
|
150
152
|
|
151
153
|
def watched_file_deleted(watched_file)
|
152
|
-
|
153
|
-
|
154
|
+
value = @sincedb[watched_file.sincedb_key]
|
155
|
+
value.unset_watched_file if value
|
154
156
|
end
|
155
157
|
|
156
158
|
def store_last_read(key, pos)
|
@@ -178,24 +180,24 @@ module FileWatch
|
|
178
180
|
get(key).watched_file.nil?
|
179
181
|
end
|
180
182
|
|
181
|
-
private
|
182
|
-
|
183
183
|
def flush_at_interval
|
184
|
-
now = Time.now
|
185
|
-
delta = now - @sincedb_last_write
|
184
|
+
now = Time.now
|
185
|
+
delta = now.to_i - @sincedb_last_write
|
186
186
|
if delta >= @settings.sincedb_write_interval
|
187
187
|
logger.debug("writing sincedb (delta since last write = #{delta})")
|
188
188
|
sincedb_write(now)
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
|
+
private
|
193
|
+
|
192
194
|
def handle_association(sincedb_value, watched_file)
|
193
195
|
watched_file.update_bytes_read(sincedb_value.position)
|
194
196
|
sincedb_value.set_watched_file(watched_file)
|
195
197
|
watched_file.initial_completed
|
196
198
|
if watched_file.all_read?
|
197
199
|
watched_file.ignore
|
198
|
-
logger.trace("handle_association fully read, ignoring.....",
|
200
|
+
logger.trace? && logger.trace("handle_association fully read, ignoring.....", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
|
199
201
|
end
|
200
202
|
end
|
201
203
|
|
@@ -208,33 +210,33 @@ module FileWatch
|
|
208
210
|
end
|
209
211
|
end
|
210
212
|
|
211
|
-
def sincedb_write(time = Time.now
|
212
|
-
logger.trace("sincedb_write:
|
213
|
+
def sincedb_write(time = Time.now)
|
214
|
+
logger.trace("sincedb_write: #{path} (time = #{time})")
|
213
215
|
begin
|
214
|
-
@write_method.call
|
216
|
+
@write_method.call(time)
|
215
217
|
@serializer.expired_keys.each do |key|
|
216
218
|
@sincedb[key].unset_watched_file
|
217
219
|
delete(key)
|
218
|
-
logger.trace("sincedb_write: cleaned",
|
220
|
+
logger.trace? && logger.trace("sincedb_write: cleaned", :key => key)
|
219
221
|
end
|
220
|
-
@sincedb_last_write = time
|
222
|
+
@sincedb_last_write = time.to_i
|
221
223
|
@write_requested = false
|
222
224
|
rescue Errno::EACCES
|
223
225
|
# no file handles free perhaps
|
224
226
|
# maybe it will work next time
|
225
|
-
logger.trace("sincedb_write:
|
227
|
+
logger.trace("sincedb_write: #{path} error: #{$!}")
|
226
228
|
end
|
227
229
|
end
|
228
230
|
|
229
|
-
def atomic_write
|
231
|
+
def atomic_write(time)
|
230
232
|
FileHelper.write_atomically(@full_path) do |io|
|
231
|
-
@serializer.serialize(@sincedb, io)
|
233
|
+
@serializer.serialize(@sincedb, io, time.to_f)
|
232
234
|
end
|
233
235
|
end
|
234
236
|
|
235
|
-
def non_atomic_write
|
237
|
+
def non_atomic_write(time)
|
236
238
|
IO.open(IO.sysopen(@full_path, "w+")) do |io|
|
237
|
-
@serializer.serialize(@sincedb, io)
|
239
|
+
@serializer.serialize(@sincedb, io, time.to_f)
|
238
240
|
end
|
239
241
|
end
|
240
242
|
end
|
@@ -3,24 +3,19 @@
|
|
3
3
|
module FileWatch module Stat
|
4
4
|
class Generic
|
5
5
|
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :inode, :modified_at, :size, :inode_struct
|
7
7
|
|
8
8
|
def initialize(source)
|
9
|
-
@source = source
|
10
|
-
@identifier = nil
|
9
|
+
@source = source # Pathname
|
11
10
|
restat
|
12
11
|
end
|
13
12
|
|
14
|
-
def add_identifier(identifier) self; end
|
15
|
-
|
16
13
|
def restat
|
17
|
-
|
18
|
-
@inode =
|
19
|
-
@modified_at =
|
20
|
-
@size =
|
21
|
-
@
|
22
|
-
@dev_minor = @inner_stat.dev_minor
|
23
|
-
@inode_struct = InodeStruct.new(@inode, @dev_major, @dev_minor)
|
14
|
+
stat = @source.stat
|
15
|
+
@inode = stat.ino.to_s
|
16
|
+
@modified_at = stat.mtime.to_f
|
17
|
+
@size = stat.size
|
18
|
+
@inode_struct = InodeStruct.new(@inode, stat.dev_major, stat.dev_minor)
|
24
19
|
end
|
25
20
|
|
26
21
|
def windows?
|
@@ -28,7 +23,7 @@ module FileWatch module Stat
|
|
28
23
|
end
|
29
24
|
|
30
25
|
def inspect
|
31
|
-
"
|
26
|
+
"<#{self.class.name} size=#{@size}, modified_at=#{@modified_at}, inode='#{@inode}', inode_struct=#{@inode_struct}>"
|
32
27
|
end
|
33
28
|
end
|
34
29
|
end end
|