Skip to content

Conversation

@snakefoot
Copy link
Contributor

@snakefoot snakefoot commented Sep 5, 2025

Resolves #5986

@snakefoot snakefoot added this to the 6.0.4 milestone Sep 5, 2025
@snakefoot snakefoot added file-target file-archiving Issues with archiving with the file target labels Sep 5, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 5, 2025

Warning

Rate limit exceeded

@snakefoot has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 59 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between faa0c64 and 05522ae.

📒 Files selected for processing (4)
  • src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (8 hunks)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1 hunks)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1 hunks)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs (2 hunks)

Walkthrough

Adds a configurable archiveSuffixWithSeqNo flag to DeleteOldFilesBeforeArchive, passes it through to FileInfoDateTime.CleanupFiles (renamed parseFileNameSeqNo), changes time grouping to hour-truncated DateTime, relaxes sequence-number parsing, updates related call sites, and adjusts unit tests for multi-digit suffixes.

Changes

Cohort / File(s) Summary
Archive cleanup core refactor
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs
Added optional archiveSuffixWithSeqNo parameter to DeleteOldFilesBeforeArchive overloads; replaced wildCardContainsSeqNo with parseFileNameSeqNo; updated FileInfoDateTime.CleanupFiles signature and logic; group file-created times by hour; relaxed TryParseStartSequenceNumber; updated cleanup log text.
Legacy handler call-site updates
src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
Updated calls to DeleteOldFilesBeforeArchive to match new signature; removed explicit trailing boolean in wildcard branch and pass archiveSuffixWithSeqNo: false where base-name digits should not be treated as archive suffix.
Zero-size handler call-site
src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
Startup deletion now calls DeleteOldFilesBeforeArchive(..., archiveSuffixWithSeqNo: false) when base name contains digits.
Unit test adjustments
tests/NLog.UnitTests/Targets/FileTargetTests.cs
Added ArchiveSuffixFormat = "_{0}"; increased archived events to produce multi-digit suffixes and updated expected archive filenames/contents.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App as Application
  participant H as BaseFileArchiveHandler
  participant F as FileInfoDateTime
  participant FS as FileSystem

  App->>H: Request archive cleanup (filePath, initialFileOpen, excludeFileName, archiveSuffixWithSeqNo)
  rect rgba(230,245,255,0.5)
    H->>H: compute wildcard start/end indices
    H->>H: parseFileNameSeqNo = archiveSuffixWithSeqNo && wildcardValid
    H->>F: CleanupFiles(fileInfos, limits, indices, excludeFileName, parseFileNameSeqNo)
    F-->>H: filesToDelete (grouped by hour, optional seq-no parsing)
  end
  loop each fileToDelete
    H->>FS: Delete(file)
    H-->>App: Log "Cleanup file archive {reason}. Delete file: '...'"
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Ensure older archive files are deleted before newer ones (#5986) Hour-based grouping and seq-no parsing were changed, but no tests or examples proving correct behavior for the reporter's rotating filenames.
Improve filename sequence-number parsing for archives (#5986)
Make sequence-number parsing configurable to avoid misinterpreting digits in base names (#5986)

Poem

I hop through logs with tidy cheer,
Sequenced tails now crisp and clear.
By hour I sort, by suffix I know,
Which files to keep, which ones to go.
A whisker-twitch, a gentle sweep—archives neat, the burrow sleeps. 🐇✨


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

267-287: Make digit parsing robust and simpler (avoid char.IsNumber + nullable math quirks).

Use char.IsDigit and an int accumulator to avoid surprises with non-ASCII numerals and nullable arithmetic.

Apply:

-            private static bool TryParseStartSequenceNumber(string archiveFileName, int seqStartIndex, out int archiveSequenceNo)
-            {
-                int? parsedSequenceNo = null;
-
-                int startIndex = seqStartIndex;
-                for (int i = startIndex; i < archiveFileName.Length; ++i)
-                {
-                    char chr = archiveFileName[i];
-                    if (!char.IsNumber(chr))
-                    {
-                        if (!parsedSequenceNo.HasValue)
-                            continue;
-                        break;
-                    }
-
-                    parsedSequenceNo = parsedSequenceNo > 0 ? parsedSequenceNo * 10 : 0;
-                    parsedSequenceNo += (chr - '0');
-                }
-                archiveSequenceNo = parsedSequenceNo ?? 0;
-                return parsedSequenceNo.HasValue;
-            }
+            private static bool TryParseStartSequenceNumber(string archiveFileName, int seqStartIndex, out int archiveSequenceNo)
+            {
+                int value = 0;
+                bool started = false;
+                for (int i = seqStartIndex; i < archiveFileName.Length; ++i)
+                {
+                    char chr = archiveFileName[i];
+                    if (!char.IsDigit(chr))
+                    {
+                        if (!started)
+                            continue;
+                        break;
+                    }
+                    started = true;
+                    checked { value = (value * 10) + (chr - '0'); }
+                }
+                archiveSequenceNo = value;
+                return started;
+            }

If overflow is a concern for pathological filenames, catch OverflowException and fall back to returning false.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 55eaacb and b663d5c.

📒 Files selected for processing (4)
  • src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (6 hunks)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1 hunks)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1 hunks)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
  • DeleteOldFilesBeforeArchive (52-60)
  • DeleteOldFilesBeforeArchive (62-116)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)
