I have a class that should be logging as usual, but sometimes it should be logging to a specific target(eg. a file).
Here are my NLog config files:
nlog.config
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="C:\Logs\logfile.log"
internalLogLevel="Info" >
<targets>
<target xsi:type="File" name="logfile" fileName="C:\Logs\logfile.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
and extra.config
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="C:\Logs\extra.log"
internalLogLevel="Info" >
<targets>
<target xsi:type="File" name="extra" fileName="C:\Logs\extra.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Trace" writeTo="extra" />
</rules>
</nlog>
I am trying something like this:
public class Program
{
private static IConfiguration _configuration;
public static void Main(string[] args)
{
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
var environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production";
_configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json", false)
.AddJsonFile($"appsettings.{environment}.json", true)
.Build();
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host
.CreateDefaultBuilder(args)
.ConfigureLogging(logBuilder =>
{
logBuilder.ClearProviders();
logBuilder.SetMinimumLevel(LogLevel.Trace);
})
.ConfigureServices((hostContext, services) =>
{
//serviceConfig
})
.UseNLog()
.UseWindowsService();
}
And the class I want two loggers in:
public class MyClass
{
private readonly ILogger<MyClass> _logger;
private readonly Logger _extraLogger;
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
_extraLogger = NLogBuilder.ConfigureNLog("extra.config").GetLogger("extra");
}
public void doBothTypesOfLogging()
{
var msg = "Hello";
_extraLogger.Info(msg);
_logger.LogInformation("Message sendt");
}
}
But this makes all logging done by the application(even other classes) end up in the extra.log file which is defined by the extra.config. Eg. both msg AND "Message sendt" ends up in that file.
What i want is to have
"Message sendt" in logfile.log
msg in extra.log
Is this possible somehow?
Why do you want two logs on the same class? If you have a subcomponent on your class which you would like to log separatelly, then it's allright, and all you need is a proper config file... but, normally, this would be a code smell.
You could do all you want with a single config:
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="C:\Logs\logfile.log"
internalLogLevel="Info" >
<targets>
<target xsi:type="File" name="logfile" fileName="C:\Logs\logfile.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<target xsi:type="File" name="extra" fileName="C:\Logs\extra.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="extra" minlevel="Trace" writeTo="extra" final="true" />
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
Pay attention to the "final" attribute in the "extra" log rule. It'll prevent the extra logs to be written to the other logs.
There's no need to create this logger with a different config. Just do as bellow:
public class MyClass
{
private readonly ILogger<MyClass> _logger;
private readonly Logger _extraLogger = LogManager.GetLogger("extra");
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
}
public void doBothTypesOfLogging()
{
var msg = "Hello";
_extraLogger.Info(msg);
_logger.LogInformation("Message sendt");
}
}
But I think you should reconsider if you really need two loggers for the same class.
UPDATE
According to your needs you can simplify your code a lot by just using out of the box NLog features and following a more nlogish pattern. The config file is almost the same, just changed the rule for the "extra" logger to "syslog" since we'll be changing it's name in the code:
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="C:\Logs\logfile.log"
internalLogLevel="Info" >
<targets>
<target xsi:type="File" name="logfile" fileName="C:\Logs\logfile.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<!-- This will probably be a network target -->
<target xsi:type="File" name="extra" fileName="C:\Logs\extra.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="syslog" minlevel="Trace" writeTo="extra" final="true" />
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
We'll be putting the "syslog" logger in a static variable inside a helper static class:
public static class CommonLoggers
{
public static Logger SysLogger { get; private set; } = LogManager.GetLogger("syslog");
// you can put other centralized loggers here if you want
}
Then you should change your class code to simply:
public class MyClass
{
// the "correct" pattern is to use a logger that follows the class name
// put this in each class you create
private static Logger logger = LogManager.GetCurrentClassLogger();
// nothing to do on the constructor (can be removed as it's redundant)
public MyClass() { }
public void doBothTypesOfLogging() {
var msg = "Hello";
CommonLoggers.SysLogger.Info(msg);
logger.Info("Message sent");
}
}
The important part is to understand that NLog rules are used to filter output given the logger names, and you can do something like this for example:
<rules>
<logger name="SomeNamespace.Somewhere.*" minlevel="Trace" writeTo="extra" final="true"/>
<logger name="SomeNamespace.SomewhereElse.MyClass" minlevel="Info" writeTo="some_other_target" />
<logger name="*" minlevel="error" writeTo="logfile" />
<logger name="*" minlevel="info" writeTo="logconsole" />
</rules>
This way you could TRACE all logs from all classes inside "SomeNamespace.Somewhere.*" to the "extra" target. Write all INFO logs of "MyClass" to "some_other_target". Send ERROR messages to "logfile" and INFO messages to "logconsole".
Reading the docs on rules, targets and filters will help you a lot!
This is a good article: https://www.codeproject.com/Articles/4051307/NLog-Rules-and-filters
The official docs: https://github.com/nlog/nlog/wiki/Configuration-file#rules
And this wonderful SO question: Most useful NLog configurations
Happy reading!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With