-
Notifications
You must be signed in to change notification settings - Fork 1.4k
FileTarget - Extend InternalLogger output with archive cleanup reason #5980
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
WalkthroughAdds an archiveCleanupReason string propagated into old-archive deletion calls and logs, threads the new reason parameter through archive handlers by changing DeleteOldArchiveFile signatures and call sites, adds early-exit paths when no files match, and expands deletion exception handling and retry/logging paths; preserves overall cleanup control flow. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor App
participant Base as BaseFileArchiveHandler
participant FS as FileSystem
App->>Base: CleanupOldArchives()
rect rgba(230,240,255,0.5)
note over Base: Determine archiveCleanupReason\n(e.g., MaxArchiveDays/MaxArchiveFiles or startup flags)
end
loop for each candidate file
Base->>FS: DeleteOldArchiveFile(path, archiveCleanupReason)
alt success
FS-->>Base: true
Base->>App: Log deletion with reason
else failure
FS-->>Base: false
Base->>App: Handle failure (retry/adjust logging)
end
end
sequenceDiagram
autonumber
actor App
participant Rolling as RollingArchiveFileHandler
participant FS as FileSystem
App->>Rolling: RollToInitialSequenceNumber(startup)
alt DeleteOldFileOnStartup=true
Rolling->>FS: DeleteOldArchiveFile(fullPath, "DeleteOldFileOnStartup=true")
FS-->>Rolling: result
else no startup deletion
Rolling-->>App: continue
end
Rolling-->>App: return 0
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 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 (
|
3a57813 to
00134e3
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
🧹 Nitpick comments (5)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
88-91: Reason string can misrepresent actual policy; include both and use effective max countCurrently logs "MaxArchiveFiles" even when both MaxArchiveFiles and MaxArchiveDays are configured, and it ignores the effective maxArchiveFiles value (after startup adjustments). Prefer composing the reason from the active/effective settings.
- var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.MaxArchiveDays > 0) ? $"MaxArchiveDays={_fileTarget.MaxArchiveDays}" : $"MaxArchiveFiles={_fileTarget.MaxArchiveFiles}"; - if (initialFileOpen && _fileTarget.DeleteOldFileOnStartup) - archiveCleanupReason = "DeleteOldFileOnStartup=true"; + var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.MaxArchiveDays > 0) + ? $"MaxArchiveDays={_fileTarget.MaxArchiveDays}" + : $"MaxArchiveFiles={maxArchiveFiles}"; // reflect effective count + if (_fileTarget.MaxArchiveFiles >= 0 && _fileTarget.MaxArchiveDays > 0) + archiveCleanupReason = $"MaxArchiveFiles={maxArchiveFiles}, MaxArchiveDays={_fileTarget.MaxArchiveDays}"; + if (initialFileOpen && _fileTarget.DeleteOldFileOnStartup) + archiveCleanupReason = "DeleteOldFileOnStartup=true";
332-373: Echo reason on failures for better diagnosticsInclude archiveCleanupReason in the exception paths as well.
- InternalLogger.Debug(ex, "{0}: Failed to delete old file as directory not found: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Failed to delete old file as directory not found (Reason: {1}): '{2}'", _fileTarget, archiveCleanupReason, filepath); ... - InternalLogger.Debug(ex, "{0}: Failed to delete old file as file not found: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Failed to delete old file as file not found (Reason: {1}): '{2}'", _fileTarget, archiveCleanupReason, filepath); ... - InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked (Reason: {1}): '{2}'", _fileTarget, archiveCleanupReason, filepath); ... - InternalLogger.Warn(ex, "{0}: Failed to delete old archive file: '{1}'.", _fileTarget, filepath); + InternalLogger.Warn(ex, "{0}: Failed to delete old archive file (Reason: {1}): '{2}'.", _fileTarget, archiveCleanupReason, filepath);src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (1)
81-83: Reason label here is about retention, but action is “reset/truncate active file”Optional: make the reason reflect the action when not on startup, e.g., “Reset Active File”, for clearer logs.
- var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.ArchiveOldFileOnStartup) ? "ArchiveOldFileOnStartup=true" : $"MaxArchiveFiles={_fileTarget.MaxArchiveFiles}"; + var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.ArchiveOldFileOnStartup) + ? "ArchiveOldFileOnStartup=true" + : "Reset Active File";src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs (1)
121-121: LGTM; consider a shared constant for the reason tokenTo avoid typos across files, extract "DeleteOldFileOnStartup=true" into a private const or a static helper.
Example:
private const string ReasonDeleteOldOnStartup = "DeleteOldFileOnStartup=true";src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1)
228-239: Unify terminology: BirthTime vs CreationTime; deduplicate the reason stringComments reference CreationTime; the reason says “BirthTime”. Use one term consistently and avoid duplicating the literal.
- if (!DeleteOldArchiveFile(newFilePath, "Reset File BirthTime")) + if (!DeleteOldArchiveFile(newFilePath, "Reset File CreationTime")) ... - DeleteOldArchiveFile(newFilePath, "Reset File BirthTime"); // Attempt to delete file to reset File-Creation-Time + DeleteOldArchiveFile(newFilePath, "Reset File CreationTime"); // Attempt to delete file to reset File-Creation-TimeOptionally:
// near class top private const string ReasonResetCreationTime = "Reset File CreationTime"; // and use ReasonResetCreationTime in both places
📜 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(2 hunks)src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs(2 hunks)src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs(1 hunks)src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
DeleteOldArchiveFile(332-373)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs (1)
DeleteOldArchiveFile(1380-1405)
src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs (2)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
DeleteOldArchiveFile(332-373)src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs (1)
DeleteOldArchiveFile(1380-1405)
src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
DeleteOldArchiveFile(332-373)
🔇 Additional comments (2)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (2)
96-96: Good: reason plumbed into deletion callPassing archiveCleanupReason through improves traceability without altering behavior.
52-60: Signature change ripple check passed Verified no one-argumentDeleteOldArchiveFilecalls remain outsidesrc/NLog.Targets.ConcurrentFile.
7f5d06f to
ba3923b
Compare
ba3923b to
575e7f5
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)
332-373: IncludearchiveCleanupReasonin allDeleteOldArchiveFilecalls
Replace the single-argument calls insrc/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cswith the two-arg overload—update at lines 1325, 1341, 1413, 1626, 1633, 1670 and 2369 to pass a meaningfularchiveCleanupReason.
🧹 Nitpick comments (4)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (4)
81-88: Guard against negative wildcard index when wildCardContainsSeqNo=true.
If fileWildcard lacks '', IndexOf returns -1; passing this into the seq-no scan can crash when wildCardContainsSeqNo=true. Recommend clamping usage to only when a '' is present, and clarifying the variable name.Apply:
- int fileWildcardStartIndex = fileWildcard.IndexOf('*'); - int fileWildcardEndIndex = fileWildcard.Length - fileWildcardStartIndex; + int fileWildcardStartIndex = fileWildcard.IndexOf('*'); + int fileWildcardEndIndex = (fileWildcardStartIndex >= 0) ? (fileWildcard.Length - fileWildcardStartIndex) : -1; var maxArchiveFiles = _fileTarget.MaxArchiveFiles; @@ - foreach (var cleanupFileInfo in FileInfoDateTime.CleanupFiles(fileInfos, maxArchiveFiles, _fileTarget.MaxArchiveDays, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName, wildCardContainsSeqNo)) + var containsSeqNo = wildCardContainsSeqNo && fileWildcardStartIndex >= 0; + foreach (var cleanupFileInfo in FileInfoDateTime.CleanupFiles(fileInfos, maxArchiveFiles, _fileTarget.MaxArchiveDays, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName, containsSeqNo))Optional: rename fileWildcardEndIndex to fileWildcardSuffixLength for clarity.
Also applies to: 94-95
89-92: Reason string can mislead when both retention limits are set.
When both MaxArchiveFiles and MaxArchiveDays are configured, deletions may be age-driven but the reason logs “MaxArchiveFiles=…”. Prefer a neutral, explicit reason that includes both settings.- var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.MaxArchiveDays > 0) ? $"MaxArchiveDays={_fileTarget.MaxArchiveDays}" : $"MaxArchiveFiles={_fileTarget.MaxArchiveFiles}"; + var archiveCleanupReason = $"Retention(MaxArchiveFiles={_fileTarget.MaxArchiveFiles}, MaxArchiveDays={_fileTarget.MaxArchiveDays})"; if (initialFileOpen && _fileTarget.DeleteOldFileOnStartup) archiveCleanupReason = "DeleteOldFileOnStartup=true";
93-98: Return true only if deletion actually succeeded.
Currently sets oldFilesDeleted=true before attempting deletion, so transient failures still report success.- bool oldFilesDeleted = false; + bool oldFilesDeleted = false; foreach (var cleanupFileInfo in FileInfoDateTime.CleanupFiles(fileInfos, maxArchiveFiles, _fileTarget.MaxArchiveDays, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName, wildCardContainsSeqNo)) { - oldFilesDeleted = true; - DeleteOldArchiveFile(cleanupFileInfo.FullName, archiveCleanupReason); + oldFilesDeleted |= DeleteOldArchiveFile(cleanupFileInfo.FullName, archiveCleanupReason); } return oldFilesDeleted;Also applies to: 100-100
332-373: Tweak log messages; include attempt count and reason on failures.
Improves diagnosability while preserving behavior.- InternalLogger.Info("{0}: {1} deleting old archive file: '{2}'.", _fileTarget, archiveCleanupReason, filepath); + InternalLogger.Info("{0}: Deleting old archive file due to {1}: '{2}'.", _fileTarget, archiveCleanupReason, filepath); @@ - InternalLogger.Debug(ex, "{0}: Failed to delete old file as directory not found: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Failed to delete old file due to {1} (directory not found): '{2}'", _fileTarget, archiveCleanupReason, filepath); @@ - InternalLogger.Debug(ex, "{0}: Failed to delete old file as file not found: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Failed to delete old file due to {1} (file not found): '{2}'", _fileTarget, archiveCleanupReason, filepath); @@ - InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked: '{1}'", _fileTarget, filepath); + InternalLogger.Debug(ex, "{0}: Attempt {1}/3 failed to delete old file (maybe locked) due to {2}: '{3}'", _fileTarget, i, archiveCleanupReason, filepath); @@ - InternalLogger.Warn(ex, "{0}: Failed to delete old archive file: '{1}'.", _fileTarget, filepath); + InternalLogger.Warn(ex, "{0}: Failed to delete old archive file due to {1}: '{2}'.", _fileTarget, archiveCleanupReason, filepath);
📜 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(2 hunks)src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs(2 hunks)src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs(1 hunks)src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
- src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs
- src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs (1)
DeleteOldArchiveFile(1380-1405)
🔇 Additional comments (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
78-80: Good early exit on empty matches.
Avoids unnecessary work when no files are present.
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
🧹 Nitpick comments (6)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (6)
74-77: Fix misleading debug message when directory is missingThe log implies “no files matching wildcard” but this branch is hit because the directory doesn’t exist.
- InternalLogger.Debug("{0}: Archive Cleanup found no files matching wildcard {1} in directory: {2}", _fileTarget, fileWildcard, fileDirectory); + InternalLogger.Debug("{0}: Archive Cleanup skipped; directory does not exist. Wildcard={1}, Directory={2}", _fileTarget, fileWildcard, fileDirectory);
92-95: Log effective cleanup driverReason currently logs configured MaxArchiveFiles, not the effective value after adjustments. Consider logging the effective value to aid diagnostics.
-var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.MaxArchiveDays > 0) ? $"MaxArchiveDays={_fileTarget.MaxArchiveDays}" : $"MaxArchiveFiles={_fileTarget.MaxArchiveFiles}"; +var archiveCleanupReason = (_fileTarget.MaxArchiveFiles < 0 && _fileTarget.MaxArchiveDays > 0) + ? $"MaxArchiveDays={_fileTarget.MaxArchiveDays}" + : $"MaxArchiveFiles={maxArchiveFiles}";
341-345: Polish Info log phrasing for readabilityMinor wording tweak reads better in logs.
-InternalLogger.Info("{0}: {1} deleting old archive file: '{2}'.", _fileTarget, archiveCleanupReason, filepath); +InternalLogger.Info("{0}: Deleting old archive file ({1}): '{2}'.", _fileTarget, archiveCleanupReason, filepath);
356-363: Elevate final IO failure to WarnOn the last retry, consider logging Warn (with reason) to surface persistent lock issues.
- catch (IOException ex) + catch (IOException ex) { - InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked: '{1}'", _fileTarget, filepath); + var isFinalAttempt = i >= 3; + if (isFinalAttempt) + InternalLogger.Warn(ex, "{0}: Final attempt failed to delete old file ({1}): '{2}'", _fileTarget, archiveCleanupReason, filepath); + else + InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked: '{1}'", _fileTarget, filepath); if (!File.Exists(filepath)) return true; - if (i >= 3 && ex.MustBeRethrown(_fileTarget)) + if (isFinalAttempt && ex.MustBeRethrown(_fileTarget)) throw; }
373-375: Optional: add small jitter to retriesHelps reduce thundering-herd on shared paths.
-Thread.Sleep(i * 10); +Thread.Sleep(i * 10 + ThreadLocalRandom.Next(0, 10));Additional code (once in the file):
private static class ThreadLocalRandom { private static readonly ThreadLocal<Random> _rng = new ThreadLocal<Random>(() => new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); public static int Next(int min, int max) => _rng.Value!.Next(min, max); }
347-355: Standardize DeleteOldArchiveFile return values
BaseFileArchiveHandler.DeleteOldArchiveFile returns true on DirectoryNotFound/FileNotFound, whereas ConcurrentFileTarget.DeleteOldArchiveFile returns false in those cases. Align both to use a consistent “true means file no longer exists after attempt” semantic across the repository.
📜 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(2 hunks)src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs(2 hunks)src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs(1 hunks)src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs
- src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs
- src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (1)
src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs (1)
DeleteOldArchiveFile(1380-1405)
🔇 Additional comments (3)
src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs (3)
81-83: Early-exit on zero matches — LGTM
96-103: Enumeration-and-continue strategy — LGTMBest-effort deletion with final boolean summarizing work done is reasonable here.
86-91: Retention logic for MaxArchiveFiles is correct
Existing tests cover initialFileOpen with/without ArchiveOldFileOnStartup, DeleteOldFileOnStartup scenarios and validate MaxArchiveFiles behavior for 0, 1, and >1; no changes required.
| int fileWildcardStartIndex = fileWildcard.IndexOf('*'); | ||
| int fileWildcardEndIndex = fileWildcard.Length - fileWildcardStartIndex; |
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.
Incorrect fileWildcardEndIndex calculation (off-by-one, wrong star)
Using the first '' and not subtracting 1 skews the suffix-length, especially when the pattern contains multiple '', leading to incorrect ExcludeFileName bounds.
-int fileWildcardStartIndex = fileWildcard.IndexOf('*');
-int fileWildcardEndIndex = fileWildcard.Length - fileWildcardStartIndex;
+int fileWildcardStartIndex = fileWildcard.IndexOf('*');
+int lastWildcardIdx = fileWildcard.LastIndexOf('*');
+int fileWildcardEndIndex = lastWildcardIdx >= 0 ? (fileWildcard.Length - lastWildcardIdx - 1) : 0;📝 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.
| int fileWildcardStartIndex = fileWildcard.IndexOf('*'); | |
| int fileWildcardEndIndex = fileWildcard.Length - fileWildcardStartIndex; | |
| int fileWildcardStartIndex = fileWildcard.IndexOf('*'); | |
| int lastWildcardIdx = fileWildcard.LastIndexOf('*'); | |
| int fileWildcardEndIndex = lastWildcardIdx >= 0 | |
| ? (fileWildcard.Length - lastWildcardIdx - 1) | |
| : 0; |
🤖 Prompt for AI Agents
In src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs around lines
84-85, the code uses fileWildcard.IndexOf('*') and computes fileWildcardEndIndex
as fileWildcard.Length - fileWildcardStartIndex which is off-by-one and uses the
first '*' only; fix by finding the last '*' (use LastIndexOf('*')) and compute
the suffix length as fileWildcard.Length - fileWildcardEndIndex - 1 (or
equivalently fileWildcard.Length - (lastStarIndex + 1)) so ExcludeFileName
bounds are calculated from the correct star position and with the correct
subtraction.
|



See also: #5978