tests/NLog.UnitTests/NLogTestBase.cs (3)
  • AssertFileContents (121-124)
  • AssertFileContents (126-170)
  • StringRepeat (214-220)
src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (2)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
  • DeleteOldFilesBeforeArchive (52-60)
  • DeleteOldFilesBeforeArchive (62-116)
src/NLog/Targets/FileTarget.cs (3)
  • FileTarget (515-518)
  • FileTarget (527-530)
  • CleanFullFilePath (1118-1147)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (3)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)
  • IEnumerable (3958-3968)
src/NLog/Internal/FileInfoHelper.cs (3)
  • FileInfoHelper (39-81)
  • DateTime (41-44)
  • DateTime (46-60)
src/NLog/Targets/FileTarget.cs (2)
  • DateTime (837-855)
  • DateTime (865-873)
🔇 Additional comments (12)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (5)

97-101: Guard parseFileNameSeqNo on “single-star pattern” is fine, but depends on the end-index fix.

Once fileWildcardEndIndex is corrected, parseFileNameSeqNo will be reliable. No further change needed here.


190-197: CleanupFiles signature change looks good.

Renaming wildCardContainsSeqNo -> parseFileNameSeqNo clarifies intent. No issues spotted.


209-215: Hour-truncation for ordering: confirm no regressions for MaxArchiveDays/date-based cleanup.

This increases ordering granularity within a day. MaxArchiveDays still computes by Date, so behavior should be stable. Please confirm tests around DeleteArchiveFilesByDate/DateArchive_* still pass (especially boundary crossings).


351-353: Log message tweak LGTM.

Clearer wording without behavior change.


52-60: All DeleteOldFilesBeforeArchive call sites updated. Callers either rely on the default archiveSuffixWithSeqNo=true or explicitly pass archiveSuffixWithSeqNo:false as intended.

src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1)

68-76: Good: disable seq-no parsing when wildcarding a digit-bearing base filename.

Passing archiveSuffixWithSeqNo: false avoids treating embedded digits as sequence suffix at startup. This matches intent of deleting the active file and not interfering with unrelated archives.

tests/NLog.UnitTests/Targets/FileTargetTests.cs (4)

3253-3262: Test now pins ArchiveSuffixFormat explicitly — good.

Setting ArchiveSuffixFormat = "_{0}" makes expectations deterministic for dynamic sequence. No issues.


3266-3278: Expanded archive generation increases coverage.

Additional Generate100BytesLog calls ensure higher sequence numbers; aligns with updated cleanup logic. Looks good.


3282-3291: Updated assertions for _10/_11 archives match new behavior.

Expectations reflect dynamic sequence growth; fine.


3294-3298: Else-branch assertion updated to 'lll' — consistent with new ordering.

Matches last emission when MaxArchiveFiles <= 1. Good.

src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (2)

90-90: Good switch to overload with default archiveSuffixWithSeqNo=true

Dropping the trailing bool here correctly relies on the default to keep sequence-number parsing enabled for the strict single-asterisk wildcard case. Behavior preserved, and intent is clearer.


95-95: Disable seq-no parsing for non-strict wildcard: verify mixed date+seq configs

Passing archiveSuffixWithSeqNo: false for the fully rendered path avoids mis-parsing digits (e.g., dates) as sequence numbers in cleanup. This is sensible given the broader wildcard produced by GetDeleteOldFileNameWildcard. Please verify retention ordering for configs that combine both date and sequence in ArchiveFileName/ArchiveSuffixFormat (many archives within the same hour), ensuring cleanup still prefers oldest by time and remains stable when timestamps bucket to the same hour. If gaps appear, consider adding a focused unit test for that combo.

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: 2

♻️ Duplicate comments (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

94-96: Off-by-one and multi-star handling in fileWildcardEndIndex (can misclassify files and skew cleanup)

Same concern as earlier review: end-length currently counts the '' and fails for multi-'' wildcards. Compute trailing literal length after the last '*'.

Apply:

- int fileWildcardStartIndex = fileWildcard.IndexOf('*');
- int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && fileWildcardStartIndex == fileWildcard.LastIndexOf('*')) ? fileWildcard.Length - fileWildcardStartIndex : -1;
+ int fileWildcardStartIndex = fileWildcard.IndexOf('*');
+ int lastStarIndex = fileWildcard.LastIndexOf('*');
+ int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && lastStarIndex >= 0)
+     ? fileWildcard.Length - lastStarIndex - 1
+     : -1;

This fixes the off-by-one and enables correct star-region bounds when two '' are present (prefixsuffix*ext).

🧹 Nitpick comments (3)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)

190-201: Guard against negative/overflow inputs; early-exit remains correct

The early exits for trivial cases look good. Consider adding a fast-path when maxArchiveDays <= 0 && maxArchiveFiles < 0 to avoid allocating fileInfoDates.


267-295: Sequence parser: tolerate separators after digits until extension; avoid false negatives

The updated parser is closer, but it rejects files with digits–separator–digits before the extension (e.g., "log_12-20250101.txt") by returning false when digits reappear before the extension. If the intended contract is “sequence starts at first digits and must be contiguous,” this is fine; otherwise, consider limiting the scan window to the first contiguous digit run and ignore subsequent digits.

Proposed minimal change (stop at first non-digit once digits started):

- if (!char.IsNumber(chr)) {
-     foundSequenceNo = parsedSequenceNo.HasValue;
-     continue;
- } else if (foundSequenceNo) {
-     if (Path.GetFileNameWithoutExtension(archiveFileName).Length < i) break;
-     archiveSequenceNo = 0; return false;
- }
+ if (!char.IsDigit(chr)) {
+     if (parsedSequenceNo.HasValue)
+         break; // stop on first separator after starting digits
+     continue;
+ }

Keeps behavior deterministic and avoids misclassifying filenames with extra tokens.

tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)

3247-3306: Add a focused regression for #5986 with non-consecutive sequences

Optional: augment this test to pre-create stray archives like "_91", "_99", "_111", then roll more files and assert that the lowest seq-nos are deleted first, not the newest. This mirrors the linked issue precisely.

I can draft that test if you want.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b663d5c and 9f08da0.

📒 Files selected for processing (4)
  • src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (6 hunks)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1 hunks)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1 hunks)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
🧰 Additional context used
🧬 Code graph analysis (2)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)
tests/NLog.UnitTests/NLogTestBase.cs (3)
  • AssertFileContents (121-124)
  • AssertFileContents (126-170)
  • StringRepeat (214-220)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (3)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)
  • IEnumerable (3958-3968)
src/NLog/Internal/FileInfoHelper.cs (3)
  • FileInfoHelper (39-81)
  • DateTime (41-44)
  • DateTime (46-60)
src/NLog/Targets/FileTarget.cs (2)
  • DateTime (837-855)
  • DateTime (865-873)
🔇 Additional comments (7)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (3)

209-215: Changed time bucketing to hourly — check interaction with minute-based archives

Rounding to the hour reduces churn but can reorder files within the same hour by filename rather than time. Please verify tests where ArchiveEvery = Minute still produce expected cleanup ordering.


359-363: Clearer cleanup logging message

Improved log text and level are good for diagnostics.


231-246: Ensure fileWildcardEndIndex represents suffix length before letter-scan
ExcludeFileName loops from fileWildcardStartIndex to fileName.Length – fileWildcardEndIndex; verify that fileWildcardEndIndex is calculated as the number of characters to skip at the end (i.e. pattern.Length – 1 – lastWildcardIndex), otherwise the scanned region may be incorrect when two * are present.

tests/NLog.UnitTests/Targets/FileTargetTests.cs (4)

3256-3262: Explicit ArchiveSuffixFormat improves test determinism

Setting ArchiveSuffixFormat = "_{0}" removes ambiguity around padding. Good.


3269-3278: Expanded archive generation increases coverage

Extending to l (12 rolls) exercises higher sequence numbers and matches the reported issue scenario.


3284-3290: Expecting _10 and _11 validates non-truncating sequence growth

