NLog.Targets.AtomicFile supporting NetStandard2 with Win32 API#6100
NLog.Targets.AtomicFile supporting NetStandard2 with Win32 API#6100
Conversation
|
Thanks for opening this pull request! |
WalkthroughAdds netstandard2.0 support and a NETSTANDARD-specific Windows atomic-file creation path by introducing Win32 CreateFile interop via a new Changes
Sequence Diagram(s)sequenceDiagram
participant Atomic as AtomicFileTarget
participant Native as NativeMethods (P/Invoke)
participant OS as Windows Kernel
participant Stream as FileStream
Atomic->>Native: Request CreateFile handle (path, access, flags)
Native->>OS: Call CreateFileW (lpFileName, access, share, disp, flags)
OS-->>Native: Return SafeFileHandle
alt handle valid
Native-->>Atomic: SafeFileHandle
Atomic->>Stream: Create FileStream from handle (no buffering)
Stream-->>Atomic: FileStream ready
else handle invalid
Native-->>Atomic: error/invalid handle
Atomic->>Atomic: fallback or throw
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/NLog.Targets.AtomicFile/AtomicFileTarget.cs (1)
123-136: TheSetAccessControlcall appears ineffective; consider removing or fixing.The
FileSecurityobject created on line 123 has no access rules added to it. CallingSetAccessControl(fileSecurity)on line 136 with this empty security descriptor doesn't actually inherit permissions from the parent directory—it does nothing meaningful.If the intent is to inherit parent ACLs, the file should already inherit them automatically when created on NTFS (via the
fileSecurityparameter passed toCreate). TheSetAccessRuleProtection(false, false)call ensures inheritance is not blocked, but callingSetAccessControlafterward with the same empty object is redundant.Consider either:
- Removing line 136 and the misleading comment on line 135, or
- Actually retrieving and applying the parent directory's ACL if explicit inheritance copying is needed.
♻️ Suggested simplification
fileStream = fileInfo.Create( FileMode.CreateNew, systemRights, fileShare, bufferSize: 1, FileOptions.None, fileSecurity: fileSecurity); - - // inherit permissions from parent directory - fileInfo.SetAccessControl(fileSecurity); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 123 - 136, The call to fileInfo.SetAccessControl(fileSecurity) in AtomicFileTarget.cs is redundant because the FileSecurity instance has no rules and SetAccessRuleProtection(false, false) merely allows inheritance; either remove the SetAccessControl call and the misleading "inherit permissions" comment, or if explicit copying is required, fetch the parent directory's FileSecurity (e.g., DirectoryInfo.GetAccessControl()) and apply that object to the file instead; update the code around fileInfo.Create(...) and the comment near SetAccessControl to reflect the chosen approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.NetStandard2.Tests.csproj`:
- Line 29: The PackageReference Include value "Basic.Reference.Assemblies "
contains a trailing space; open the project file and edit the PackageReference
element to remove the trailing space so the Include reads
"Basic.Reference.Assemblies" (verify there are no other accidental trailing
spaces in package names), save the .csproj and run a restore/build to confirm
the package resolves correctly.
---
Nitpick comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 123-136: The call to fileInfo.SetAccessControl(fileSecurity) in
AtomicFileTarget.cs is redundant because the FileSecurity instance has no rules
and SetAccessRuleProtection(false, false) merely allows inheritance; either
remove the SetAccessControl call and the misleading "inherit permissions"
comment, or if explicit copying is required, fetch the parent directory's
FileSecurity (e.g., DirectoryInfo.GetAccessControl()) and apply that object to
the file instead; update the code around fileInfo.Create(...) and the comment
near SetAccessControl to reflect the chosen approach.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csprojsrc/NLog.slntests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.NetStandard2.Tests.csproj
tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.NetStandard2.Tests.csproj
Outdated
Show resolved
Hide resolved
79091b9 to
d1a72d9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/NLog.Targets.AtomicFile/Internal/Win32FileAttributes.cs (1)
44-45: Consider making the enuminternalto match its namespace intent.The
Win32FileAttributesenum is declared aspublicbut resides in theNLog.Internalnamespace. Since this is an implementation detail for the Win32 interop layer, marking itinternalwould prevent it from being part of the public API surface.♻️ Suggested change
[Flags] - public enum Win32FileAttributes : int + internal enum Win32FileAttributes : int🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/Internal/Win32FileAttributes.cs` around lines 44 - 45, The Win32FileAttributes enum is declared public but lives in the NLog.Internal namespace and should be an implementation detail; change its accessibility from public to internal by updating the Win32FileAttributes enum declaration to internal (ensure no external code relies on its public visibility and update any unit tests or callers if needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/NLog.Targets.AtomicFile/Internal/Win32FileNativeMethods.cs`:
- Line 40: Remove the unused using directive "using NLog.Targets;" from the top
of Win32FileNativeMethods.cs—it's not referenced by the Win32FileNativeMethods
class or any methods in this file, so delete that import to clean up warnings
and unused references.
---
Nitpick comments:
In `@src/NLog.Targets.AtomicFile/Internal/Win32FileAttributes.cs`:
- Around line 44-45: The Win32FileAttributes enum is declared public but lives
in the NLog.Internal namespace and should be an implementation detail; change
its accessibility from public to internal by updating the Win32FileAttributes
enum declaration to internal (ensure no external code relies on its public
visibility and update any unit tests or callers if needed).
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
run-tests.ps1src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/Internal/Win32FileAttributes.cssrc/NLog.Targets.AtomicFile/Internal/Win32FileNativeMethods.cssrc/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csprojsrc/NLog.slntests/NLog.Targets.AtomicFile.Tests.NetStandard20/NLog.Targets.AtomicFile.Tests.NetStandard20.csproj
🚧 Files skipped from review as they are similar to previous changes (1)
- src/NLog.sln
d1a72d9 to
4b63730
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/NLog.Targets.AtomicFile/Internal/Win32FileNativeMethods.cs`:
- Around line 43-48: The Win32SecurityFlags enum has an incorrect value for
SecurityAnonymous; update the enum in Win32FileNativeMethods.cs so
SecurityAnonymous is set to 0x00000000 (zero) instead of 0x00200000 to match the
Win32 SECURITY_ANONYMOUS constant; ensure any callers (e.g., CreateFile usage in
AtomicFileTarget.cs where Win32SecurityFlags is passed) will now receive the
correct anonymous security level.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
run-tests.ps1src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/Internal/Win32FileAttributes.cssrc/NLog.Targets.AtomicFile/Internal/Win32FileNativeMethods.cssrc/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csprojsrc/NLog.slntests/NLog.Targets.AtomicFile.Tests.NetStandard20/NLog.Targets.AtomicFile.Tests.NetStandard20.csproj
🚧 Files skipped from review as they are similar to previous changes (4)
- src/NLog.Targets.AtomicFile/AtomicFileTarget.cs
- tests/NLog.Targets.AtomicFile.Tests.NetStandard20/NLog.Targets.AtomicFile.Tests.NetStandard20.csproj
- src/NLog.sln
- run-tests.ps1
dec15c8 to
a88286e
Compare
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
a88286e to
9353306
Compare
|
Really like to have unit-test of the Win32-API, maybe something similar to And then have the following in <PropertyGroup Condition=" '$(TestTargetFramework)' != '' ">
<OutputPath>.\bin\$(Configuration)\$(TestTargetFramework)\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' ">
<Reference Include="NLog.Targets.AtomicFile">
<HintPath>../../src/NLog.Targets.AtomicFile/bin/$(Configuration)/$(TestTargetFramework)/NLog.Targets.AtomicFile.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>This should make Net462 run with the NetStandard2.0 assembly-dll for doing atomic-file-writing using Win32-API. |
ec44894 to
2e22247
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj`:
- Around line 37-46: The ProjectReference in the second ItemGroup must be
conditioned the same as the explicit Reference to avoid duplicate assembly
resolution: modify the ProjectReference (the ProjectReference element that
points to ..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj) so
it is only included when the explicit Reference is not used by adding a
Condition that mirrors the first ItemGroup (examine TargetFramework and
TestTargetFramework) to exclude the ProjectReference when TestTargetFramework is
set and TargetFramework != 'net8.0'.
| <ItemGroup Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' "> | ||
| <Reference Include="NLog.Targets.AtomicFile"> | ||
| <HintPath>../../src/NLog.Targets.AtomicFile/bin/$(Configuration)/$(TestTargetFramework)/NLog.Targets.AtomicFile.dll</HintPath> | ||
| <Private>True</Private> | ||
| </Reference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" /> | ||
| </ItemGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if similar patterns exist in other test projects in the repo for reference
echo "=== ProjectReference with Condition ==="
rg -n "ProjectReference.*Condition" --glob "*.csproj" -C2
echo -e "\n=== TestTargetFramework usage ==="
rg -n "TestTargetFramework" --glob "*.csproj" -C3Repository: NLog/NLog
Length of output: 4443
🏁 Script executed:
cat -n tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csprojRepository: NLog/NLog
Length of output: 2602
🏁 Script executed:
cat -n tests/NLog.UnitTests/NLog.UnitTests.csproj | head -80Repository: NLog/NLog
Length of output: 4068
Add condition to ProjectReference to prevent duplicate assembly resolution.
When TestTargetFramework is set and TargetFramework is not net8.0, both the explicit Reference (lines 38-41) and the unconditional ProjectReference (line 45) will be active, causing MSBuild to resolve the same assembly from two different sources. This can lead to build warnings or unpredictable behavior.
Apply the condition to the ProjectReference to exclude it when the explicit Reference is used:
🔧 Proposed fix
- <ItemGroup>
+ <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' OR '$(TestTargetFramework)' == '' ">
<ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" />
</ItemGroup>📝 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.
| <ItemGroup Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' "> | |
| <Reference Include="NLog.Targets.AtomicFile"> | |
| <HintPath>../../src/NLog.Targets.AtomicFile/bin/$(Configuration)/$(TestTargetFramework)/NLog.Targets.AtomicFile.dll</HintPath> | |
| <Private>True</Private> | |
| </Reference> | |
| </ItemGroup> | |
| <ItemGroup> | |
| <ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" /> | |
| </ItemGroup> | |
| <ItemGroup Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' "> | |
| <Reference Include="NLog.Targets.AtomicFile"> | |
| <HintPath>../../src/NLog.Targets.AtomicFile/bin/$(Configuration)/$(TestTargetFramework)/NLog.Targets.AtomicFile.dll</HintPath> | |
| <Private>True</Private> | |
| </Reference> | |
| </ItemGroup> | |
| <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' OR '$(TestTargetFramework)' == '' "> | |
| <ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" /> | |
| </ItemGroup> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj`
around lines 37 - 46, The ProjectReference in the second ItemGroup must be
conditioned the same as the explicit Reference to avoid duplicate assembly
resolution: modify the ProjectReference (the ProjectReference element that
points to ..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj) so
it is only included when the explicit Reference is not used by adding a
Condition that mirrors the first ItemGroup (examine TargetFramework and
TestTargetFramework) to exclude the ProjectReference when TestTargetFramework is
set and TargetFramework != 'net8.0'.
2e22247 to
57c1ca5
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj (1)
37-42:⚠️ Potential issue | 🟠 MajorPrevent dual resolution of
NLog.Targets.AtomicFilein this mode.With this explicit
<Reference>enabled, the unconditional<ProjectReference>at Line 45 can still be included, so MSBuild may resolve the same assembly from two paths. Gate theProjectReferenceto the complementary condition.🔧 Proposed fix
- <ItemGroup> + <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' OR '$(TestTargetFramework)' == '' "> <ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" /> </ItemGroup>#!/bin/bash # Verify whether ProjectReference is still unconditional while explicit Reference is conditional. python - <<'PY' import xml.etree.ElementTree as ET p = "tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj" root = ET.parse(p).getroot() def local(tag): return tag.split('}', 1)[-1] project_ref_conditions = [] explicit_ref_conditions = [] for itemgroup in root: if local(itemgroup.tag) != "ItemGroup": continue cond = itemgroup.attrib.get("Condition", "").strip() for child in itemgroup: name = local(child.tag) include = child.attrib.get("Include", "") if name == "ProjectReference" and include.endswith("NLog.Targets.AtomicFile.csproj"): project_ref_conditions.append(cond) if name == "Reference" and include == "NLog.Targets.AtomicFile": explicit_ref_conditions.append(cond) print("Explicit <Reference> conditions:", explicit_ref_conditions) print("ProjectReference conditions:", project_ref_conditions) print("Has unconditional ProjectReference:", any(c == "" for c in project_ref_conditions)) PY🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj` around lines 37 - 42, The explicit <Reference Include="NLog.Targets.AtomicFile"> is conditionally added when Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' ", but the corresponding <ProjectReference> for NLog.Targets.AtomicFile remains unconditional and can cause dual resolution; update the <ProjectReference Include="...NLog.Targets.AtomicFile.csproj"> block so it is only present when the explicit Reference is not used (i.e., add the complementary Condition to that ItemGroup or ProjectReference—e.g., the inverse of "'$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != ''") to ensure only one resolution path is active.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj`:
- Around line 37-42: The explicit <Reference Include="NLog.Targets.AtomicFile">
is conditionally added when Condition=" '$(TargetFramework)' != 'net8.0' AND
'$(TestTargetFramework)' != '' ", but the corresponding <ProjectReference> for
NLog.Targets.AtomicFile remains unconditional and can cause dual resolution;
update the <ProjectReference Include="...NLog.Targets.AtomicFile.csproj"> block
so it is only present when the explicit Reference is not used (i.e., add the
complementary Condition to that ItemGroup or ProjectReference—e.g., the inverse
of "'$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != ''") to
ensure only one resolution path is active.
|
Have been looking at how dotnet-runtime repo is using CreateFile, and forked your pull-request with additional changes:
If you can apply the changes to this pull-request, then I will merge and get release out this weekend:
|
Removed conditional property group for output path.
e1b6d81 to
47acbc7
Compare
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs (1)
82-84: Enum values are GENERIC_ constants, not FILE_GENERIC_*.*The values
0x80000000,0x40000000, and0x20000000correspond toGENERIC_READ,GENERIC_WRITE, andGENERIC_EXECUTErespectively. The actualFILE_GENERIC_*constants are composite values combining multiple rights. While these members aren't used in the current implementation, the naming could cause confusion when searching Win32 documentation.This aligns with the reviewer feedback in PR comments suggesting renaming to match Win32 defines for easier searching.
♻️ Suggested rename to match Win32 defines
- FILE_GENERIC_READ = 0x80000000, - FILE_GENERIC_WRITE = 0x40000000, - FILE_GENERIC_EXECUTE = 0x20000000, + GENERIC_READ = 0x80000000, + GENERIC_WRITE = 0x40000000, + GENERIC_EXECUTE = 0x20000000,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs` around lines 82 - 84, Rename the enum members FILE_GENERIC_READ, FILE_GENERIC_WRITE, and FILE_GENERIC_EXECUTE in NativeMethods.cs to GENERIC_READ, GENERIC_WRITE, and GENERIC_EXECUTE respectively, since their hex values (0x80000000, 0x40000000, 0x20000000) map to the Win32 GENERIC_* constants; update any references (e.g., usages in methods within the same file or other files) to the new member names to keep semantics identical and make the names searchable against Win32 docs.src/NLog.Targets.AtomicFile/AtomicFileTarget.cs (1)
184-189: Consider usingDispose()or ausingstatement for cleaner resource management.The current cleanup logic works, but the pattern could be simplified. Since
SafeFileHandleimplementsIDisposable, callingDispose()is more idiomatic than checkingIsClosedand callingClose().♻️ Simplified cleanup using Dispose
catch { - if ((handle != null) && (!handle.IsClosed)) - handle.Close(); + handle?.Dispose(); throw; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 184 - 189, Replace the manual SafeFileHandle cleanup in the catch block with Dispose() (or use a using statement where the SafeFileHandle is created) to rely on IDisposable instead of checking IsClosed and calling Close(); specifically update the catch that references the local variable handle (SafeFileHandle) in AtomicFileTarget.cs so it calls handle.Dispose() (or refactor the handle acquisition into a using block) before rethrowing to simplify and make resource management idiomatic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 144-147: The bitwise check uses FileMode as if it were a flags
enum and can yield false positives; change the condition to a proper equality
check against the specific mode: replace "if ((fileMode & FileMode.Append) !=
0)" with a direct comparison (e.g., "if (fileMode == FileMode.Append)") so that
only the FileMode.Append case sets dwDesiredAccess |=
NativeMethods.Win32FileAccess.FILE_APPEND_DATA; reference the fileMode variable,
the condition, dwDesiredAccess and
NativeMethods.Win32FileAccess.FILE_APPEND_DATA in your change.
---
Nitpick comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 184-189: Replace the manual SafeFileHandle cleanup in the catch
block with Dispose() (or use a using statement where the SafeFileHandle is
created) to rely on IDisposable instead of checking IsClosed and calling
Close(); specifically update the catch that references the local variable handle
(SafeFileHandle) in AtomicFileTarget.cs so it calls handle.Dispose() (or
refactor the handle acquisition into a using block) before rethrowing to
simplify and make resource management idiomatic.
In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs`:
- Around line 82-84: Rename the enum members FILE_GENERIC_READ,
FILE_GENERIC_WRITE, and FILE_GENERIC_EXECUTE in NativeMethods.cs to
GENERIC_READ, GENERIC_WRITE, and GENERIC_EXECUTE respectively, since their hex
values (0x80000000, 0x40000000, 0x20000000) map to the Win32 GENERIC_*
constants; update any references (e.g., usages in methods within the same file
or other files) to the new member names to keep semantics identical and make the
names searchable against Win32 docs.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
run-tests.ps1src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/Internal/NativeMethods.cstests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj
🚧 Files skipped from review as they are similar to previous changes (2)
- run-tests.ps1
- tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj
I cherry-picked your changes. One thing to note is that the |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/NLog.Targets.AtomicFile/AtomicFileTarget.cs (1)
175-177: Minor style inconsistency with brace placement.Line 175 uses K&R style (opening brace on same line), while the rest of the file uses Allman style (opening brace on new line). Consider aligning for consistency.
♻️ Suggested style fix
- if (handle.IsInvalid) { + if (handle.IsInvalid) + { System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(System.Runtime.InteropServices.Marshal.GetHRForLastWin32Error()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 175 - 177, The if statement checking handle.IsInvalid uses K&R brace placement; change it to Allman style to match the file by placing the opening brace on the next line so the block for the condition "if (handle.IsInvalid)" matches the rest of the file's brace convention (the block that calls System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(...)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 175-177: The if statement checking handle.IsInvalid uses K&R brace
placement; change it to Allman style to match the file by placing the opening
brace on the next line so the block for the condition "if (handle.IsInvalid)"
matches the rest of the file's brace convention (the block that calls
System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(...)).
0653a7c to
0625798
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs`:
- Around line 72-75: The XML doc comment for the constant FILE_APPEND_DATA is
incomplete; update the summary on FILE_APPEND_DATA in NativeMethods.cs so it
reads as a complete, clear sentence (for example: "For a file, the right to
append data to the file without overwriting existing data.") and ensure
punctuation and grammar are corrected so the docstring fully explains the
permission's intent.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/Internal/NativeMethods.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/NLog.Targets.AtomicFile/AtomicFileTarget.cs
…), simplified handle close
0625798 to
2c4c8a3
Compare
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs (1)
87-96: Consider documenting the FileMode parameter constraint.The P/Invoke accepts
FileModedirectly fordwCreationDisposition. While .NETFileModevalues 1-5 align with Win32 creation disposition constants,FileMode.Append(6) is invalid for Win32 and must be converted by callers. A comment clarifying this constraint would improve maintainability.📝 Suggested documentation
+ /// <summary> + /// Creates or opens a file or I/O device. + /// </summary> + /// <remarks> + /// Note: FileMode.Append is not a valid Win32 creation disposition. + /// Callers must convert Append to OpenOrCreate and use FILE_APPEND_DATA access rights. + /// </remarks> [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] public static extern Microsoft.Win32.SafeHandles.SafeFileHandle CreateFile(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs` around lines 87 - 96, The CreateFile P/Invoke signature currently accepts a FileMode for the dwCreationDisposition parameter; add a brief XML comment above the CreateFile method explaining that .NET FileMode values 1–5 map to Win32 creation dispositions but FileMode.Append (value 6) is not a valid Win32 disposition and must be converted by callers (e.g., treat Append as OpenOrCreate with an explicit file pointer move) before calling CreateFile to avoid invalid behavior. Reference CreateFile and the FileMode dwCreationDisposition parameter in the comment so maintainers know the constraint and conversion responsibility.src/NLog.Targets.AtomicFile/AtomicFileTarget.cs (1)
136-157: Method is specialized for FileMode.Append despite accepting generic FileMode parameter.The method accepts
FileMode fileModebut only adds write access (FILE_APPEND_DATA) whenfileMode == FileMode.Append. Other file modes would result in a handle without write permissions. Since this is currently only called withFileMode.Append, this works correctly, but the signature is misleading.Consider either:
- Removing the
fileModeparameter and hardcodingFileMode.Appendbehavior, or- Adding validation/support for other modes if future callers might use them.
♻️ Option 1: Simplify to Append-only
- private static FileStream CreateWindowsStream(string filePath, FileMode fileMode, FileShare fileShare, FileOptions fileOptions) + private static FileStream CreateWindowsStream(string filePath, FileShare fileShare, FileOptions fileOptions) { Microsoft.Win32.SafeHandles.SafeFileHandle? handle = null; try { - NativeMethods.Win32FileAccess dwDesiredAccess = NativeMethods.Win32FileAccess.SYNCHRONIZE; - if (fileMode == FileMode.Append) - { - dwDesiredAccess |= NativeMethods.Win32FileAccess.FILE_APPEND_DATA; - } + NativeMethods.Win32FileAccess dwDesiredAccess = NativeMethods.Win32FileAccess.SYNCHRONIZE | NativeMethods.Win32FileAccess.FILE_APPEND_DATA; if ((fileOptions & FileOptions.DeleteOnClose) != 0) { dwDesiredAccess |= NativeMethods.Win32FileAccess.DELETE; // required by FILE_DELETE_ON_CLOSE } - // Must use a valid Win32 constant here... - if (fileMode == FileMode.Append) - { - fileMode = FileMode.OpenOrCreate; - } + FileMode fileMode = FileMode.OpenOrCreate;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 136 - 157, The CreateWindowsStream method currently accepts a FileMode parameter but only implements append semantics (adds FILE_APPEND_DATA and forces FileMode.OpenOrCreate), which is misleading; change CreateWindowsStream to be append-only by removing the FileMode parameter and hardcoding append behavior: update the method signature (remove the fileMode argument), always include NativeMethods.Win32FileAccess.FILE_APPEND_DATA in dwDesiredAccess, set the effective fileMode to FileMode.OpenOrCreate inside the method, remove the conditional branch that mutates fileMode, and update all callers to invoke CreateWindowsStream without a FileMode argument; alternatively, if you want to support other modes, add validation and branch logic to grant appropriate access bits for FileMode.Create, FileMode.Truncate, FileMode.Open, etc., but prefer the simpler append-only refactor given current usage in CreateWindowsStream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 136-157: The CreateWindowsStream method currently accepts a
FileMode parameter but only implements append semantics (adds FILE_APPEND_DATA
and forces FileMode.OpenOrCreate), which is misleading; change
CreateWindowsStream to be append-only by removing the FileMode parameter and
hardcoding append behavior: update the method signature (remove the fileMode
argument), always include NativeMethods.Win32FileAccess.FILE_APPEND_DATA in
dwDesiredAccess, set the effective fileMode to FileMode.OpenOrCreate inside the
method, remove the conditional branch that mutates fileMode, and update all
callers to invoke CreateWindowsStream without a FileMode argument;
alternatively, if you want to support other modes, add validation and branch
logic to grant appropriate access bits for FileMode.Create, FileMode.Truncate,
FileMode.Open, etc., but prefer the simpler append-only refactor given current
usage in CreateWindowsStream.
In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs`:
- Around line 87-96: The CreateFile P/Invoke signature currently accepts a
FileMode for the dwCreationDisposition parameter; add a brief XML comment above
the CreateFile method explaining that .NET FileMode values 1–5 map to Win32
creation dispositions but FileMode.Append (value 6) is not a valid Win32
disposition and must be converted by callers (e.g., treat Append as OpenOrCreate
with an explicit file pointer move) before calling CreateFile to avoid invalid
behavior. Reference CreateFile and the FileMode dwCreationDisposition parameter
in the comment so maintainers know the constraint and conversion responsibility.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/NLog.Targets.AtomicFile/AtomicFileTarget.cssrc/NLog.Targets.AtomicFile/Internal/NativeMethods.cs
|
|
Thank you for the quick adjustments. Will merge and create new NLog release. |
|
Hooray your first pull request is merged! Thanks for the contribution! Looking for more? 👼 Please check the up-for-grabs issues - thanks! |
|
NLog.Targets.AtomicFile ver. 6.1.1 has been released: Hope the new target platform Thank you for driving this, and making the effort to get this implemented. |




Unit tests for netstandard2.0 target for NLog AtomicFile target.
Stacked on top of #6099