Programmatic change of NLog variable value is not taking effect - c#

This is my NLog.config in which I've defined a variable logLevelMaxLength:
<?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="logLevelMaxLength" value="20" />
<targets>
<target name="logfile" xsi:type="File" fileName="logfile.log" layout="${longdate}|${pad:padding=${logLevelMaxLength}:inner=${level:uppercase=true}}|${logger}|${message}" />
<target name="logconsole" xsi:type="Console" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logconsole" />
<logger name="*" minlevel="Trace" writeTo="logfile" />
</rules>
</nlog>
I am using that variable value to pad the level printing in the log. But then in my code, I'm updating that value and trying to get the logging to use the new value for padding:
MessageBox.Show(LogManager.Configuration.Variables["logLevelMaxLength"].ToString()); //Shows old value: 20
LogManager.Configuration.Variables["logLevelMaxLength"] = "10";
MessageBox.Show(LogManager.Configuration.Variables["logLevelMaxLength"].ToString()); //Shows new value: 10
I am able to change the value programmatically and confirm the change via a MessageBox, but still the logs printed are still using the old value for printing. I even tried using the LogManager.ReconfigExistingLoggers(); command, but it didn't help. What am I doing wrong ?

When using ${logLevelMaxLength} the value can't be changed runtime. If you need this, use this syntax: ${var:logLevelMaxLength}
See docs of the var layout renderer
Performance is better when using NLog variables directly in config file (Ex. ${myvar}), instead of using this layout renderer that always performs dynamic lookup (Ex. ${var:myvar}). But this layout renderer reacts to NLog variables being modified at runtime.
Padding property
Unfortunately the padding property of ${pad} isn't layoutable (it's not of type Layout), so you can't use a dynamic value.
There are multiple solutions for that:
Edit the value from the API (e.g. C#)
Write your own padding layout renderer, which works with Layouts: https://github.com/NLog/NLog/wiki/How-to-write-a-custom-layout-renderer
Current work-around is to declare the entire Layout as NLog Config Variable, and then update the NLog Config Variable with the new complete updated Layout. Using ${var:mySpecialLayout}. See also (Thanks Rolf Kristensen!)

Related

How to programmatically configure nLog targets

I am trying to figure out how to programatically configure a NLog FallbackGroup target that has two email type sub-targets. Based on some things that happen during the start of the application, I would like to override the "to" part of the two sub-targets of the FallbackGroup target.
Currently, the app has a NLog.config file that has these targets in it. But, the values are hard-coded in it. Any change will require a redeploy of the app... That's not what we want... What we really need is the ability to modify the "to" setting in the two targets in some logic called on startup.
Here's the NLog.config file:
<?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="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<targets>
<target name="logfile" xsi:type="File" fileName="BAM_logfile.txt" />
<target xsi:type="FallbackGroup"
name="email-error"
returnToFirstOnSuccess="true">
<target xsi:type="Mail"
name="mailserver1"
to="kevin.orcutt#acme.com"
from="noreply#acme.com"
subject="Exception Message from: ${processname} v:${assembly-version} on ${machinename}"
smtpServer="smtp.acme.com"
smtpPort="25"
layout="${longdate}${newline}${windows-identity} running ${processname} v:${assembly-version} on ${machinename}${newline}At: ${callsite}${newline}Message: ${message}${newline}Exception:${newline}${exception:format=toString,Data:maxInnerExceptionLevel=10}${newline}" />
<target xsi:type="Mail"
name="mailserver2"
to="kevin.orcutt#acme.com"
from="noreply#acme.com"
subject="Exception Message from: ${processname} v:${assembly-version} on ${machinename}"
smtpServer="mail.acme.com"
smtpPort="25"
layout="${longdate}${newline}${windows-identity} running ${processname} v:${assembly-version} on ${machinename}${newline}At: ${callsite}${newline}Message: ${message}${newline}Exception:${newline}${exception:format=toString,Data:maxInnerExceptionLevel=10}${newline}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" maxlevel="Warn" writeTo="logfile" />
<logger name="*" level="Error" writeTo="email-error" />
</rules>
<extensions>
<add assembly="NLog.MailKit"/>
</extensions>
</nlog>
So my question is... Is there a way of overriding the "to" part of the sub-targets in the NLog.config file programatically, or is it best to configure the entire FallbackGroup target on application start? A second and less obvious question is I'm looking for some examples of either solution... I haven't quite figured out the code to do either... :-(
It works something like this:
Load the configuration from the appropriate XML file. This can be your app.config or a separate XML file. Then find the target by name, and cast it to the appropriate type. From there you can modify its properties however you see fit.
var xmlConfig = new XmlLoggingConfiguration("nlog.config");
var target = xmlConfig.FindTargetByName("mailserver1") as MailTarget;
target.To = "...";
You can do this with any target: load it by name, then cast it to the appropriate type. When you're done making changes, apply the configuration:
LogManager.Configuration = xmlConfig;
This should be done at startup, before you get any loggers. I think any loggers you get before applying these changes will not be affected.

NLOG & Azure table storage

everything is logging just fine except for the fact that its recording 2 columns that I believe is causing Azure Storage Explorer to freak out when sorting via the application. See image below.
Can anyone explain what the difference between Timestamp and LogTimeStamp is here?
When there are a ton of entries the sorting here gets horribly complicated where I am missing log entries.
See example of my current config file
<?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">
<!-- include this assembly as an NLog extension -->
<extensions>
<add assembly="NLog.Extensions.AzureTableStorage"/>
</extensions>
<!-- set up a an azure storage table target -->
<targets>
<target name="AzureTableStorage"
xsi:type="AzureTableStorage"
PartitionKey="${date}.${logger}"
RowKey="${guid}"
ConnectionString="UseDevelopmentStorage=true"
tableName="Bootloader" />
</targets>
<rules>
<!-- set up a rule to log to the azure storage target! -->
<logger name="*" minlevel="Trace" writeTo="AzureTableStorage" />
</rules>
</nlog>
Timestamp Time zone is UTC.
LogTimeStamp Time zone is Local.

NLog not creating log in MVC application?

I’ve used NLog on a few console apps without any issue but this is the first time I’ve used it on an MVC web application. When I try to log to a file nothing happens. There are no errors nor are any log files created. When I step through the app locally I don’t get any errors either.
First thing I did was confirm that my NLog.config file’s property was set to “Copy always” in the “Copy to Output” property. I even uninstalled NLog through NuGet and then installed it again as well as closed VS and opened up the project again.
I’ve done some searching and the only real suggestions I found were to create the folder location first and then check the permissions on the app pool. I created the folder location but that didn’t seem to work either. I’m running this through Debug mode in Visual Studio 2015 which automatically fires up a local web server so I don’t know how to find out what service it’s using to write to the file location.
In the example below I can put a break point on my ActionResult inside my controller and see a value being passed in the “gate” parameter. I step though to my test of the logger and don’t get any errors. I look in my log location and no log file is created.
Anyone have any suggestions on what I can try?
NLog.config file
<?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"
autoReload="true"
throwExceptions="true">
<variable name="LogDirBase" value="D:/logs/RampInfo"/>
<variable name="LogYear" value="${date:format=yyy}"/>
<variable name="LogMonth" value="${date:format=MM}"/>
<variable name="LogDay" value="${date:format=dd}"/>
<variable name="LogFileShortDate" value="${date:format=yyyy-MM-dd}"/>
<targets>
<target name="DefaultTarget"
xsi:type="File"
fileName="${LogDirBase}/${LogFileShortDate}.log"
encoding="utf-8"
layout="${longdate} | ${callsite} | ${message}"
maxArchiveFiles="14"
/>
</targets>
<rules>
<logger name="defaultLogger" minlevel="Debug" writeTo="DefaultTarget" />
</rules>
</nlog>
My Controller
public class RampController : Controller
{
private static Logger logger = LogManager.GetCurrentClassLogger();
// GET: GateInfo
public ActionResult Gate(string gate)
{
logger.Debug("Start action result. Gate: " + gate);
}
Logger names typically use something like this
<logger name="Name.Space.RampController" minlevel="Debug" writeTo="DefaultTarget" />
or, if you just want a single log file for your application:
<logger name="*" minlevel="Debug" writeTo="DefaultTarget" />
Internally, LogManager.GetCurrentClassLogger looks at the current Stack to determine the calling type: (silverlight conditionals removed):
public new T GetCurrentClassLogger()
{
StackFrame frame = new StackFrame(1, false);
return this.GetLogger(frame.GetMethod().DeclaringType.FullName);
}
I appreciate everyone's suggestions. I spent hours trying to figure out why the NLog wasn't working. I gave up for the day and came back to it this morning with a fresh mind. I basically started over with a whole new NLog.config and that worked. I copied out of different project a different NLog configuration. I don't really see the difference between the two but either way it's now working as expected.
Here is the new NLog.config I went with.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwExceptions="true">
<variable name="LogDirectory" value="D:\integration\logs\RampInfo"/>
<targets>
<target name="LogTarget"
xsi:type="File"
fileName="${LogDirectory}/${shortdate}.log"
encoding="utf-8"
maxArchiveFiles="14"
layout="${longdate} ${callsite} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="LogTarget" />
</rules>
</nlog>

Nlog logging methods

I am in the very alpha stage of a small website development project I am doing, and have decided to use NLog as my logging solution.
My solution is developed so far without logging. I am adding in the logging now.
An example:
private static Logger logger = LogManager.GetCurrentClassLogger();
public int SaveProject(ProjectDto project)
{
logger.Trace("SaveProject ({0}) : {1}", project.Id, _userId);
return _pb.SaveProject(project);
}
The 'GetCurrentClassLogger' method is great in that it now knows which class I am in.
But is there a way to report the Method name, instead of how I am doing it? In the example above, you can see I need to add "SaveProject" to the message. Is there a way to automatically get this? Or do I need to add this to every method logging call?
Yes, see the callsite layout renderer. You put it in your layout configuration. eg:
<?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">
<targets>
<target name="console" xsi:type="ColoredConsole" layout="${callsite:className=false:includeSourcePath=false:methodName=true} ${message}"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="console" />
</rules>
</nlog>

How to make Nlog archive a file with the date when the logging took place

We are using Nlog as our logging framework and I cannot find a way to archive files the way I want. I would like to have the date of when the logging took place in the logging file name.
Ex All logging that happend from 2009-10-01 00:00 -> 2009-10-01:23:59 should be placed in Log.2009-10-01.log. But all the logs for this day should be placed in Log.log for tailing and such.
The current NLog.config that I use looks like this.
<?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" >
<extensions>
<add assembly="My.Awesome.LoggingExentions"/>
</extensions>
<targets>
<target name="file1" xsi:type="File"
fileName="${basedir}/Logs/Log.log"
layout="${longdate} ${level:uppercase=true:padding=5} ${session} ${storeid} ${msisdn} - ${logger:shortName=true} - ${message} ${exception:format=tostring}"
archiveEvery="Day"
archiveFileName="${basedir}/Logs/Log${shortdate}-{#}.log"
archiveNumbering="Sequence"
maxArchiveFiles="99999"
keepFileOpen="true"
/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file1" />
</rules>
</nlog>
This however sets the date in the logfile to the date when the new logfile is created. Which cause frustration when you want to read logs later.
It also seems like I have to have atleast one # in the archiveFileName, which I rather not. So if you got a solution for that also I would be twice as thankful =)
Probably too late to help you, but I believe all you need to do is include the date in the file name using the date layout renderer with the proper date format. By including the date, you don't need to specify the archive features. When the date changes, a new file will be automatically created.
<?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" >
<extensions>
<add assembly="My.Awesome.LoggingExentions"/>
</extensions>
<targets>
<target name="file1" xsi:type="File"
fileName="${basedir}/Logs/${date:format=yyyy-MM-dd}.log"
layout="${longdate} ${level:uppercase=true:padding=5} ${session} ${storeid} ${msisdn} - ${logger:shortName=true} - ${message} ${exception:format=tostring}"
keepFileOpen="true"
/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file1" />
</rules>
</nlog>
Just in case if somebody still needs a solution -- requested feature has been added to NLog recently: https://github.com/NLog/NLog/pull/241, but it is still not available by Nuget
Maybe this is what you need,
Daily folder with the file Log.log in it
<target xsi:type="File" name="file1" fileName="${basedir}/logs/Log.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
</targets>

Categories