Search code examples
wixwindows-servicesnlogwix3.7appdata

Windows Service Logging


I´ve a Windows Service and I want to log some information with NLog. My NLog file looks like:

    <?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <time type="AccurateUtc" />

  <targets>
    <target name="debug-logfile" xsi:type="File" fileName="${specialfolder:folder=ApplicationData}/log/${date:format=yyyy-MM}.log" layout="${longdate} | ${level} | ${message}" />
    <target name="trace-logfile" xsi:type="File" fileName="${specialfolder:folder=ApplicationData}/log/trace/${date:format=yyyy-MM-dd}.log" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fffffff} | ${level} | ${message}" />
    <target name="errorfile" xsi:type="File" fileName="${specialfolder:folder=ApplicationData}/log/Error.log" layout="${longdate} | ${level} | ${message}" />
    <target name="console" xsi:type="Console" layout="${longdate} | ${level} | ${callsite} | ${message}"/>
  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="trace-logfile" />
    <logger name="*" minlevel="Debug" writeTo="debug-logfile" />
    <logger name="*" minlevel="Error" writeTo="errorfile" />
  </rules>
</nlog>

Now the problem is, the %appdata% folder for my service is: C:\Windows\System32\config\systemprofile\AppData\Roaming and NLog can´t write into this folder. May someone tell me how to get access to this folder or change it to default ServiceProfile folder? (C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\ATLED\log\trace)

I think it´s possible to change it when I give the service the local service user but it´s installed with WIX so may the problem be there. Here the WIX code sample:

<Component Id="CMP_Service" Feature="Core">
            <File Source="$(var.Servie.TargetPath)" KeyPath="yes"/>
            <ServiceInstall Id="ServiceInstallELS"
                            Name="Service"
                            Description="..."
                            Start="auto"
              Account="[SERVICEACCOUNT]"
                            ErrorControl="normal"
                            Type="ownProcess"
              Vital="no" />
            <ServiceControl Id="ServiceControllELS"
                            Name="Service"
                            Start="install"
                            Stop="both"
                            Remove="uninstall"
                            Wait="no" />
          </Component>

EDIT

With installutil.exe the service writes it´s logs into: C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\ATLED\log\trace


Solution

  • I am new to NLog. But i faced similar issues when i used Log4Net with windows service . I was setting "appdata" path manually from the code. There must be a way in NLog also to set this path manually from code.

    However when windows service runs as you mentioned it will take "appdata" path as the one you have mentioned. To overcome this, i have used WMI windows API's. Please refer the below example.

    Idea here is to get the UserName from WMI API's and construct the path manually .

     private string GetWindowsUserAccountName()
            {
                string userName = string.Empty;
    
                ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
                ObjectQuery query = new ObjectQuery("select * from win32_computersystem");
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(ms, query);
    
                foreach (ManagementBaseObject mo in searcher?.Get())
                {
                    userName = mo["username"]?.ToString();
                }
                userName = userName?.Substring(userName.IndexOf(@"\",StringComparison.InvariantCulture) + 1);
    
                FCLogger.Logger.LogDebug("[ReturnWindowsUserAccountName]UserName Fetched:" + userName);
    
                return userName;
            }
    

    You can construct path by your own , because only the UserName is unknown field here .

    var userName = GetWindowsUserAccountName();
    var appDataPath = Path.Combine(LoggerConstants.C, LoggerConstants.USERS, userName, LoggerConstants.APP_DATA, LoggerConstants.ROAMING); 
    //Ex: "C:\\Users\\" + userName from WMI +"\\AppData"+"\\Roaming" ;