I use a NLog.config, but some values I want to change from outside. E.g. target:adress could change so I want to set it each time the software starts.
I imagine some thing like
var logger = new LoggerFactory().AddNLog().CreateLogger<Program>();
logger.target.adress = "myNewAdress";
How can I set values to my NLog.config?
You could edit the config in C# like this:
var configuration = LogManager.Configuration;
var fileTarget = configuration.FindTargetByName<FileTarget>("myTargetName");
fileTarget.FileName = "${basedir}/file.log";
LogManager.Configuration = configuration; //apply
Please note that combining the config file (nlog.config) and changing it in code, the reload of nlog.config could undo your changes. If you combine both, then reapply the changes on the reload event. E.g.
public void UpdateConfig()
{
var configuration = LogManager.Configuration;
var fileTarget = configuration.FindTargetByName<FileTarget>("myTargetName");
fileTarget.FileName = "${basedir}/file.log";
LogManager.Configuration = configuration; //apply
}
// On start of your program
UpdateConfig();
LogManager.ConfigurationReloaded += (sender, e) =>
{
//Re apply if config reloaded
UpdateConfig();
};
See also: https://github.com/NLog/NLog/wiki/Configure-from-code
I recommend that one makes use of the NLog context layoutrenderers instead of modifying target properties at runtime. It ofcourse requires that the target-property supports NLog Layout.
Example of NLog.config:
<nlog>
<targets>
<target type="file" name="file" fileName="${gdc:item=LogDir}\LogFile.txt}" />
</targets>
<rules>
<logger minLevel="Trace" writeTo="file" />
</rules>
</nlog>
Then at runtime you can change the GDC-variables:
NLog.GlobalDiagnosticsContext.Set("LogDir", "C:\\Temp");
${gdc} Can also be combined with WhenEmpty, so one can provide a fallback default value, when nothing has been asigned from code.
See also: https://github.com/NLog/NLog/wiki/Context
Related
I have an application that listens for a hash input, processes that input, and serves it back. I would like to create a separate log directory for each input with the following structure:
- hashcode1
- input1.txt
- output1.txt
- log1.txt
- hashcode2
- input2.txt
- output2.txt
- log2.txt
I've looked into the NLog library but it seems the config requires a hardcoded log location, whereas my implementation requires the hashcode as an input. How can I achieve this with NLog, or any other logging library?
You can create the NLog configuration from code. This allows you to dynamically create targets in your processing loop for the appropriate log outputs:
using NLog;
using NLog.Targets;
// Init the config if we don't have a nlog.config
LogManager.Configuration = new();
// Do the work
var work = new List<string> { "5dd2ec2f", "24e1e841", "05137e70" };
work.ForEach(ProcessHash);
void ProcessHash(string hash)
{
// Create dynamic configuration and logger
var fileTarget = new FileTarget(hash) { FileName = $"{hash}/log.txt" };
LogManager.Configuration.AddRuleForAllLevels(fileTarget, hash);
LogManager.ReconfigExistingLoggers();
var logger = LogManager.GetLogger(hash);
// Process...
logger.Info($"Processing hash {hash}");
/* ... your code here ... */
// Remove the logging target
LogManager.Configuration.RemoveTarget(fileTarget.Name);
}
You can create a scope-property like this:
void ProcessHashCode(string hashInput)
{
using (NLog.MappedDiagnosticsLogicalContext.SetScoped("HashCode", hashInput))
{
NLog.LogManager.GetLogger("HashInput").Info(hashInput);
NLog.LogManager.GetCurrentClassLogger().Info("Received Hashcode");
var hashOutput = hashInput.GetHashCode();
NLog.LogManager.GetLogger("HashOuput").Info(hashOutput);
}
}
Then you can do this in NLog.config
<nlog>
<variable name="logdir" value="${mdlc:HashCode:whenEmpty=${basedir}}" />
<targets>
<target name="hashinput" type="file" filename="${logdir}/input.txt" />
<target name="hashouput" type="file" filename="${logdir}/output.txt" />
<target name="hashlog" type="file" filename="${logdir}/log.txt" />
</target>
<rules>
<logger name="HashInput" writeTo="hashinput" final="true" />
<logger name="HashOuput" writeTo="hashouput" final="true" />
<logger name="*" writeTo="hashlog" />
</rules>
</nlog>
See also: https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer
Alternative you can use Logger.WithProperty("HashCode", hashInput) like this:
void ProcessHashCode(string hashInput)
{
var inputLogger = NLog.LogManager.GetLogger("HashInput").WithProperty("HashCode", hashInput);
var outputLogger =
NLog.LogManager.GetLogger("HashOutput").WithProperty("HashCode", hashInput);
var hashLogger =
NLog.LogManager.GetCurrentClassLogger.WithProperty("HashCode", hashInput);
inputLogger.Info(hashCode);
hashLogger.Info("Received Hashcode");
var hashOutput = hashInput.GetHashCode();
outputLogger.Info(hashOutput);
}
And instead use variable like this:
<variable name="logdir" value="${event-properties:HashCode:whenEmpty=${basedir}}" />
See also: https://github.com/NLog/NLog/wiki/Context#logevent-properties
I have a NLog.config file with the following target:
<?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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="true"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log" >
<variable name="ProgramDataPath"
<target name="MyLog" xsi:type="File" fileName="${ProgramDataPath}/MyLog.txt"
archiveFileName="${ProgramDataPath}/Archive/Mine/{#}_MyLog.txt" archiveNumbering="Date" archiveEvery="Day" archiveDateFormat="dd-MM-yyyy" maxArchiveFiles="7" />
I'd like to be able to modify the 'archiveEvery' property by setting it to either 'Day' or 'Month' value each time I start my app, and that should be done by reading another config file where I have the property:
<IsMylogDailyArchiveEnabled>false</IsMylogDailyArchiveEnabled>
False means it should log monthly, True - daily.
So far I am able to read the later, however nothing inside NLog.config seems to be changed after I start my app...
public string IsMylogDailyArchiveEnabled
{
get { return _isMylogDailyArchiveEnabled ; }
set
{
_isMylogDailyArchiveEnabled = value;
//Here I can see that 'false' is being returned as it should
EnableMyLogDailyArchiving(_isMylogDailyArchiveEnabled );
}
}
private void EnableMyLogDailyArchiving(string value)
{
var config = new XmlLoggingConfiguration("NLog.config");
var target = config.FindTargetByName("MyLog") as FileTarget;
if (value == "false")
{
//Does not work
target.ArchiveEvery = FileArchivePeriod.Month;
}
else
{
target.ArchiveEvery = FileArchivePeriod.Day;
}
//LogManager.ReconfigExistingLoggers();
LogManager.Configuration = config;
}
Tried both options 'LogManager.ReconfigExistingLoggers()' and reassigning configuration (see above), however as mentioned before, I can't see any changes inside the NLog.config file.
Any thoughts or ideas? Thanks
It should work,
When doing LogManager.Configuration = config;, the configuration is reloaded and all targets are initialized. So the new ArchiveEvery setting will be used.
But be aware that auto reload is enabled (autoReload="true"), so every change in the XML config will revert the change made in code. If you don't want that, then change the config by subscribing to
LogManager.ConfigurationReloaded
PS: it's recommend to disabled throwExceptions in production (e.g. by removing throwExceptions="true")
I have a nlog.config file with the following entry:
<include file="${basedir}/ActiveConfig/NLog/*.config"/>
But I want to solve this via code behind and have not found a method to do so.
LogManager.LoadConfiguration()
is overwriting my existing configuration.
Did I miss something?
You could set it like this:
var config = XmlLoggingConfiguration
.CreateFromXmlString("<include file='${basedir}/ActiveConfig/NLog/*.config' />");
LogManager.Configuration = config; // apply
Think the easy solution is just to load all config-files into an in-memorystream, and then load that into a single XmlLoggingConfiguration:
var xmlReader = System.Xml.XmlReader.Create(memorystream);
NLog.LogManager.Configuration = new XmlLoggingConfiguration(xmlReader, null);
Something like this, where you put the contents of all config-files within the same <nlog>-root:
<nlog>
<!-- XML File 1 -->
<targets>
</targets>
<rules>
</rules>
<! -- XML File 2 -->
<targets>
</targets>
<rules>
</rules>
</nlog>
If you want the AutoReload-feature working also, then this might work:
class MySpecialLoggingConfiguration : XmlLoggingConfiguration
{
private string[] _fileNames;
public MySpecialLoggingConfiguration(string[] fileNames)
{
_fileNames = fileNames;
// Your special concat-logic in memory
}
public override LoggingConfiguration Reload()
{
return new MySpecialLoggingConfiguration(_fileNames);
}
public override IEnumerable<string> FileNamesToWatch => _fileNames;
}
I have a project which uses the below code to create a NLog instance.
public FileTarget CreateNLogFileTarget(string layout, FileArchivePeriod archiveMode, int maxArchiveFiles,
bool keepFileOpen, bool enableConcurrentWrites, ArchiveNumberingMode archiveNumberingMode, string fileName)
{
FileTarget fileTarget = new FileTarget();
fileTarget.Layout = layout;
fileTarget.ArchiveEvery = archiveMode;
fileTarget.MaxArchiveFiles = maxArchiveFiles;
fileTarget.KeepFileOpen = keepFileOpen;
fileTarget.ConcurrentWrites = enableConcurrentWrites;
fileTarget.ArchiveNumbering = archiveNumberingMode;
fileTarget.FileName = fileName;
return fileTarget;
}
FileTarget infoLogFileTarget = CreateNLogFileTarget(#"${longdate} ${message}",
FileArchivePeriod.Hour, 70, false, true, ArchiveNumberingMode.Rolling, "${basedir}/Logs/" + infoLogName + "/${shortdate}{#}.log");
I am using this project in another project and I need to use this NLog utility class to create my loggers. But I need to override these configurations. How can I override these configurations through the xml file? Any help would be much appreciated.
To use the FileTarget from CreateNLogFileTarget in your XML config, you should first find out the target name of the FileTarget it's probably in other parts of the code. Then you could use the target in your config:
<logger name='*' minlevel="Trace" writeTo='theTarget' />
Maybe by using NLog-variables. Change your CreateNLogFileTarget to setup the parameters to get their value from NLog-variables.
Then on startup check if these NLog variables already exists in the loaded NLog-configuration. If not then they are set by the runtime, before calling CreateNLogFileTarget.
https://github.com/NLog/NLog/wiki/Configuration-file#variables
I need to write event log for every instance of class to separate file. Historically project uses NLog for logging, so I want to try resolving this issue using NLog (I've found similar topic Unique log file for each instance of class , but it's suggests using log4net)
Currently I'm getting instance of logger like this:
public static Logger GetInstanceLogger(string name, bool init = false)
{
if (!LogManager.Configuration.AllTargets.Any(t => t.Name == name))
{
var target = new FileTarget();
target.Name = name;
target.FileName = string.Format("logs/{0}.${{shortdate}}.log", name);
target.Layout =
"${date:format=dd.MM.yyyy HH\\:mm\\:ss.fff} thread[${threadid}] ${logger} (${level:uppercase=true}): ${message}. ${exception:format=ToString}";
var config = init ? new LoggingConfiguration() : LogManager.Configuration;
config.AddTarget(name, target);
var ruleInfo = new LoggingRule("*", LogLevel.Trace, target);
config.LoggingRules.Add(ruleInfo);
LogManager.Configuration = config;
LogManager.ReconfigExistingLoggers();
}
var logger = LogManager.GetLogger(name);
return logger;
}
Right now it's writing same log into all files (I suppose it's caused by the log level). Is there a way to accomplish this task using NLog?
Thanks.
I came out with solution using event properties of layout renderer in the filename. When I'm writing new message to log, I'm adding filename as a property for LogEventInfo
protected virtual void SendEvent(LogLevel level, string message, Exception exception, string memberName = null)
{
var logEvent = new LogEventInfo(level, _name, message);
logEvent.Exception = exception;
foreach (String key in _properties.Keys)
{
logEvent.Properties[key] = _properties[key];
}
_logger.Log(logEvent);
}
And in configuration file in NLog targets section:
<targets async="true">
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}_${event-properties:item=Name}.log"
layout="${detailedLayout}" />
<target xsi:type="File" name="errorLogFile" fileName="${basedir}/logs/${shortdate}.ERROR_${event-properties:item=Name}.log"
layout="${detailedLayout}" />
</targets>
try var ruleInfo = new LoggingRule(name, LogLevel.Trace, target);