Log only to a specific target at runtime - c#

I am using NLog with two targets:
<?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 async="true">
<target name="logfile" xsi:type="File" fileName="my.log"/>
<target name="console" xsi:type="Console"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile"/>
<logger name="*" minlevel="Info" writeTo="console"/>
</rules>
</nlog>
Is it possible to log a message only to the "logfile" target, without having the message written to the "console" target as well?
EDIT
To clarify: I want to direct messages from the same class to different loggers at run time (w/o having to change the XML). Something like:
class Program
{
static Logger _logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
// Note - _logger.InfoToTarget() does not really exist
_logger.InfoToTarget("logfile", "This is my very detailed message, possibly with object dumps");
_logger.InfoToTarget("console", "Short message");
}
}
I'm aware that this couples my code with the NLlog.config file.

One way to accomplish the functionality you are looking for is to name your logger
_logger = LogManager.GetLogger("MyConsoleLogger")
_logger.Info("This will log to the console...");
_logger = LogManager.GetLogger("MyFileLogger")
_logger.Trace("This will log to a file...");
rather than using
LogManager.GetCurrentClassLogger().
In your config file you could then list in the rules
<rules>
<logger name="MyFileLogger" minlevel="Trace" writeTo="logfile"/>
<logger name="MyConsoleLogger" minlevel="Info" writeTo="console"/>
</rules>
This is by far not the most pretty solution to look at, but it does give you the functionality that you are looking for.

There are a few ways to do this, and the correct method depends on your situation.
Keep in mind that you typically want to avoid having your app know too much about the inner-workings of logging. If possible, it's best to configure nlog to decide where things should get logged.
Is there a specific namespace that should not be logged to console? That's easy to configure. Also, you can use the "When" filter (https://github.com/nlog/nlog/wiki/When-filter) or conditions (https://github.com/nlog/nlog/wiki/Conditions)
It may also be best to have multiple logger instances, so you can call the one that is appropriate for each situation (logger per class) (Why do loggers recommend using a logger per class?).

