Configure NLog to log at set days of week - c#

I have a service running hourly that uses NLog to send mail when exception occurs. So how do I configure it to not log at Saturday and Sunday? Can I even do that?
Here's my target:
<target name="m"
xsi:type="Mail"
html="true"
smtpServer="*******"
smtpPort="*******"
smtpAuthentication="*******"
smtpUserName="*******"
smtpPassword="*******"
enableSsl="true"
subject="Exception"
from="*******"
to="*******"
layout ="${longdate} ${uppercase:${level}}${newline} ${callsite:className=true:includeSourcePath=true:methodName=true}${newline} ${message}${newline}"
useSystemNetMailSettings="false" />
And rule:
<logger name="*" minlevel="Fatal" writeTo="m" />

Maybe use the PostFilteringWrapper, and use a filter condition like:
not equals('${date:format=ddd}', 'Sat')
not equals('${date:format=ddd}', 'Sun')
https://github.com/NLog/NLog/wiki/PostFilteringWrapper-target
https://github.com/NLog/NLog/wiki/When-filter
https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-extract-the-day-of-the-week-from-a-specific-date

Related

Using NLog to output to one of two log files

I have a single-threaded .NET Winforms application. I am using a static NLog logger to log errors using this syntax:
MyLogger.Log.Error("Error occurred...");
In accordance with my one NLog.config-defined target and one NLog.config-defined rule, that log-error output always is written to a file called "errors.txt". Sometimes I want an error to be written to "errors2.txt" instead. How would I do that without creating another static logger instance?
You could use filters in the config or pass the filename to the logger call.
Note: These examples are using .WithProperty (on the NLog Logger), introduced in NLog 4.6.3
Filters
Use filters in the logger rules, we use the LogFinal so we need only the rules once (not needed for the 2nd logger rule)
See Filtering log messages on the NLog wiki
<rules>
<logger name="*" writeTo="file-error1">
<filters defaultAction='LogFinal'>
<when condition="${event-properties:target} == 'error2'" action="Ignore" />
</filters>
</logger>
<logger name="*" writeTo="file-error2">
</logger>
</rules>
Logger call:
MyLogger.Log.Error("Error occurred..."); // writes to file-error1 target
MyLogger.Log.WithProperty("target", "error2").Error("Error occurred..."); // writes to file-error2 target
Pass file name
Pass the file name in as property. Maybe you could set a default property in the static Log method.
See also Event properties documentation
<targets>
<target name="file" xsi:type="File"
....
fileName="${basedir}/logs/{event-properties:filename}"
... />
</targets>
Logger call:
MyLogger.Log.WithProperty("filename", "error1.txt").Error("Error occurred...");
MyLogger.Log.WithProperty("filename", "error2.txt").Error("Error occurred...");

NLog for asp.net core is not filtering Microsoft logs

I have the following nlog.config file in ASP.NET Core 2.1 project. However, it's logging messages from every logger (including Microsoft logger) to console.
<?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"
internalLogLevel="Error"
internalLogFile="${specialfolder:folder=UserProfile}\nlog\internal-nlog.txt">
<variable name="fullLayout" value="${shortdate} [${uppercase:${level}}] ${logger}: ${message} ${exception:format=tostring} url: ${aspnet-request-url}" />
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write to console -->
<target name="console" xsi:type="ColoredConsole" layout="${fullLayout}" />
<!-- write to file -->
<target xsi:type="File"
name="allfile"
fileName="${defaultDirectory}/test-api-all.log"
archiveEvery="Day"
archiveFileName="${defaultDirectory}/test-api-all-${#}.log"
archiveNumbering="Date"
archiveDateFormat="yyyy-MM-dd"
maxArchiveFiles="5"
layout="${fullLayout}" />
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<target xsi:type="File"
name="ownFile-web"
fileName="${defaultDirectory}/test-api-app.log"
archiveEvery="Day"
archiveFileName="${defaultDirectory}/test-api-app-${#}.log"
archiveNumbering="Date"
archiveDateFormat="yyyy-MM-dd"
maxArchiveFiles="5"
layout="${fullLayout}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="console" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
I do not have any logging settings in appsettings.json files. All logger configuration in in nlog.config. In the Program.cs, I'm registering NLog like:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging => logging.ClearProviders())
.UseNLog()
.Build();
How can I filter out Microsoft logs?
EDIT:
Above configuration started working without a problem the next day without me making any changes :O
Maybe this will work:
<!-- rules to map from logger name to target -->
<rules>
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="console" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
The order of the logging rules are important, as the rules are matched from the top. See also https://github.com/NLog/NLog/wiki/Configuration-file#rules
The order of your logging rules seems to be correct. I suggest trying to double check if your updated nlog.config file is being copied to the build directory (e.g. \bin\Debug\netcoreapp2.1\nlog.config)
I kind of encountered the same issue, but found out that debugging using Visual Studio, it sometimes doesn't really copy the nlog.config file to your build directory. So the solution is to Clean the project first, then Build, and finally Debug.

Is there a fail over mechanism with NLog?

