SimonKravis
SimonKravis

Reputation: 659

NLog does not write to log file when called from Outlook Add-in

I have a C# Add-in for Outlook (Add-In Express) from which I'm trying to store some log data, but no log file is created even though the calls to logger do not fail. I am using VS 2013 in a Win 10 environment.

My NLog.Config file (stored in folder OutlookAddin\bin\Debug, the same location as OutlookAddIn.dll.config) is as follows:

<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
    <target name="file" xsi:type="File"
        layout="${longdate} ${logger} ${message}" 
        fileName="${specialfolder:ApplicationData}\FindAlike\NewMails.txt"
        keepFileOpen="false"
        encoding="iso-8859-2" />
</targets>

<rules>
    <logger name="*"  writeTo="file" />
</rules>

Code within the Add-In is declaration:

public AddinModule()
    {
        Application.EnableVisualStyles();
        InitializeComponent();
        // Please add any initialization code to the AddinInitialize event handler
    }

    private ADXOutlookAppEvents adxOutlookEvents;
    private DateTime LastReceivedDate = DateTime.Now;
    private Timer mailCheckTimer;
    public static RegistryKey SoftwareKey = Registry.CurrentUser.OpenSubKey("Software", true);
    public static RegistryKey AppNameKey = SoftwareKey.CreateSubKey("FindAlike");
    public static Logger logger = LogManager.GetCurrentClassLogger();

And routine for testing log file writing is:

public static void TestNLog()
    {
        try
        {
            NLog.LogManager.ThrowExceptions = true;

            logger.Info("test1");
            logger.Warn("test2");
            logger.Error("test3");

            var fileTarget1 = (FileTarget)NLog.LogManager.Configuration.FindTargetByName("file");
            var logEventInfo = new LogEventInfo { TimeStamp = DateTime.Now };
            string fileName = fileTarget1.FileName.Render(logEventInfo);
            if (!System.IO.File.Exists(fileName))
                throw new Exception("Log file does not exist.");
        }
        catch (Exception Ex)
        {
            MessageBox.Show(Ex.Message);
        }
    }

When TestNLog is called, the Message that the log file does not exist appears, although the target file is correct, indicating that the config file has been successfully read.

The same code works as expected when included in an executable.

Upvotes: 3

Views: 1101

Answers (2)

SimonKravis
SimonKravis

Reputation: 659

Another StackOverflow question ( How to use NLog for a DLL) suggested that NLog.config needs to be placed in the same directory as the executable that calls the Add-in. This fixed the problem. However, this makes distribution very difficult as the location of the Outlook executable will vary according to Outlook version and Administrator privilege is needed to copy a file into it. Perhaps another logger will not require this.

Thanks to suggestion from @Julian the following code programmatically specified the NLog configuration and ran successfully from the Add-in:

    using NLog;
    using NLog.Config;
    using NLog.Targets;
    using System.Xml;
...

public static Logger logger = LogManager.GetCurrentClassLogger();

....   
    public static void ConfigNLog()
            {

            string xml = @"
            <nlog xmlns=""http://www.nlog-project.org/schemas/NLog.xsd""
                xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
                <targets>
                    <target name=""file"" xsi:type=""File""
                            layout=""${longdate} ${logger} ${message}"" 
                            fileName=""${specialfolder:ApplicationData}\FindAlike\NewMails.txt""
                            keepFileOpen=""false""
                            encoding=""iso-8859-2"" />
                </targets>
                <rules>
                    <logger name=""*""  writeTo=""file"" />
                </rules>
            </nlog>";

            StringReader sr = new StringReader(xml);
            XmlReader xr = XmlReader.Create(sr);
            XmlLoggingConfiguration config = new XmlLoggingConfiguration(xr, null);
            NLog.LogManager.Configuration = config;
        }

Upvotes: 0

Julian
Julian

Reputation: 36740

In addition to @SimonKarvis's answer, the location of the nlog.config could be difficult. This is the same for unit tests.

I would recommend:

  1. Creating the config from C#, e.g.

     var target = new FileTarget
     {
           FileName = logfile,
           ReplaceFileContentsOnEachWrite = true,
           CreateDirs = createDirs
     };
     var config = new LoggingConfiguration();
    
     config.AddTarget("logfile", target);
    
     config.AddRuleForAllLevels(target);
    
     LogManager.Configuration = config;
    
  2. Or load the config from a string:

    string configXml = "<nlog>...<nlog>";
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(configXml);
    var config = new XmlLoggingConfiguration(doc.DocumentElement, Environment.CurrentDirectory);
    LogManager.Configuration = config;
    
  3. Or least but not last, find the correct path of nlog.config and "feed" it to NLog.

    var pathToNlogConfig = "c:\..";
    var config = new XmlLoggingConfiguration(pathToNlogConfig);
    LogManager.Configuration = config;
    

Upvotes: 4

Related Questions