Absolutely, however I am assuming you mean at release you no longer wish to log to the console. You can do this very easily by removing or commenting out the listener that writes to the console target. Now it will only write to the logfile 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">
<targets async="true">
<target name="logfile" xsi:type="File" fileName="my.log"/>
<target name="console" xsi:type="Console"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile"/>
<!--<logger name="*" minlevel="Info" writeTo="console"/>-->
</rules>
</nlog>
The rule that writes to the console is now deactivated, but the log file is active. If this is during release you probably want to change your rule to not process your trace logging as the min level for the log file since it will slow down your app with excessive IO. I have asked this question in the past and it appears that best practice is to do this via the XML configuration files. (Logging in Release Build of Application (C#))

Related

NLog logger doesn't log to the configured logfile in release build

I have the configuration file set up
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<nlog autoReload="true">
<targets>
<target name="file" type="File" fileName="${basedir}/log/${shortdate}.log" layout="${date:format=HH\:mm\:ss.fff}|${message}"/>
<target name="file_webs" type="File" fileName="${basedir}/log/${shortdate}_webs.log" layout="${date:format=HH\:mm\:ss.fff}|${message}"/>
</targets>
<rules>
<logger name="WebSocket.*" minlevel="Debug" writeTo="file_webs" final="true"/>
<logger name="*" minlevel="Debug" writeTo="file" />
</rules>
</nlog>
</configuration>
The logger is loaded in each class like this:
private static Logger logger = LogManager.GetCurrentClassLogger();
The logging is directed to the correct file as long as I don't run the build version. Then all logging is done in the default log file.
What could be the cause?
Check the following:
Is the correct config file placed in the output directory of the release. Maybe there is a different version of your config file there or it is missing.
Are there other things running after your build? Like a tool to obfuscate the source code? If so, this might mess up the class name in your build, the config the can not redirect to the right output. In that case, explicitly load the logger for your class this way:
private static Logger logger = LogManager.GetLogger("WebSocket.*");

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 - Archive Start / Stop Time

Just started working with NLog and have it running with the following configuration:
<?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"
throwConfigExceptions="true">
<targets async="true">
<target name="logfile"
xsi:type="File"
layout="${longdate} [${level:uppercase=true}] (${threadid}) ${logger}: ${message} ${onexception:${newline}Exception\: ${exception:format=type,message,method,stacktrace:maxInnerExceptionLevel=5:innerFormat=shortType,message,method}}"
fileName="logs/current.log"
archiveFileName="logs/Archive/${ticks}.log"
archiveEvery="Minute"
archiveOldFileOnStartup="true"
keepFileOpen="false"
/>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
</nlog>
Everything is working as expected. However, I need to have the rotated file be in the format of ${archive_start_ticks}_${arhive_end_ticks}.log rather than the current format which is ${archive_end_ticks}.log.
I was initially hoping I could name the active log file as ${ticks} and then, on archive, use the active log file's name as a parameter into the archive file to compose some like:
fileName="logs/${ticks}"
archiveFileName="logs/Archive/${fileName}_${ticks}.log"
Of course, there's two issues here:
Using ${ticks} for the active file creates a new file for each log line.
I can't seem to reference the original fileName as an input variable into archiveFileName.
That said, what is the best way to achieve this goal? Is this something NLog can handle natively or with minor extensions?
Updating in case anyone ever cares:
I bailed on using the FileTarget with configurations and wrote my own Target wrapped in a BufferedWrapper. On each flush, I use the first and last LogEvents to determine the timespan which gives me what I need to for the required file format with little custom code to support.

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

Why does NLog miss some messages when logging a large number of messages?

I try to test NLog performance (latest version) with settings:
<?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">
<variable name="basePath" value="c:\logs\" />
<variable name="msgFormat" value="${message}" />
<targets async="true">
<target name="file"
xsi:type="File"
fileName="${basePath}/${logger}/${date:format=yyyy}/${date:format=MMMM}/log-${date:format=yyMMdd}-${level}.log"
layout="${msgFormat}"
concurrentWrites="true" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file"/>
</rules>
</nlog>
and run this code:
var msg = "this is example string for logging test. it's not very long, but not very short";
var count = 20000;
Parallel.For(0, count, x => nlog.Info(msg));
NLog writes to file, but when file size reaches 1MB it stops writing. I try to use simple for loop, but it doesn't helped me.
And i try to use internal logging, but there is no errors, by the way i see there this strings:
2013-04-01 11:36:18.2458 Trace Opening
c:\logs/NLogTest/2013/April/log-130401-Info.log with
concurrentWrite=False
It's very strange, because concurrentWrites default value is true, furthermore I've set this value in config.
The problem lies in the default value of the AsyncWrappers QueueLimit, which is 10000.
The value determines how big the queue of messages to write are allowed to be, the problem arises because all 20000 messages are queued before anything is written to the file, which causes NLog to discard the last 10000 messages.
Unfortunately this cannot be changed when using the async attribute, you have to define the AsyncWrapper manually to be able to control the QueueLimit, which is done 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" autoReload="true">
<variable name="basePath" value="c:\logs\" />
<variable name="msgFormat" value="${message}" />
<targets async>
<target name="asyncWrapper" xsi:Type="AsyncWrapper" queueLimit="20000">
<target name="file"
xsi:type="File"
fileName="${basePath}/${logger}/${date:format=yyyy}/${date:format=MMMM}/log-${date:format=yyMMdd}-${level}.log"
layout="${msgFormat}"
concurrentWrites="true" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file"/>
</rules>
</nlog>
Where QueueLimit is set to 20000.
You could also changed the OverflowAction if you need to do something other the discard messages not put in the queue, see AsyncWrapper documentation for more information. The options are Block, Discard or Grow.

Categories

Resources