NLog - how logging custom data to XML - c#

Hi I need log temperature to XML file. I would like have XMl in this format
<logs>
<log>
<dateTime></dateTime/>
<value>x</dateTime/>
<log>
<log>
<dateTime></dateTime/>
<value>x</dateTime/>
<log>
<log>
<dateTime></dateTime/>
<value>x</dateTime/>
<log>
</logs>
First I tried log data to txt file with this configuration:
<targets>
<target name="logfile" xsi:type="File" fileName="users.log" layout="${date:format=yyyyMMddHHmmss} ${message}" />
<target name ="xmlFile" xsi:type="File" fileName="log.xml"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logFile" />
</rules>
Problems is that level Info log all data I need log only custom data. I dont know which type of level I must use when I want log only some string for example.
Second problem is how create XML layout?

That's not the intended purpose of nlog. It's purpose is to help you debugging your application by writing information to one or more log sources.
What you need to use is some sort of XML serializer which will write the tempature changes to a file.

Log "custom data": Use log.Trace(...), log.Debug(...), log.Info(...) to achieve different levels of logging.
XML Layout: Look into NLog.Layouts and NLog.LayoutRenderers. Note: this isn't a relatively easy task, and considering you couldn't figure out how to achieve different levels of logging, you may want to delay this task and work on your actual project.
Edit: Are you sure you can't just output the logged data to a normal text file in simple format, and when logging is complete, parse the data and write to an XML file?

Related

NLog - 2 separate log files - How to write to one or the other

I want to explicitly write to 2 different log files. Based on the method or type of operations.
How to?
I have seen the following StackOverFlow post
Having NLog loggers with different configuration
How do I configure NLog ? What code do I need too, so I can write to 1 file or the other? using code like :log.Error("My Big Error");
The post contains the following
<targets>
<target name="f1" xsi:type="File" fileName="${logger}.txt" />
<target name="f2" xsi:type="File" fileName="${shortdate}.txt" />
</targets>
So, if it's 'general error' I want write to f1. If it's file operation error I want to write to f2
thx in advance
In the NLog configuration you need to set up some rules to write to the targets (otherwise nothing get logged). In those rules you could add filters and conditions.
Simple example
For example:
<rules>
<logger name="Logger1" writeTo="f1" />
<logger name="Logger2" writeTo="f2" />
</rules>
The name attribute is here a filter, so the first rule means: write to f1 if logger name is equals to "Logger1". The rules are processed from top to bottom.
So when calling LogManager.GetLogger("Logger1").Info("My message") this will write to target f1 and LogManager.GetLogger("Logger2").Info("My message") will write to target f2.
GetCurrentClassLogger
When using LogManager.GetCurrentClassLogger(), the logger name is constructed of the current class and namespace name, e.g. "MyNameSpace.MyClass". This means you could adjust the name attribute to name="MyNameSpace.MyClass", or name="*.MyClass" for matching.
Final
You could also write this:
<rules>
<logger name="Logger1" writeTo="f1" final="true" />
<logger name="*" writeTo="f2" />
</rules>
This will write events of Logger1 to f1, and others to f2. You probably need the filter attribute, otherwise also the events of Logger1 will be written to f2 - and that isn't always what you need.
Other filter rules
There are many more filter options, e.g. min level, max level, but also more advanced filters (not only on logger name). You could read more about that here

Programmatic change of NLog variable value is not taking effect

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!)

NLog, how to execute code on logging ERROR and higher?

I'm quite new to NLog and I wanted to execute some code when logs are done.
Basically, I got a RichTextBoxTarget. The RichTextBox is hidden by default. I want to display it when a Error log or higher is done.
How could I do this, if it's possible ?
Sounds like you would like to have 2 targets listening for events:
RichTextBox-Target for capturing all events in the error-window.
MethodCall-Target for reacting to error-events and show the error-window.
So the trick is just to setup the two targets:
<nlog>
<targets>
<target name="richtext" xsi:type="RichTextBox" />
<target name="showrichtext" xsi:type="MethodCall" className="SomeNamespace.MyClass, MyAssembly" methodName="LogMethod" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="richtext" />
<logger name="*" minlevel="Error" writeTo="showrichtext" />
</rules>
</nlog>
And implement a suitable static method for the MethodCall-Target to call.
See also: https://github.com/NLog/NLog/wiki/MethodCall-target
See also: https://github.com/NLog/NLog/wiki/RichTextBox-target
What is the purpose of this RichTextBox exactly?
If you want to show errors on input error then the ErrorProvider is better for that.
If you want to capture Error Logging also into this RTB as well as to say file, then you can write your own target, register it, and then it can process the events.
See:
https://github.com/NLog/NLog/wiki/Extending-NLog

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.

Is this the correct way to log to a specific target with NLog?

I'd like to log some special events into a different table that will contain more data then the general application log.
If I add a second database target to the NLog.config how can I use it in my code?
Would this be the right thing to do:
NLog
.LogManager
.Configuration
.AllTargets
.Single(x => x.Name == "mySecondLogTable")
.WriteAsyncLogEvent(...);
Then in the NLog.config I would just skip this target in the rules element, right?
Summary: I'd like to define multiple database targets like a general log and specialized log for cases where I need to log more details into a different table. I'd like to use the general log by default and the special log only in functions that need this special kind of logging because of their business logic.
You can always create another logger instance and use the NLog LoggingRules for redirection to the wanted target.
For example I want to make an extended logging into a separate file. Then I go and create:
<nlog>
<rules>
<!--- Notice that final=true stops the logevents from also reaching defaultTarget -->
<logger name="ExtendedLogging" minlevel="Trace" writeTo="extendedTarget" final="true" />
<!--- Wildcard rule will capture all logevents not matching the "final" rule above -->
<logger name="*" minlevel="Trace" writeTo="defaultTarget" />
</rules>
<targets>
<target name="extendedTarget" xsi:type="File" fileName="ExtendedLog_${shortdate}.log" />
<target name="defaultTarget" xsi:type="File" fileName="AppLog_${shortdate}.log" />
</targets>
</nlog>
And then I go to the code and create
private readonly Logger logger = LogManager.GetLogger("ExtendedLogging");
I don't think it's a good idea to search for something inside the config-file and perform logging through something like a backdoor. It's better to make all this things explicitly.
See also: https://github.com/nlog/nlog/wiki/Configuration-file#rules
var fileLogger = LogManager.Configuration.AllTargets.Single(x => x.Name == "file");
fileLogger.WriteAsyncLogEvents(
new LogEventInfo(LogLevel.Info, null, error.ToString())
.WithContinuation(new NLog.Common.AsyncContinuation(_ => { })));
I am not sure what did I do but it works. Because the accepted solution didn't

Categories