Skip to content

in_tail: support recursive directory monitoring#11452

Closed
courageJ wants to merge 1 commit intofluent:masterfrom
courageJ:in_tail_recursive_dir
Closed

in_tail: support recursive directory monitoring#11452
courageJ wants to merge 1 commit intofluent:masterfrom
courageJ:in_tail_recursive_dir

Conversation

@courageJ
Copy link
Contributor

@courageJ courageJ commented Feb 11, 2026

This PR introduces recursive directory monitoring support for the Tail input plugin.

Key changes:

  • Added 'path_recursion' configuration option (default: false).
  • Implemented recursive directory scanning for POSIX (opendir/readdir).
  • Implemented recursive directory scanning for Windows (FindFirstFile/FindNextFile).
  • Added protection against symlinked directory recursion loops.
  • Added unit tests to verify recursion behavior.

Summary by CodeRabbit

  • New Features

    • Added optional recursive directory scanning to the Tail input via the path_recursion setting (boolean, default false).
    • Supports nested directories on Linux/Unix and Windows; processes symlinked files while skipping symlinked directories.
    • Improved log messages for invalid/too-long paths and skipped entries during scans.
  • Tests

    • Added runtime tests covering directory recursion enabled/disabled and symlink behavior to validate expected record counts.

This commit introduces recursive directory monitoring support for the
Tail input plugin.

Key changes:
- Added 'path_recursion' configuration option (default: false).
- Implemented recursive directory scanning for POSIX (opendir/readdir).
- Implemented recursive directory scanning for Windows (FindFirstFile/FindNextFile).
- Added protection against symlinked directory recursion loops.
- Added unit tests to verify recursion behavior.

Signed-off-by: Gemini CLI <gemini-cli@google.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

This PR introduces recursive directory scanning support to the tail plugin through a new path_recursion configuration option. Changes span configuration declarations, recursive directory traversal implementations for both Unix and Windows platforms, and comprehensive test coverage for the feature.

Changes

Cohort / File(s) Summary
Configuration Definition
plugins/in_tail/tail_config.h, plugins/in_tail/tail_config.c, plugins/in_tail/tail.c
Added new recursive boolean field to flb_tail_config structure with initialization to FLB_FALSE and corresponding path_recursion config map entry.
Unix/Linux Implementation
plugins/in_tail/tail_scan_glob.c
Introduced tail_process_file helper for centralized per-file validation; added tail_scan_directory for recursive directory enumeration with symlink-aware handling; refactored tail_scan_path to delegate recursive traversal when enabled.
Windows Implementation
plugins/in_tail/tail_scan_win32.c
Added tail_scan_directory for recursive Windows directory traversal with reparse-point filtering; extended tail_scan_path to invoke recursive scanning for directory paths when recursion is enabled.
Test Coverage
tests/runtime/in_tail.c
Added three test cases: flb_test_in_tail_directory_recursion (recursion enabled), flb_test_in_tail_directory_recursion_off (recursion disabled), and flb_test_in_tail_directory_recursion_symlink (symlink handling).

Sequence Diagram

sequenceDiagram
    participant Cfg as Configuration
    participant Scan as tail_scan_path
    participant Dir as tail_scan_directory
    participant File as tail_process_file
    participant List as File List

    Cfg->>Scan: path + recursive=true
    alt Path is Directory
        Scan->>Dir: invoke recursive scan
        Dir->>Dir: iterate directory entries
        alt Entry is Regular File
            Dir->>File: validate file
            File->>File: check blacklist, ignore_older
            File->>List: append file
        else Entry is Symlink to File
            Dir->>File: validate symlink target
            File->>List: append file
        else Entry is Directory
            Dir->>Dir: recurse into subdirectory
        else Entry is Symlink to Directory
            Dir->>Dir: skip (avoid loops)
        end
    else Path is File
        Scan->>File: validate file
        File->>List: append file
    else Path has Wildcards
        Scan->>List: glob matching
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • edsiper
  • fujimotos

Poem

🐰 Hop, skip, jump through directories deep,
Recursion's magic makes logs we keep,
No symlink loops, just files so fine,
Nested folders—now they're all mine!
Windows, Unix, together they'll play,
Tail paths recursive, hooray, hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding recursive directory monitoring support to the in_tail plugin, with a reference to the fixed issue.
Linked Issues check ✅ Passed The PR successfully implements recursive directory scanning for both POSIX and Windows, adds the path_recursion configuration option, and includes tests covering enabled/disabled recursion and symlink handling scenarios.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing recursive directory monitoring: configuration fields, scanning logic for POSIX and Windows, and comprehensive unit tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41d6ae271a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


mkdir(dir, 0777);
mkdir(target_dir, 0777);
symlink(target_dir, sym_dir); /* Create symlink to directory */

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point symlink to the actual target directory

The symlink test currently creates link_dir with symlink(target_dir, sym_dir), but target_dir is a relative path ("recursion_test_sym_target") while sym_dir is inside recursion_test_sym_dir; this makes the link resolve to recursion_test_sym_dir/recursion_test_sym_target (nonexistent) instead of the intended sibling directory. As a result, the test can pass without exercising the symlinked-directory traversal behavior it is meant to validate, so regressions in symlink recursion handling may go undetected.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/in_tail/tail_scan_win32.c (1)

304-335: ⚠️ Potential issue | 🟡 Minor

Add tail_signal_manager call after registering files in Win32 implementation.

The glob version (tail_scan_glob.c line 396–398) calls tail_signal_manager(ctx) when files are registered in tail_scan_path(). The Win32 version (tail_scan_win32.c) lacks this call entirely, meaning newly discovered files via recursive directory scans won't signal the processing thread until the next refresh cycle.

Add tail_signal_manager(ctx) in tail_scan_path() after tail_scan_directory() returns (when count > 0), and also after tail_register_file() in the non-directory case.

🤖 Fix all issues with AI agents
In `@tests/runtime/in_tail.c`:
- Line 2845: The test creates a relative symlink that points to a non-existent
path, so the "skip symlinked directory" behavior isn't exercised; update the
symlink creation in tests/runtime/in_tail.c by changing the symlink target used
in the symlink(...) call (currently using "recursion_test_sym_target" for the
link at "recursion_test_sym_dir/link_dir") to a path that actually resolves from
the link's parent directory (e.g. "../recursion_test_sym_target") so the kernel
resolves the link to recursion_test_sym_dir/recursion_test_sym_target and the
code path in your directory-recursion logic that skips symlinked directories is
properly tested.
🧹 Nitpick comments (3)
plugins/in_tail/tail_scan_win32.c (1)

250-302: tail_scan_directory lacks a recursion depth limit.

Deeply nested directory trees (or edge cases with mount points) could cause stack overflow. The POSIX counterpart in tail_scan_glob.c has the same issue. Consider adding a max depth parameter to guard against pathological directory structures.

plugins/in_tail/tail_scan_glob.c (1)

253-320: Recursive traversal has no depth limit — same as the Win32 counterpart.

A pathological or very deep directory tree could exhaust the stack. Consider adding a max_depth parameter (e.g., 64) and decrementing on each recursive call. This applies to both tail_scan_glob.c and tail_scan_win32.c.

Sketch of depth-limited recursion
-static int tail_scan_directory(const char *dir, struct flb_tail_config *ctx, time_t now)
+static int tail_scan_directory(const char *dir, struct flb_tail_config *ctx,
+                               time_t now, int depth)
 {
+    if (depth <= 0) {
+        flb_plg_warn(ctx->ins, "max recursion depth reached at: %s", dir);
+        return 0;
+    }
     ...
     else if (S_ISDIR(st.st_mode)) {
-        ret = tail_scan_directory(path, ctx, now);
+        ret = tail_scan_directory(path, ctx, now, depth - 1);
         ...
     }
tests/runtime/in_tail.c (1)

2733-2820: Verbose inline comments could be cleaned up, but test logic is sound.

Lines 2798–2807 contain extensive inline reasoning about expected behavior. While helpful during development, these comments are noisy for long-term maintenance. Consider trimming to a one-liner explaining the expected behavior.


mkdir(dir, 0777);
mkdir(target_dir, 0777);
symlink(target_dir, sym_dir); /* Create symlink to directory */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Broken symlink — test doesn't validate the intended behavior.

symlink("recursion_test_sym_target", "recursion_test_sym_dir/link_dir") creates a relative symlink. When resolved, the kernel computes the target relative to the link's parent directory: recursion_test_sym_dir/recursion_test_sym_target, which doesn't exist. The test still passes (expects 1 record) because stat() fails on the dangling link, not because the code correctly identifies and skips a symlinked directory.

Use ../recursion_test_sym_target so the symlink actually resolves and the "skip symlinked directory" logic is exercised:

Proposed fix
-    symlink(target_dir, sym_dir); /* Create symlink to directory */
+    symlink("../recursion_test_sym_target", sym_dir); /* Create symlink to directory */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
symlink(target_dir, sym_dir); /* Create symlink to directory */
symlink("../recursion_test_sym_target", sym_dir); /* Create symlink to directory */
🤖 Prompt for AI Agents
In `@tests/runtime/in_tail.c` at line 2845, The test creates a relative symlink
that points to a non-existent path, so the "skip symlinked directory" behavior
isn't exercised; update the symlink creation in tests/runtime/in_tail.c by
changing the symlink target used in the symlink(...) call (currently using
"recursion_test_sym_target" for the link at "recursion_test_sym_dir/link_dir")
to a path that actually resolves from the link's parent directory (e.g.
"../recursion_test_sym_target") so the kernel resolves the link to
recursion_test_sym_dir/recursion_test_sym_target and the code path in your
directory-recursion logic that skips symlinked directories is properly tested.

@courageJ courageJ closed this Feb 11, 2026
@courageJ courageJ changed the title in_tail: support recursive directory monitoring (Fix #2120) in_tail: support recursive directory monitoring Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant