Skip to content

Commit 6ca7f0e

Browse files
committed
in_tail: fix NameError in flush_buffer with emit_unmatched_lines
**Which issue(s) this PR fixes**: Fixes # **What this PR does / why we need it**: TailInput#flush_buffer referenced an undefined local variable `tail_watcher` instead of its argument `tw`, so it raised `NameError: undefined local variable or method 'tail_watcher'` when an incomplete buffered line is flushed and the parser returns no record while `emit_unmatched_lines` is enabled. This can happen with parser plugins which don't implement the `unmatched_lines` feature, because in_tail only emits the buffered line as `unmatched_line` in that case. Add a regression test which flushes a buffered line with such a parser and checks that `unmatched_line` and `path_key` are emitted. **Docs Changes**: None **Release Note**: in_tail: fix NameError raised when flushing buffered lines with emit_unmatched_lines true Co-Authored-By: Qwen3.8-Flash-Next Signed-off-by: Takuro Ashie <ashie@clear-code.com>
1 parent f727da5 commit 6ca7f0e

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

‎lib/fluent/plugin/in_tail.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ def flush_buffer(tw, buf)
706706
else
707707
if @emit_unmatched_lines
708708
record = { 'unmatched_line' => buf }
709-
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
709+
record[@path_key] ||= tw.path unless @path_key.nil?
710710
tag = if @tag_prefix || @tag_suffix
711711
@tag_prefix + tw.tag + @tag_suffix
712712
else

‎test/plugin/test_in_tail.rb‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ def common_follow_inode_config
110110
})
111111
])
112112

113+
# A parser which never matches, like parser plugins which don't implement
114+
# the `unmatched_lines` feature. Such parsers yield (nil, nil) to the caller.
115+
class NeverMatchingParser
116+
def parse(text, &block)
117+
block.call(nil, nil)
118+
nil
119+
end
120+
end
121+
113122
EX_ROTATE_WAIT = 0
114123
EX_FOLLOW_INODES = false
115124

@@ -1310,6 +1319,32 @@ def test_multiline_with_emit_unmatched_lines_true(data)
13101319
assert_equal({"message1" => "test8"}, events[4][2])
13111320
end
13121321

1322+
data(flat: MULTILINE_CONFIG,
1323+
parse: PARSE_MULTILINE_CONFIG)
1324+
def test_multiline_flush_buffer_with_emit_unmatched_lines_and_path_key(data)
1325+
config = data + config_element("", "", { "emit_unmatched_lines" => true, "path_key" => "path" })
1326+
Fluent::FileWrapper.open("#{@tmp_dir}/tail.txt", "wb") { |f| }
1327+
1328+
d = create_driver(config)
1329+
d.run(expect_emits: 1) do
1330+
plugin = d.instance
1331+
# `unmatched_lines` is a feature of each parser plugin, so a parser
1332+
# which doesn't implement it yields (nil, nil) even if
1333+
# `emit_unmatched_lines` is enabled. in_tail must emit the buffered
1334+
# line as `unmatched_line` in that case.
1335+
plugin.instance_variable_set(:@parser, NeverMatchingParser.new)
1336+
tw = Fluent::Plugin::TailInput::TailWatcher.new(
1337+
create_target_info("#{@tmp_dir}/tail.txt"), nil, $log, true, false, nil, nil, nil, nil
1338+
)
1339+
plugin.flush_buffer(tw, "incomplete line\n")
1340+
end
1341+
1342+
events = d.events
1343+
assert_equal(1, events.length)
1344+
assert_equal("t1", events[0][0])
1345+
assert_equal({ "unmatched_line" => "incomplete line", "path" => "#{@tmp_dir}/tail.txt" }, events[0][2])
1346+
end
1347+
13131348
data(
13141349
flat: MULTILINE_CONFIG_WITH_NEWLINE,
13151350
parse: PARSE_MULTILINE_CONFIG_WITH_NEWLINE)

0 commit comments

Comments
 (0)