Good assertion update; it will catch regressions where cleanup accidentally removes the newest archives.


3295-3296: Else-branch expectation aligned with latest sequence

Assertion matches the last-current file content when archives are constrained.

@snakefoot snakefoot closed this Sep 5, 2025
@snakefoot snakefoot reopened this Sep 5, 2025
@snakefoot snakefoot force-pushed the filetarget_cleanup branch 7 times, most recently from 3c9123d to 62f8996 Compare September 5, 2025 19:16
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: 0

Caution

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

⚠️ Outside diff range comments (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

271-296: Parse the last digit run before extension to robustly extract seq-no.

Current logic aborts when encountering multiple numeric groups (e.g., date + seq), disabling seq ordering. Parse the final contiguous digit group before the extension instead.

-                int? parsedSequenceNo = null;
-
-                bool foundSequenceNo = false;
-
-                int startIndex = seqStartIndex;
-                for (int i = startIndex; i < archiveFileName.Length; ++i)
-                {
-                    char chr = archiveFileName[i];
-                    if (!char.IsNumber(chr))
-                    {
-                        foundSequenceNo = parsedSequenceNo.HasValue;
-                        continue;
-                    }
-                    else if (foundSequenceNo)
-                    {
-                        var fileExt = archiveFileName.LastIndexOf('.');
-                        if (fileExt > 0 && fileExt < i)
-                            break;      // Ignore bonus sequence-number, when found inside file-extension
-                        archiveSequenceNo = 0;
-                        return false;   // Abort bonus sequence-number found, so cannot trust anything
-                    }
-
-                    parsedSequenceNo = parsedSequenceNo > 0 ? parsedSequenceNo * 10 : 0;
-                    parsedSequenceNo += (chr - '0');
-                }
-                archiveSequenceNo = parsedSequenceNo ?? 0;
-                return parsedSequenceNo.HasValue;
+                int fileExt = archiveFileName.LastIndexOf('.');
+                int endIndex = fileExt > 0 ? fileExt : archiveFileName.Length;
+                int lastStart = -1, lastLen = 0, curStart = -1, curLen = 0;
+                for (int i = Math.Max(0, seqStartIndex); i < endIndex; ++i)
+                {
+                    char ch = archiveFileName[i];
+                    if (char.IsDigit(ch))
+                    {
+                        if (curLen == 0) curStart = i;
+                        curLen++;
+                    }
+                    else if (curLen > 0)
+                    {
+                        lastStart = curStart;
+                        lastLen = curLen;
+                        curStart = -1;
+                        curLen = 0;
+                    }
+                }
+                if (curLen > 0)
+                {
+                    lastStart = curStart;
+                    lastLen = curLen;
+                }
+                if (lastLen > 0)
+                {
+                    int value = 0;
+                    for (int i = lastStart; i < lastStart + lastLen; i++)
+                        value = (value * 10) + (archiveFileName[i] - '0');
+                    archiveSequenceNo = value;
+                    return true;
+                }
+                archiveSequenceNo = 0;
+                return false;
♻️ Duplicate comments (3)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (3)

52-60: New flag archiveSuffixWithSeqNo: verify call-site propagation and add tests.

Ensure all call sites (incl. ZeroFileArchiveHandler, LegacyArchiveFileNameHandler, RollingArchiveFileArchiveHandler) pass the intended value and add unit tests for both true/false to lock behavior. This was flagged earlier and still lacks explicit tests.


95-95: Off-by-one in fileWildcardEndIndex skews star-region and exclusion.

This can misclassify files and delete wrong ones when computing the trailing literal length. Fix as below.

-int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && fileWildcardStartIndex == fileWildcard.LastIndexOf('*')) ? fileWildcard.Length - fileWildcardStartIndex : -1;
+int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && fileWildcardStartIndex == fileWildcard.LastIndexOf('*'))
+    ? fileWildcard.Length - fileWildcardStartIndex - 1
+    : -1;

97-97: Sequence parsing disabled for common multi- wildcards; causes wrong deletions when seq jumps.*

Requiring exactly one '' turns off seq parsing for typical patterns like "prefixsuffixext", undermining #5986 fix. Enable parsing when any '' exists.

