So, just started using NLog. I'm doing a programmatic implementation where I'm trying to setup a class that I can import into any project. The class has two methods: CreateLogger() and GenerateLog(). Here is the class in its entirety:
using System;
using NLog;
using NLog.Config;
using NLog.Targets;
namespace LogEngine
{
/// <summary>
/// Create an instance of NLog for use on a per class level.
/// </summary>
internal sealed class EventLog
{
#region Internal Methods
/// <summary>
/// Generates the NLog.Logger object that will control logging facilities in this program.
/// </summary>
/// <returns>
/// static reference to a <see cref="NLog.Logger" /> object.
/// </returns>
internal static Logger CreateLogger(string baseDir = @"${basedir}\")
{
// Setup log configuration object and new file and screen output targets.
var config = new LoggingConfiguration();
var screenTarget = new ConsoleTarget();
config.AddTarget("screen", screenTarget);
var fileTarget = new FileTarget();
config.AddTarget("file", fileTarget);
screenTarget.Layout = @"${newline}${message}";
var MinScreenOutput = new LoggingRule("*", LogLevel.Fatal, screenTarget);
config.LoggingRules.Add(MinScreenOutput);
// Set the properties for the file output target.
fileTarget.FileName = baseDir + @"${appdomain:format={1\}} logs\${shortdate}.log";
fileTarget.Layout = @"${longdate} ${pad:padcharacter=~:padding=29:inner= ${level:uppercase=true}}"
+ @" ${pad:padcharacter=~:padding=30:inner= Event ID\: ${event-properties:item=EventCode}}"
+ @"${newline}${message} ${when:when=level == 'Error':inner=${newline}Class / Method\:"
+ @"${pad:padding=9:inner=}${callsite:fileName=true:includeSourcePath=false:skipFrames=1}"
+ @"${newline}Exception\:${pad:padding=14:inner=}${exception}}${newline}";
// Define what sort of events to send to the file output target.
var MinOutputDebug = new LoggingRule("*", LogLevel.Debug, fileTarget);
config.LoggingRules.Add(MinOutputDebug);
// Set the configuration for the LogManager
LogManager.Configuration = config;
// Get the working instance of the logger.
return LogManager.GetLogger("LogEngine");
}
/// <summary>
/// Passes one log entry to the destination logger.
/// </summary>
/// <param name="log">
/// The <see cref="NLog.Logger" /> object to write the log entry to.
/// </param>
/// <param name="eventId">
/// Four character unique event ID as <see cref="System.String" />.
/// </param>
/// <param name="level">
/// The <see cref="NLog.LogLevel" /> value.
/// </param>
/// <param name="message">
/// The message to save to the log file as <see cref="System.String" />.
/// </param>
/// <param name="ex">
/// If this is an error log event, pass it as an <see cref="System.Exception" /> object.
/// </param>
internal static void GenerateLog(Logger log, string eventId, LogLevel level, string message, Exception ex = null)
{
// Values used for all events.
LogEventInfo logEvent = new LogEventInfo();
logEvent.Properties["EventCode"] = eventId;
logEvent.Level = level;
logEvent.Message = message;
// If we have an error log, make sure the exception is passed.
if (level.Equals(LogLevel.Error))
logEvent.Exception = ex;
// Actually write the log entry.
log.Log(logEvent);
if (level.Equals(LogLevel.Error) || level.Equals(LogLevel.Fatal))
System.Environment.Exit(Convert.ToInt32(eventId));
}
#endregion Internal Methods
}
}
In the CreateLogger() method, you'll see there is a default parameter. So, how things work is when I call CreateLogger() in my program at the start of my class, I pass no parameters and the ${basedir} value is used to generate initial logging:
internal class Program
{
#region Private Fields
private static Logger log = EventLog.CreateLogger();
#endregion Private Fields
...
However, during execution, I need to change the logging location from ${basedir} to a value that I pull from a SQL database. Here is how I do that:
if (!path.Equals(null))
{
sArgs.path = path.ToString().Trim();
//NLog.Config.SimpleConfigurator.ConfigureForFileLogging(sArgs.path + @"Logs\log1.txt", LogLevel.Debug);
//LogManager.Shutdown();
//LogManager.ReconfigExistingLoggers();
log = EventLog.CreateLogger(sArgs.path);
LogManager.ReconfigExistingLoggers();
}
"path" is an object returned by a call to SQLCommand.ExecuteScalar(). It is the replacement for ${basedir} that I need to connect my Logger to. If the path is not null, then I convert it to a string and store it into a singleton class instantiated as "sArgs". There are some commented out code here to show how I've been trying to resolve this issue.
Okay, so what I'm seeing is in the last code block (when I set "log" to a new instance generated by CreateLogger(sArgs.path)) I can see that my logging path in the log object is actually updating. But, when I get to the first opportunity to log an event, it is still using the old Logger instance (so ${basedir} is still in use, not sArgs.path).
My question is, what am I missing that is keeping the change to "log" that I can plainly see while stepping my code in the debugger from actually becoming the location for the Logger object? Or is it that I'm doing the EventLog class completely wrong?
Thank you for any insights you can provide to this problem.