You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
\ is only a directory separator on Windows. On Linux and macOS it is an ordinary file name character, and NLog does not normalize it. So the log does not go into LogDirectory. It goes into the parent folder of LogDirectory, as one file whose name starts with Logging\.
Observed on 2026-09-15 on Ubuntu 26.04 (WSL) with Shared.Core 3.0.8 / NLog 6.1.3, in a small console app that sets BGLoggerConfiguration.LogDirectory = "/tmp/logsep-asis/Logging" and logs a few lines:
rendered FileName = '/tmp/logsep-asis/Logging\LogSepProbe.log'
Path.GetDirectoryName(rendered) = '/tmp/logsep-asis' (expected '/tmp/logsep-asis/Logging')
$ ls -lab /tmp/logsep-asis
-rw-r--r-- 1 ... Logging\\LogSepProbe.log
-rw-r--r-- 1 ... Logging\\LogSepProbeyyyy-MM-dd.log <- archive, same problem
(no Logging directory is created)
The archive file shows that ArchiveFileName has the same problem. The run forced size-based archiving, since a day rollover could not be waited for.
A search of the library sources for hard-coded backslashes found no other file system paths. The other hits are not paths: Application/EnvironmentProperties.cs:30 (DOMAIN\user) and the registry keys in Desktop.Browser/Internet/InternetExplorerBrowserEmulation.cs:15-16.
Proposal
Use NLog's OS-dependent ${dir-separator} in both layouts:
This was checked with the same console app by replacing both layouts at runtime:
Linux:/tmp/logsep-dirsep/Logging/LogSepProbe.log is created inside a real Logging directory, and the archive lands there too.
Windows: the rendered path is byte-for-byte the same as today (...\Logging\LogSepProbe.log), so Windows stations see no change.
Alternatives:
A forward slash (/): also works on Linux (verified). On Windows it produces mixed separators (...\Logging/LogSepProbe.log) in the rendered path. Files were still created in Logging, but archive cleanup was not tested with that path.
Path.Combine in C#: today LogDirectory is a GDC value (BGLoggerConfiguration.cs:91-102) that the layout reads when it renders the path. A combined path string would have to be rebuilt whenever LogDirectory or ApplicationName changes, which adds code for no gain.
Also update the XML doc at BGLoggerConfiguration.cs:89 ("defaults to %ProgramData%{ApplicationName}\Logging"). The default (BGLoggerConfiguration.cs:34-35) uses SpecialFolder.CommonApplicationData, which is /usr/share on Linux.
Regression test (in tests/BAUERGROUP.Shared.Test). CI runs the tests on windows-latest only (.github/workflows/dotnet-release.yml:67), so a test that only checks files on disk would pass on CI even with the bug:
Get the target via LogManager.Configuration.FindTargetByName<FileTarget>("FILE"). Assert that FileName.ToString() and ArchiveFileName.ToString() contain no \. This fails on Windows CI with the bug and passes with the fix (verified).
Additionally assert Path.GetDirectoryName(FileName.Render(LogEventInfo.CreateNullEvent())) == BGLoggerConfiguration.LogDirectory. This check only matters on a Linux or macOS runner: it is False on Linux today and True with the fix (verified).
Default data folder on Linux: without an explicit value, LogDirectory resolves to /usr/share/<App>/Logging (observed). Normal users cannot write there, and BAUERGROUP_ROAMINGAPPLICATIONDATA=TRUE does not change it (observed). Fixing only the separator therefore matters for apps that set LogDirectory themselves, or once the default folder is fixed.
Seen during the same test, on both Windows and Linux: the archive file is named <App>yyyy-MM-dd.log, with the format text taken literally. ArchiveSuffixFormat = "yyyy-MM-dd" (BGLoggerConfiguration.cs:551) has no {0}/{1} placeholder, and NLog 6 treats it as a string.Format pattern. Later archives were appended to that same file. This is worth its own issue.
Context
bgIndustrialAutomation Client (bauer-group/OT-AutomationClient, Shared.Core 3.0.8) is being brought up on Linux (Avalonia X11/Wayland) and found this there. The client has no workaround yet. Until this is fixed, an application can replace FileName / ArchiveFileName on the FILE target after BGLogger.Configuration has been created, then call LogManager.ReconfigExistingLoggers() (the test above did exactly that).
Problem
BGLoggerConfigurationbuilds the NLog file target paths with a literal backslash (main, 02325dd; unchanged since v3.0.8):src/BAUERGROUP.Shared.Core/Logging/BGLoggerConfiguration.cs:547TargetFile.FileName = @"${gdc:item=LogDirectory}\${gdc:item=ApplicationName}.log";src/BAUERGROUP.Shared.Core/Logging/BGLoggerConfiguration.cs:550TargetFile.ArchiveFileName = @"${gdc:item=LogDirectory}\${gdc:item=ApplicationName}.{#}.log";\is only a directory separator on Windows. On Linux and macOS it is an ordinary file name character, and NLog does not normalize it. So the log does not go intoLogDirectory. It goes into the parent folder ofLogDirectory, as one file whose name starts withLogging\.Observed on 2026-09-15 on Ubuntu 26.04 (WSL) with Shared.Core 3.0.8 / NLog 6.1.3, in a small console app that sets
BGLoggerConfiguration.LogDirectory = "/tmp/logsep-asis/Logging"and logs a few lines:The archive file shows that
ArchiveFileNamehas the same problem. The run forced size-based archiving, since a day rollover could not be waited for.A search of the library sources for hard-coded backslashes found no other file system paths. The other hits are not paths:
Application/EnvironmentProperties.cs:30(DOMAIN\user) and the registry keys inDesktop.Browser/Internet/InternetExplorerBrowserEmulation.cs:15-16.Proposal
Use NLog's OS-dependent
${dir-separator}in both layouts:This was checked with the same console app by replacing both layouts at runtime:
/tmp/logsep-dirsep/Logging/LogSepProbe.logis created inside a realLoggingdirectory, and the archive lands there too....\Logging\LogSepProbe.log), so Windows stations see no change.Alternatives:
/): also works on Linux (verified). On Windows it produces mixed separators (...\Logging/LogSepProbe.log) in the rendered path. Files were still created inLogging, but archive cleanup was not tested with that path.Path.Combinein C#: todayLogDirectoryis a GDC value (BGLoggerConfiguration.cs:91-102) that the layout reads when it renders the path. A combined path string would have to be rebuilt wheneverLogDirectoryorApplicationNamechanges, which adds code for no gain.Also update the XML doc at
BGLoggerConfiguration.cs:89("defaults to %ProgramData%{ApplicationName}\Logging"). The default (BGLoggerConfiguration.cs:34-35) usesSpecialFolder.CommonApplicationData, which is/usr/shareon Linux.Regression test (in
tests/BAUERGROUP.Shared.Test). CI runs the tests onwindows-latestonly (.github/workflows/dotnet-release.yml:67), so a test that only checks files on disk would pass on CI even with the bug:LogManager.Configuration.FindTargetByName<FileTarget>("FILE"). Assert thatFileName.ToString()andArchiveFileName.ToString()contain no\. This fails on Windows CI with the bug and passes with the fix (verified).Path.GetDirectoryName(FileName.Render(LogEventInfo.CreateNullEvent())) == BGLoggerConfiguration.LogDirectory. This check only matters on a Linux or macOS runner: it isFalseon Linux today andTruewith the fix (verified).Related
ApplicationName/LogDirectoryinputs, but it is a separate cause.LogDirectoryresolves to/usr/share/<App>/Logging(observed). Normal users cannot write there, andBAUERGROUP_ROAMINGAPPLICATIONDATA=TRUEdoes not change it (observed). Fixing only the separator therefore matters for apps that setLogDirectorythemselves, or once the default folder is fixed.<App>yyyy-MM-dd.log, with the format text taken literally.ArchiveSuffixFormat = "yyyy-MM-dd"(BGLoggerConfiguration.cs:551) has no{0}/{1}placeholder, and NLog 6 treats it as astring.Formatpattern. Later archives were appended to that same file. This is worth its own issue.Context
bgIndustrialAutomation Client (bauer-group/OT-AutomationClient, Shared.Core 3.0.8) is being brought up on Linux (Avalonia X11/Wayland) and found this there. The client has no workaround yet. Until this is fixed, an application can replace
FileName/ArchiveFileNameon theFILEtarget afterBGLogger.Configurationhas been created, then callLogManager.ReconfigExistingLoggers()(the test above did exactly that).