-var parseFileNameSeqNo = archiveSuffixWithSeqNo && (fileWildcardStartIndex >= 0 && fileWildcardEndIndex >= 0);
+var parseFileNameSeqNo = archiveSuffixWithSeqNo && (fileWildcardStartIndex >= 0);
🧹 Nitpick comments (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

209-213: Hour-level bucketing may collapse order within same hour.

If seq parsing is off (e.g., due to config), many files in the same hour will sort by name, not recency. If feasible, keep full resolution or only round when parseFileNameSeqNo is true.

- fileCreatedTimeUtc = new DateTime(fileCreatedTimeUtc.Year, fileCreatedTimeUtc.Month, fileCreatedTimeUtc.Day, fileCreatedTimeUtc.Hour, 0, 0, fileCreatedTimeUtc.Kind);
+ // Consider retaining full resolution when not parsing sequence numbers
+ if (parseFileNameSeqNo)
+     fileCreatedTimeUtc = new DateTime(fileCreatedTimeUtc.Year, fileCreatedTimeUtc.Month, fileCreatedTimeUtc.Day, fileCreatedTimeUtc.Hour, 0, 0, fileCreatedTimeUtc.Kind);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9f08da0 and 3c9123d.

📒 Files selected for processing (4)
  • src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (6 hunks)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1 hunks)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1 hunks)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
src/NLog/Internal/FileInfoHelper.cs (3)
  • FileInfoHelper (39-81)
  • DateTime (41-44)
  • DateTime (46-60)
src/NLog/Targets/FileTarget.cs (2)
  • DateTime (837-855)
  • DateTime (865-873)
🔇 Additional comments (2)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)

190-201: Signature change LGTM; ensure downstream updates.

CleanupFiles(new bool parseFileNameSeqNo) is sound. Confirm all invocations updated; consider adding a targeted test to cover both parse on/off paths.


360-360: Log message tweak LGTM.

Clearer context in cleanup logs will help diagnose deletions.

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: 0

Caution

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

⚠️ Outside diff range comments (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

267-296: Sequence parsing fails for common "{date}_{seq}" names; parse the last digit run before extension

Current TryParseStartSequenceNumber aborts when a second digit run appears (e.g., "App_20250905_12.txt"), so the seq "12" isn’t recognized and cleanup may fall back to timestamps, reintroducing the bug from #5986 in mixed date+seq patterns.

Refactor to take the last contiguous digit run between seqStartIndex and the file extension.

Apply:

-            private static bool TryParseStartSequenceNumber(string archiveFileName, int seqStartIndex, out int archiveSequenceNo)
-            {
-                int? parsedSequenceNo = null;
-
-                bool foundSequenceNo = false;
-
-                int startIndex = seqStartIndex;
-                for (int i = startIndex; i < archiveFileName.Length; ++i)
-                {
-                    char chr = archiveFileName[i];
-                    if (!char.IsNumber(chr))
-                    {
-                        foundSequenceNo = parsedSequenceNo.HasValue;
-                        continue;
-                    }
-                    else if (foundSequenceNo)
-                    {
-                        var fileExt = archiveFileName.LastIndexOf('.');
-                        if (fileExt > 0 && fileExt < i)
-                            break;      // Ignore bonus sequence-number, when found inside file-extension
-                        archiveSequenceNo = 0;
-                        return false;   // Abort bonus sequence-number found, so cannot trust anything
-                    }
-
-                    parsedSequenceNo = parsedSequenceNo > 0 ? parsedSequenceNo * 10 : 0;
-                    parsedSequenceNo += (chr - '0');
-                }
-                archiveSequenceNo = parsedSequenceNo ?? 0;
-                return parsedSequenceNo.HasValue;
-            }
+            private static bool TryParseStartSequenceNumber(string archiveFileName, int seqStartIndex, out int archiveSequenceNo)
+            {
+                archiveSequenceNo = 0;
+                if (seqStartIndex < 0 || seqStartIndex >= archiveFileName.Length)
+                    return false;
+
+                // Limit scan to filename part (exclude extension)
+                int endExclusive = archiveFileName.LastIndexOf('.');
+                if (endExclusive <= seqStartIndex)
+                    endExclusive = archiveFileName.Length;
+
+                // Capture the last contiguous digit run between seqStartIndex and endExclusive
+                int lastRunStart = -1, lastRunEnd = -1;
+                bool inRun = false;
+                for (int i = seqStartIndex; i < endExclusive; ++i)
+                {
+                    char ch = archiveFileName[i];
+                    if (char.IsDigit(ch))
+                    {
+                        if (!inRun) { inRun = true; lastRunStart = i; }
+                        lastRunEnd = i + 1;
+                    }
+                    else
+                    {
+                        if (inRun) { inRun = false; }
+                    }
+                }
+
+                if (lastRunStart >= 0 && lastRunEnd > lastRunStart)
+                {
+                    int value = 0;
+                    for (int i = lastRunStart; i < lastRunEnd; ++i)
+                        value = (value * 10) + (archiveFileName[i] - '0');
+                    archiveSequenceNo = value;
+                    return true;
+                }
+                return false;
+            }
♻️ Duplicate comments (2)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)