I am using NLog for DB logging. If the DB is down then I only need it to log to a file. Is there a way to do this?
I don't want to record log entries log in both a file and the DB. If the DB target fails to log (if DB is down) only then do I have to log to the file. Is there any fail over or fallback feature with Nlog?
Yes, there is a way. It's called a FallbackGroup.
Documentation can be found here: https://github.com/nlog/NLog/wiki/FallbackGroup-target
Here is an example taken blatantly and ashamedly from the documentation:
<target xsi:type="FallbackGroup"
name="mail"
returnToFirstOnSuccess="true">
<target xsi:type="Mail"
name="mailserver1"
subject="Layout"
to="Layout"
from="Layout"
smtpServer="mx1.example.com"
smtpPort="Integer"
layout="Layout" />
<target xsi:type="Mail"
name="mailserver2"
subject="Layout"
to="Layout"
from="Layout"
smtpServer="mx2.example.com"
smtpPort="Integer"
layout="Layout" />
</target>
<rules>
<logger name="*" minlevel="Trace" writeTo="mail" />
</rules>
It'll try another mail server if the first fails. It'll also return to the first target if it's successful.
I never used it in production, maybe the other options in the documentation can help you fine tune it to your problem.

NLog Header (Ignore layout)

Is is possible to write a line to the log file that's not a log?
I'd like to write a "Note" that will ignore the layout in my NLog.config and just write the text exactly as I pass it for one or two lines.
This could be used for writing a header on the log file.
I'm not sure if this will matter to the solution but I'm using a separate ClassLogger (LogManager.GetCurrentClassLogger()) for every class and it's not an option for me to change that.
I was able to do this with a few creative rules.
First in my code I created a logger specifically for the header like this:
var loggerNoLayout = LogManager.GetLogger("NoLayout");
loggerNoLayout.Info("<Header Line 1>");
loggerNoLayout.Info("<Header Line 2>");
loggerNoLayout.Info("<Header Line 3>");
Then I modified my NLog.config 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">
<targets>
<target name="NoLayout"
xsi:type="File"
fileName="${specialfolder:folder=Desktop}\LogFile.log"
layout="${message}" />
<target name="FileTrace"
xsi:type="File"
fileName="${specialfolder:folder=Desktop}\LogFile.log"
layout="${level:padding=-5:fixedLength=true} | ${logger:padding=-62:fixedLength=true:alignmentOnTruncation=right}| ${date:format=HH\:mm\:ss} | ${message:padding=-100}"
deleteOldFileOnStartup="true" />
</targets>
<rules>
<logger writeTo="NoLayout"
levels="Info"
name="NoLayout"
final="true" />
<logger writeTo="FileTrace"
minlevel="Trace"
name="*" />
</rules>
</nlog>
The key here is that the NoLayout target's layout only writes the message and the rule for writing to NoLayout only works if the logger is named NoLayout. The final step is to add the final="true" so that my normal (FileTrace) logger does not pick up the header logs.
Sure. Write a string to the log that starts with a newline. Then write your content.
var longMultilineMessage = "pretend this variable contains\n a long multiline message";
logger.Info($"\n{longMultilineMessage}");
will show up in logs as :
2017-01-01 INFO
pretend this variable contains
a long multiline message

Configure NLog programmatically, web.config

I have following Nlog config section in my web.config (modified to show only relevant info)
<nlog>
<targets async="true">
<target name="mail" type="Mail"
body="${date:format=yyyy-MM-dd HH\:mm\:ss} - ${level} [${logger}] - ${message} ${newline}${newline}${event-context:item=OtherInfo}${newline}${newline}${exception:maxInnerExceptionLevel=2:format=ToString}${newline}${newline}"
subject="[${machinename}] ${logger}"
to="mail#domain.com" encoding="UTF-8" from="anotheremail#domain.com" smtpServer="" enableSsl="true" smtpAuthentication="Basic"
/>
</targets>
<targets>
<target name="mailsync" type="Mail" body="${date:format=yyyy-MM-dd HH\:mm\:ss} - ${level} [${logger}] - ${message} ${newline}${newline}${event-context:item=OtherInfo}${newline}${newline}${exception:maxInnerExceptionLevel=2:format=ToString}${newline}${newline}" subject="[${machinename}] ${logger}"
to="mail#domain.com" encoding="UTF-8" from="anotheremail#domain.com" smtpServer="" enableSsl="true" smtpAuthentication="Basic"
/>
</targets>
<rules>
<logger name="*" level="Error" writeTo="mail" />
</rules>
</nlog>
I am updating the configuration via code in Application_Start,
var config = LogManager.Configuration;
const string targetName = "mail";
var wrapper = (AsyncTargetWrapper) config.FindTargetByName(targetName);
wrapper.WrappedTarget.SmtpServer = "hostname";
wrapper.WrappedTarget.SmtpUserName = "username";
wrapper.WrappedTarget.SmtpPassword = "password";
config.RemoveTarget(targetName);
config.AddTarget(targetName, wrapper);
LogManager.Configuration = config;
However, when I log any errors, the emails are not being sent. I have an additional file target (not shown in the code snippet), that contains the error message. That tells me the errors are getting logged, but somehow not sent via email.
If instead of updating the config via code, if I hard code the values in the web.config, then the emails get sent. I have verified that the smtp values that I am using via code are valid.
I did search through many similar questions on SO, but I haven't yet found one that mentions a solution that works for me.
EDIT:
Based on Xharze's answer, I enabled exceptions, internal logging and I also outputted the values of the target after I had made my changes. The internal log showed an exception about a MailAdrress being the incorrect format. So I checked all target values that accept an email address and I found the issue. The from property of the target accepts an email address whereas I was providing it a display name!
Could you try enabling throw exceptions and the interalnal log, and post it? It may provide more information. See github.com/NLog/NLog/wiki/Logging-troubleshooting.

Categories

Resources