-
Notifications
You must be signed in to change notification settings - Fork 1.4k
FileTarget - Improve archive cleanup with dynamic seq-no #5987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
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 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. 📒 Files selected for processing (4)
WalkthroughAdds 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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.
📒 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: AllDeleteOldFilesBeforeArchivecall sites updated. Callers either rely on the defaultarchiveSuffixWithSeqNo=trueor explicitly passarchiveSuffixWithSeqNo:falseas 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=trueDropping 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 configsPassing
archiveSuffixWithSeqNo: falsefor 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.
b663d5c to
9f08da0
Compare
There was a problem hiding this 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 correctThe early exits for trivial cases look good. Consider adding a fast-path when
maxArchiveDays <= 0 && maxArchiveFiles < 0to avoid allocatingfileInfoDates.
267-295: Sequence parser: tolerate separators after digits until extension; avoid false negativesThe 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 sequencesOptional: 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.
📒 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 archivesRounding to the hour reduces churn but can reorder files within the same hour by filename rather than time. Please verify tests where
ArchiveEvery = Minutestill produce expected cleanup ordering.
359-363: Clearer cleanup logging messageImproved log text and level are good for diagnostics.
231-246: EnsurefileWildcardEndIndexrepresents suffix length before letter-scan
ExcludeFileNameloops fromfileWildcardStartIndextofileName.Length – fileWildcardEndIndex; verify thatfileWildcardEndIndexis 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 determinismSetting
ArchiveSuffixFormat = "_{0}"removes ambiguity around padding. Good.
3269-3278: Expanded archive generation increases coverageExtending to l (12 rolls) exercises higher sequence numbers and matches the reported issue scenario.
3284-3290: Expecting _10 and _11 validates non-truncating sequence growthGood assertion update; it will catch regressions where cleanup accidentally removes the newest archives.
3295-3296: Else-branch expectation aligned with latest sequenceAssertion matches the last-current file content when archives are constrained.
3c9123d to
62f8996
Compare
There was a problem hiding this 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.
📒 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.
There was a problem hiding this 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 extensionCurrent 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 coverageParameter 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 rightExtending 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 orderingTruncating 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.
📒 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 archivesSetting ArchiveSuffixFormat to "_{0}" validates multi-digit seq-no handling with same-directory archiving. Matches PR intent.
3284-3291: Updated expectations reflect new tail archivesAsserting _10 with "kkk" and _11 with "lll" is consistent with rolling and maxArchiveFiles > 1. Looks good.
3294-3296: Else-branch expectation updated correctlyVerifies current file content when maxArchiveFiles <= 1. Matches behavior.
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
360-363: Log message tweak is fineMore explicit cleanup message; OK.
3521973 to
996a706
Compare
996a706 to
05522ae
Compare
|



Resolves #5986