Skip to content

Commit 871e40e

Browse files
authored
Refactor: improve debug logging (log catched exceptions) (#280)
tried to normalize traces involving `:filename` or `:path` to be more consistent - easier to grep. otherwise the intent is mostly (debug) logging exceptions the plugin's expected to rescue and continue running. particularly, I wasn't sure why a watched file keeps going through the read loop (having new content) but comes out as never reading anything on Windows (e.g. from here), knowing whether there's an EOF or file-access gone wrong would confirm whether there's a deeper issue in the plugin or not. have also raised a few traces to the debug level - ones that seem to provide more value than just tracing what the plugin is processing atm.
1 parent 20db56d commit 871e40e

File tree

12 files changed

+88
-94
lines changed

12 files changed

+88
-94
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 4.2.3
2+
- Refactor: improve debug logging (log catched exceptions) [#280](https://github.com/logstash-plugins/logstash-input-file/pull/280)
3+
14
## 4.2.2
25
- Fix: sincedb_clean_after not being respected [#276](https://github.com/logstash-plugins/logstash-input-file/pull/276)
36

Diff for: lib/filewatch/read_mode/handlers/base.rb

+7-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def quit?
1919
end
2020

2121
def handle(watched_file)
22-
logger.trace("handling: #{watched_file.path}")
22+
logger.trace? && logger.trace("handling:", :path => watched_file.path)
2323
unless watched_file.has_listener?
2424
watched_file.set_listener(@observer)
2525
end
@@ -41,14 +41,14 @@ def open_file(watched_file)
4141
# don't emit this message too often. if a file that we can't
4242
# read is changing a lot, we'll try to open it more often, and spam the logs.
4343
now = Time.now.to_i
44-
logger.trace("opening OPEN_WARN_INTERVAL is '#{OPEN_WARN_INTERVAL}'")
44+
logger.trace? && logger.trace("opening OPEN_WARN_INTERVAL is '#{OPEN_WARN_INTERVAL}'")
4545
if watched_file.last_open_warning_at.nil? || now - watched_file.last_open_warning_at > OPEN_WARN_INTERVAL
4646
backtrace = e.backtrace
4747
backtrace = backtrace.take(3) if backtrace && !logger.debug?
4848
logger.warn("failed to open", :path => watched_file.path, :exception => e.class, :message => e.message, :backtrace => backtrace)
4949
watched_file.last_open_warning_at = now
5050
else
51-
logger.trace("suppressed warning (failed to open)", :path => watched_file.path, :exception => e.class, :message => e.message)
51+
logger.trace? && logger.trace("suppressed warning (failed to open)", :path => watched_file.path, :exception => e.class, :message => e.message)
5252
end
5353
watched_file.watch # set it back to watch so we can try it again
5454
end
@@ -67,8 +67,7 @@ def add_or_update_sincedb_collection(watched_file)
6767
elsif sincedb_value.watched_file == watched_file
6868
update_existing_sincedb_collection_value(watched_file, sincedb_value)
6969
else
70-
msg = "add_or_update_sincedb_collection: the found sincedb_value has a watched_file - this is a rename, switching inode to this watched file"
71-
logger.trace(msg)
70+
logger.trace? && logger.trace("add_or_update_sincedb_collection: the found sincedb_value has a watched_file - this is a rename, switching inode to this watched file")
7271
existing_watched_file = sincedb_value.watched_file
7372
if existing_watched_file.nil?
7473
sincedb_value.set_watched_file(watched_file)
@@ -77,7 +76,7 @@ def add_or_update_sincedb_collection(watched_file)
7776
watched_file.update_bytes_read(sincedb_value.position)
7877
else
7978
sincedb_value.set_watched_file(watched_file)
80-
logger.trace("add_or_update_sincedb_collection: switching from", :watched_file => watched_file.details)
79+
logger.trace? && logger.trace("add_or_update_sincedb_collection: switching from", :watched_file => watched_file.details)
8180
watched_file.rotate_from(existing_watched_file)
8281
end
8382

@@ -86,15 +85,15 @@ def add_or_update_sincedb_collection(watched_file)
8685
end
8786

8887
def update_existing_sincedb_collection_value(watched_file, sincedb_value)
89-
logger.trace("update_existing_sincedb_collection_value: #{watched_file.path}, last value #{sincedb_value.position}, cur size #{watched_file.last_stat_size}")
88+
logger.trace? && logger.trace("update_existing_sincedb_collection_value: #{watched_file.path}, last value #{sincedb_value.position}, cur size #{watched_file.last_stat_size}")
9089
# sincedb_value is the source of truth
9190
watched_file.update_bytes_read(sincedb_value.position)
9291
end
9392

9493
def add_new_value_sincedb_collection(watched_file)
9594
sincedb_value = SincedbValue.new(0)
9695
sincedb_value.set_watched_file(watched_file)
97-
logger.trace("add_new_value_sincedb_collection:", :path => watched_file.path, :position => sincedb_value.position)
96+
logger.trace? && logger.trace("add_new_value_sincedb_collection:", :path => watched_file.path, :position => sincedb_value.position)
9897
sincedb_collection.set(watched_file.sincedb_key, sincedb_value)
9998
end
10099
end

Diff for: lib/filewatch/sincedb_collection.rb

+22-25
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@ def open
4747
@time_sdb_opened = Time.now.to_f
4848
begin
4949
path.open do |file|
50-
logger.trace("open: reading from #{path}")
50+
logger.debug("open: reading from #{path}")
5151
@serializer.deserialize(file) do |key, value|
52-
logger.trace("open: importing ... '#{key}' => '#{value}'")
52+
logger.trace? && logger.trace("open: importing #{key.inspect} => #{value.inspect}")
5353
set_key_value(key, value)
5454
end
5555
end
5656
logger.trace("open: count of keys read: #{@sincedb.keys.size}")
5757
rescue => e
5858
#No existing sincedb to load
59-
logger.trace("open: error:", :path => path, :exception => e.class, :message => e.message)
59+
logger.debug("open: error opening #{path}", :exception => e.class, :message => e.message)
6060
end
6161
end
6262

@@ -68,35 +68,32 @@ def associate(watched_file)
6868
# and due to the window handling of many files
6969
# this file may not be opened in this session.
7070
# a new value will be added when the file is opened
71-
logger.trace("associate: unmatched")
71+
logger.trace("associate: unmatched", :filename => watched_file.filename)
7272
return true
7373
end
7474
logger.trace? && logger.trace("associate: found sincedb record", :filename => watched_file.filename,
7575
:sincedb_key => watched_file.sincedb_key, :sincedb_value => sincedb_value)
76-
if sincedb_value.watched_file.nil?
77-
# not associated
76+
if sincedb_value.watched_file.nil? # not associated
7877
if sincedb_value.path_in_sincedb.nil?
7978
handle_association(sincedb_value, watched_file)
80-
logger.trace("associate: inode matched but no path in sincedb")
79+
logger.trace? && logger.trace("associate: inode matched but no path in sincedb", :filename => watched_file.filename)
8180
return true
8281
end
8382
if sincedb_value.path_in_sincedb == watched_file.path
84-
# the path on disk is the same as discovered path
85-
# and the inode is the same.
83+
# the path on disk is the same as discovered path and the inode is the same.
8684
handle_association(sincedb_value, watched_file)
87-
logger.trace("associate: inode and path matched")
85+
logger.trace? && logger.trace("associate: inode and path matched", :filename => watched_file.filename)
8886
return true
8987
end
90-
# the path on disk is different from discovered unassociated path
91-
# but they have the same key (inode)
88+
# the path on disk is different from discovered unassociated path but they have the same key (inode)
9289
# treat as a new file, a new value will be added when the file is opened
9390
sincedb_value.clear_watched_file
9491
delete(watched_file.sincedb_key)
95-
logger.trace("associate: matched but allocated to another")
92+
logger.trace? && logger.trace("associate: matched but allocated to another", :filename => watched_file.filename)
9693
return true
9794
end
9895
if sincedb_value.watched_file.equal?(watched_file) # pointer equals
99-
logger.trace("associate: already associated")
96+
logger.trace? && logger.trace("associate: already associated", :filename => watched_file.filename)
10097
return true
10198
end
10299
# sincedb_value.watched_file is not this discovered watched_file but they have the same key (inode)
@@ -107,7 +104,7 @@ def associate(watched_file)
107104
# after the original is deleted
108105
# are not yet in the delete phase, let this play out
109106
existing_watched_file = sincedb_value.watched_file
110-
logger.trace? && logger.trace("----------------- >> associate: the found sincedb_value has a watched_file - this is a rename",
107+
logger.trace? && logger.trace("associate: found sincedb_value has a watched_file - this is a rename",
111108
:this_watched_file => watched_file.details, :existing_watched_file => existing_watched_file.details)
112109
watched_file.rotation_in_progress
113110
true
@@ -197,43 +194,43 @@ def handle_association(sincedb_value, watched_file)
197194
watched_file.initial_completed
198195
if watched_file.all_read?
199196
watched_file.ignore
200-
logger.trace? && logger.trace("handle_association fully read, ignoring.....", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
197+
logger.trace? && logger.trace("handle_association fully read, ignoring", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
201198
end
202199
end
203200

204201
def set_key_value(key, value)
205202
if @time_sdb_opened < value.last_changed_at_expires(@settings.sincedb_expiry_duration)
206-
logger.trace("open: setting #{key.inspect} to #{value.inspect}")
207203
set(key, value)
208204
else
209-
logger.trace("open: record has expired, skipping: #{key.inspect} #{value.inspect}")
205+
logger.debug("set_key_value: record has expired, skipping: #{key.inspect} => #{value.inspect}")
210206
end
211207
end
212208

213209
def sincedb_write(time = Time.now)
214-
logger.trace("sincedb_write: #{path} (time = #{time})")
210+
logger.trace? && logger.trace("sincedb_write: #{path} (time = #{time})")
215211
begin
216-
@write_method.call(time)
217-
@serializer.expired_keys.each do |key|
212+
expired_keys = @write_method.call(time)
213+
expired_keys.each do |key|
218214
@sincedb[key].unset_watched_file
219215
delete(key)
220216
logger.trace? && logger.trace("sincedb_write: cleaned", :key => key)
221217
end
222218
@sincedb_last_write = time.to_i
223219
@write_requested = false
224-
rescue Errno::EACCES
225-
# no file handles free perhaps
226-
# maybe it will work next time
227-
logger.trace("sincedb_write: #{path} error: #{$!}")
220+
rescue Errno::EACCES => e
221+
# no file handles free perhaps - maybe it will work next time
222+
logger.debug("sincedb_write: #{path} error:", :exception => e.class, :message => e.message)
228223
end
229224
end
230225

226+
# @return expired keys
231227
def atomic_write(time)
232228
FileHelper.write_atomically(@full_path) do |io|
233229
@serializer.serialize(@sincedb, io, time.to_f)
234230
end
235231
end
236232

233+
# @return expired keys
237234
def non_atomic_write(time)
238235
IO.open(IO.sysopen(@full_path, "w+")) do |io|
239236
@serializer.serialize(@sincedb, io, time.to_f)

Diff for: lib/filewatch/sincedb_record_serializer.rb

+5-11
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,25 @@
33
module FileWatch
44
class SincedbRecordSerializer
55

6-
attr_reader :expired_keys
7-
86
def self.days_to_seconds(days)
97
(24 * 3600) * days.to_f
108
end
119

1210
def initialize(sincedb_value_expiry)
1311
@sincedb_value_expiry = sincedb_value_expiry
14-
@expired_keys = []
15-
end
16-
17-
def update_sincedb_value_expiry_from_days(days)
18-
@sincedb_value_expiry = SincedbRecordSerializer.days_to_seconds(days)
1912
end
2013

14+
# @return Array expired keys (ones that were not written to the file)
2115
def serialize(db, io, as_of = Time.now.to_f)
22-
@expired_keys.clear
16+
expired_keys = []
2317
db.each do |key, value|
2418
if as_of > value.last_changed_at_expires(@sincedb_value_expiry)
25-
@expired_keys << key
19+
expired_keys << key
2620
next
2721
end
2822
io.write(serialize_record(key, value))
2923
end
24+
expired_keys
3025
end
3126

3227
def deserialize(io)
@@ -36,8 +31,7 @@ def deserialize(io)
3631
end
3732

3833
def serialize_record(k, v)
39-
# effectively InodeStruct#to_s SincedbValue#to_s
40-
"#{k} #{v}\n"
34+
"#{k} #{v}\n" # effectively InodeStruct#to_s SincedbValue#to_s
4135
end
4236

4337
def deserialize_record(record)

Diff for: lib/filewatch/tail_mode/handlers/base.rb

+32-23
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def quit?
1818
end
1919

2020
def handle(watched_file)
21-
logger.trace("handling: #{watched_file.filename}")
21+
logger.trace? && logger.trace("handling:", :path => watched_file.path)
2222
unless watched_file.has_listener?
2323
watched_file.set_listener(@observer)
2424
end
@@ -37,7 +37,7 @@ def update_existing_specifically(watched_file, sincedb_value)
3737

3838
def controlled_read(watched_file, loop_control)
3939
changed = false
40-
logger.trace("reading...", "iterations" => loop_control.count, "amount" => loop_control.size, "filename" => watched_file.filename)
40+
logger.trace? && logger.trace(__method__.to_s, :iterations => loop_control.count, :amount => loop_control.size, :filename => watched_file.filename)
4141
# from a real config (has 102 file inputs)
4242
# -- This cfg creates a file input for every log file to create a dedicated file pointer and read all file simultaneously
4343
# -- 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
@@ -48,7 +48,7 @@ def controlled_read(watched_file, loop_control)
4848
loop_control.count.times do
4949
break if quit?
5050
begin
51-
logger.debug("read_to_eof: get chunk")
51+
logger.debug? && logger.debug("#{__method__} get chunk")
5252
result = watched_file.read_extract_lines(loop_control.size) # expect BufferExtractResult
5353
logger.trace(result.warning, result.additional) unless result.warning.empty?
5454
changed = true
@@ -57,40 +57,42 @@ def controlled_read(watched_file, loop_control)
5757
# sincedb position is now independent from the watched_file bytes_read
5858
sincedb_collection.increment(watched_file.sincedb_key, line.bytesize + @settings.delimiter_byte_size)
5959
end
60-
rescue EOFError
60+
rescue EOFError => e
6161
# it only makes sense to signal EOF in "read" mode not "tail"
62+
logger.debug(__method__.to_s, exception_details(watched_file.path, e, false))
6263
loop_control.flag_read_error
6364
break
64-
rescue Errno::EWOULDBLOCK, Errno::EINTR
65+
rescue Errno::EWOULDBLOCK, Errno::EINTR => e
66+
logger.debug(__method__.to_s, exception_details(watched_file.path, e, false))
6567
watched_file.listener.error
6668
loop_control.flag_read_error
6769
break
6870
rescue => e
69-
logger.error("read_to_eof: general error reading #{watched_file.path}", "error" => e.inspect, "backtrace" => e.backtrace.take(4))
71+
logger.error("#{__method__} general error reading", exception_details(watched_file.path, e))
7072
watched_file.listener.error
7173
loop_control.flag_read_error
7274
break
7375
end
7476
end
75-
logger.debug("read_to_eof: exit due to quit") if quit?
77+
logger.debug("#{__method__} stopped loop due quit") if quit?
7678
sincedb_collection.request_disk_flush if changed
7779
end
7880

7981
def open_file(watched_file)
8082
return true if watched_file.file_open?
81-
logger.trace("opening #{watched_file.filename}")
83+
logger.trace? && logger.trace("open_file", :filename => watched_file.filename)
8284
begin
8385
watched_file.open
84-
rescue
86+
rescue => e
8587
# don't emit this message too often. if a file that we can't
8688
# read is changing a lot, we'll try to open it more often, and spam the logs.
8789
now = Time.now.to_i
88-
logger.trace("open_file OPEN_WARN_INTERVAL is '#{OPEN_WARN_INTERVAL}'")
90+
logger.trace? && logger.trace("open_file OPEN_WARN_INTERVAL is '#{OPEN_WARN_INTERVAL}'")
8991
if watched_file.last_open_warning_at.nil? || now - watched_file.last_open_warning_at > OPEN_WARN_INTERVAL
90-
logger.warn("failed to open #{watched_file.path}: #{$!.inspect}, #{$!.backtrace.take(3)}")
92+
logger.warn("failed to open file", exception_details(watched_file.path, e))
9193
watched_file.last_open_warning_at = now
9294
else
93-
logger.trace("suppressed warning for `failed to open` #{watched_file.path}: #{$!.inspect}")
95+
logger.debug("open_file suppressed warning `failed to open file`", exception_details(watched_file.path, e, false))
9496
end
9597
watched_file.watch # set it back to watch so we can try it again
9698
else
@@ -108,40 +110,38 @@ def add_or_update_sincedb_collection(watched_file)
108110
update_existing_sincedb_collection_value(watched_file, sincedb_value)
109111
watched_file.initial_completed
110112
else
111-
msg = "add_or_update_sincedb_collection: found sincedb record"
112-
logger.trace(msg,
113-
"sincedb key" => watched_file.sincedb_key,
114-
"sincedb value" => sincedb_value
115-
)
113+
logger.trace? && logger.trace("add_or_update_sincedb_collection: found sincedb record",
114+
:sincedb_key => watched_file.sincedb_key, :sincedb_value => sincedb_value)
116115
# detected a rotation, Discoverer can't handle this because this watched file is not a new discovery.
117116
# we must handle it here, by transferring state and have the sincedb value track this watched file
118117
# rotate_as_file and rotate_from will switch the sincedb key to the inode that the path is now pointing to
119118
# and pickup the sincedb_value from before.
120-
msg = "add_or_update_sincedb_collection: the found sincedb_value has a watched_file - this is a rename, switching inode to this watched file"
121-
logger.trace(msg)
119+
logger.debug("add_or_update_sincedb_collection: the found sincedb_value has a watched_file - this is a rename, switching inode to this watched file")
122120
existing_watched_file = sincedb_value.watched_file
123121
if existing_watched_file.nil?
124122
sincedb_value.set_watched_file(watched_file)
125-
logger.trace("add_or_update_sincedb_collection: switching as new file")
123+
logger.trace? && logger.trace("add_or_update_sincedb_collection: switching as new file")
126124
watched_file.rotate_as_file
127125
watched_file.update_bytes_read(sincedb_value.position)
128126
else
129127
sincedb_value.set_watched_file(watched_file)
130-
logger.trace("add_or_update_sincedb_collection: switching from...", "watched_file details" => watched_file.details)
128+
logger.trace? && logger.trace("add_or_update_sincedb_collection: switching from:", :watched_file => watched_file.details)
131129
watched_file.rotate_from(existing_watched_file)
132130
end
133131
end
134132
sincedb_value
135133
end
136134

137135
def update_existing_sincedb_collection_value(watched_file, sincedb_value)
138-
logger.trace("update_existing_sincedb_collection_value: #{watched_file.filename}, last value #{sincedb_value.position}, cur size #{watched_file.last_stat_size}")
136+
logger.trace? && logger.trace("update_existing_sincedb_collection_value", :position => sincedb_value.position,
137+
:filename => watched_file.filename, :last_stat_size => watched_file.last_stat_size)
139138
update_existing_specifically(watched_file, sincedb_value)
140139
end
141140

142141
def add_new_value_sincedb_collection(watched_file)
143142
sincedb_value = get_new_value_specifically(watched_file)
144-
logger.trace("add_new_value_sincedb_collection", "position" => sincedb_value.position, "watched_file details" => watched_file.details)
143+
logger.trace? && logger.trace("add_new_value_sincedb_collection", :position => sincedb_value.position,
144+
:watched_file => watched_file.details)
145145
sincedb_collection.set(watched_file.sincedb_key, sincedb_value)
146146
sincedb_value
147147
end
@@ -153,5 +153,14 @@ def get_new_value_specifically(watched_file)
153153
watched_file.update_bytes_read(position)
154154
value
155155
end
156+
157+
private
158+
159+
def exception_details(path, e, trace = true)
160+
details = { :path => path, :exception => e.class, :message => e.message }
161+
details[:backtrace] = e.backtrace if trace && logger.debug?
162+
details
163+
end
164+
156165
end
157166
end end end

0 commit comments

Comments
 (0)