Skip to content

BGLogger file target uses a hard-coded backslash, so on Linux/macOS the log is written as a file named "Logging\<App>.log" #134

Description

@karlspace

Problem

BGLoggerConfiguration builds the NLog file target paths with a literal backslash (main, 02325dd; unchanged since v3.0.8):

  • src/BAUERGROUP.Shared.Core/Logging/BGLoggerConfiguration.cs:547 TargetFile.FileName = @"${gdc:item=LogDirectory}\${gdc:item=ApplicationName}.log";
  • src/BAUERGROUP.Shared.Core/Logging/BGLoggerConfiguration.cs:550 TargetFile.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 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:

TargetFile.FileName = "${gdc:item=LogDirectory}${dir-separator}${gdc:item=ApplicationName}.log";
TargetFile.ArchiveFileName = "${gdc:item=LogDirectory}${dir-separator}${gdc:item=ApplicationName}.{#}.log";

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).

Related

  • Single-file applications get empty application names and collapsed folders (Assembly.Location is empty) #132 (single-file apps get empty names): it changes the same ApplicationName / LogDirectory inputs, but it is a separate cause.
  • 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreleased

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions