tail: add --debug flag to show follow implementation mode (#10105)

* tail: add --debug flag to show follow implementation mode

* tail: add tests for --debug flag output

* fix formatting
This commit is contained in:
Chris Dryden
2026-01-14 06:07:17 +01:00
committed by GitHub
parent 0b0ba60d41
commit d806231866
4 changed files with 61 additions and 0 deletions
+5
View File
@@ -6,6 +6,7 @@ tail-usage = tail [FLAG]... [FILE]...
# Help messages
tail-help-bytes = Number of bytes to print
tail-help-debug = indicate which --follow implementation is used
tail-help-follow = Print the file as it grows
tail-help-lines = Number of lines to print
tail-help-pid = With -f, terminate after process ID, PID dies
@@ -70,3 +71,7 @@ tail-giving-up-on-this-name = ; giving up on this name
tail-stdin-header = standard input
tail-no-files-remaining = no files remaining
tail-become-inaccessible = has become inaccessible
# Debug messages
tail-debug-using-notification-mode = using notification mode
tail-debug-using-polling-mode = using polling mode
+10
View File
@@ -38,6 +38,7 @@ pub mod options {
pub const MAX_UNCHANGED_STATS: &str = "max-unchanged-stats";
pub const ARG_FILES: &str = "files";
pub const PRESUME_INPUT_PIPE: &str = "-presume-input-pipe"; // NOTE: three hyphens is correct
pub const DEBUG: &str = "debug";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -139,6 +140,7 @@ pub struct Settings {
pub use_polling: bool,
pub verbose: bool,
pub presume_input_pipe: bool,
pub debug: bool,
/// `FILE(s)` positional arguments
pub inputs: Vec<Input>,
}
@@ -155,6 +157,7 @@ impl Default for Settings {
use_polling: Default::default(),
verbose: Default::default(),
presume_input_pipe: Default::default(),
debug: Default::default(),
inputs: Vec::default(),
}
}
@@ -223,6 +226,7 @@ impl Settings {
mode: FilterMode::from(matches)?,
verbose: matches.get_flag(options::verbosity::VERBOSE),
presume_input_pipe: matches.get_flag(options::PRESUME_INPUT_PIPE),
debug: matches.get_flag(options::DEBUG),
..Default::default()
};
@@ -543,6 +547,12 @@ pub fn uu_app() -> Command {
.overrides_with(options::FOLLOW_RETRY)
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DEBUG)
.long(options::DEBUG)
.help(translate!("tail-help-debug"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::PRESUME_INPUT_PIPE)
.long("presume-input-pipe")
+10
View File
@@ -72,6 +72,16 @@ fn uu_tail(settings: &Settings) -> UResult<()> {
let mut observer = Observer::from(settings);
observer.start(settings)?;
// Print debug info about the follow implementation being used
if settings.debug && settings.follow.is_some() {
if observer.use_polling {
show_error!("{}", translate!("tail-debug-using-polling-mode"));
} else {
show_error!("{}", translate!("tail-debug-using-notification-mode"));
}
}
// Do an initial tail print of each path's content.
// Add `path` and `reader` to `files` map if `--follow` is selected.
for input in &settings.inputs.clone() {
+36
View File
@@ -5065,3 +5065,39 @@ fn test_follow_stdout_pipe_close() {
child.close_stdout();
child.delay(2000).make_assertion().is_not_alive();
}
#[test]
fn test_debug_flag_with_polling() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.touch("f");
let mut child = ts
.ucmd()
.args(&["--debug", "-f", "--use-polling", "f"])
.run_no_wait();
child.make_assertion_with_delay(500).is_alive();
child
.kill()
.make_assertion()
.with_all_output()
.stderr_contains("tail: using polling mode");
}
#[test]
#[cfg(target_os = "linux")]
fn test_debug_flag_with_inotify() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.touch("f");
let mut child = ts.ucmd().args(&["--debug", "-f", "f"]).run_no_wait();
child.make_assertion_with_delay(500).is_alive();
child
.kill()
.make_assertion()
.with_all_output()
.stderr_contains("tail: using notification mode");
}