52-60: API change: new archiveSuffixWithSeqNo parameter — verify all call sites and coverage

Parameter threading looks fine. Please ensure all internal callers and tests cover both true/false paths (incl. ZeroFileArchiveHandler and LegacyArchiveFileNameHandler). I can provide focused tests if helpful.
Suggested verification:

#!/bin/bash
rg -nP --glob 'src/**/*.cs' '\bDeleteOldFilesBeforeArchive\s*\(' -C2

95-98: Off-by-one in fileWildcardEndIndex and too-strict parsing gate can delete wrong files

  • fileWildcardEndIndex currently includes the '*' itself, shrinking the star-region and skewing ExcludeFileName/sequence parsing.
  • parseFileNameSeqNo only turns on when there is exactly one '*', disabling seq parsing for common "{date}_{seq}" patterns and risking wrong-deletion when seq jumps (the bug this PR targets).

Apply:

- int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && fileWildcardStartIndex == fileWildcard.LastIndexOf('*')) ? fileWildcard.Length - fileWildcardStartIndex : -1;
+ int fileWildcardEndIndex = (fileWildcardStartIndex >= 0 && fileWildcardStartIndex == fileWildcard.LastIndexOf('*'))
+     ? fileWildcard.Length - fileWildcardStartIndex - 1
+     : -1;

- var parseFileNameSeqNo = archiveSuffixWithSeqNo && fileWildcardEndIndex >= 0;
+ var parseFileNameSeqNo = archiveSuffixWithSeqNo && fileWildcardStartIndex >= 0;
🧹 Nitpick comments (2)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (1)

3269-3278: Broadened archive generation looks right

Extending to d..l forces rollover into two-digit seq-no; appropriate for stressing cleanup order. If test runtime grows, consider reducing a couple of Generate100BytesLog calls without losing coverage.

src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

190-215: Hour-bucketing creation time: confirm no regressions in cleanup ordering

Truncating to hour reduces temporal resolution; ordering then falls back to file-name when many archives occur within the same hour and parseFileNameSeqNo is false. This can subtly change which files are deleted when MaxArchiveFiles is hit. Please confirm with a test that creates >10 archives within the same hour with non-padded numeric names and asserts the intended deletion order (both with and without seq parsing enabled).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9123d and 62f8996.

📒 Files selected for processing (4)
  • src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (6 hunks)
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1 hunks)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1 hunks)
  • tests/NLog.UnitTests/Targets/FileTargetTests.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
  • src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
src/NLog/Internal/FileInfoHelper.cs (3)
  • FileInfoHelper (39-81)
  • DateTime (41-44)
  • DateTime (46-60)
src/NLog/Targets/FileTarget.cs (2)
  • DateTime (837-855)
  • DateTime (865-873)
🔇 Additional comments (4)
tests/NLog.UnitTests/Targets/FileTargetTests.cs (3)

3256-3263: Good: exercising dynamic seq-no suffix in same-folder archives

Setting ArchiveSuffixFormat to "_{0}" validates multi-digit seq-no handling with same-directory archiving. Matches PR intent.


3284-3291: Updated expectations reflect new tail archives

Asserting _10 with "kkk" and _11 with "lll" is consistent with rolling and maxArchiveFiles > 1. Looks good.


3294-3296: Else-branch expectation updated correctly

Verifies current file content when maxArchiveFiles <= 1. Matches behavior.

src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)

360-363: Log message tweak is fine

More explicit cleanup message; OK.

@snakefoot snakefoot force-pushed the filetarget_cleanup branch 4 times, most recently from 3521973 to 996a706 Compare September 5, 2025 19:58
@snakefoot snakefoot closed this Sep 5, 2025
@snakefoot snakefoot reopened this Sep 5, 2025
@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 5, 2025

@snakefoot snakefoot enabled auto-merge (squash) September 5, 2025 20:23
This was referenced Sep 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement on existing feature file-archiving Issues with archiving with the file target file-target size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

V6.0: When scrolling, the newer files are deleted rather than the older ones. An example is as follows.

1 participant