NLog 2 files logging will log same info in both files - c#

So I have a rather simple problem, but seems like I am missing something. I have a WPF application ( .NET Core 5.0 ).
I am trying to use 2 files for logging, but whenever I retrieve one logger and write a message, I get the same log in both files.
Here is my code.
private void SetupLogger()
{
// Step 1. Create configuration object
var config = new LoggingConfiguration();
// Step 2. Create targets
var firstTarget = new FileTarget()
{
Name = "FirstLogger",
FileName = Path.Combine(basePath, "log1.txt"),
Layout = "${longdate} ${level} ${message} ${exception}"
};
var secondTarget = new FileTarget()
{
Name = "SecondLogger",
FileName = Path.Combine(basePath, "log2.txt"),
Layout = "${longdate} ${level} ${message}"
};
config.AddTarget("FirstLogger", firstTarget );
config.AddTarget("SecondLogger", secondTarget );
var ruleInfo1 = new LoggingRule("*", NLog.LogLevel.Trace, firstTarget );
var ruleInfo2= new LoggingRule("*", NLog.LogLevel.Trace, secondTarget );
// Step 3. Define rules
config.LoggingRules.Add(ruleInfo1);
config.LoggingRules.Add(ruleInfo2);
// Step 4. Activate the configuration
LogManager.Configuration = config;
// Example usage
var logger = LogManager.GetLogger("FirstLogger");
logger.Info("App starting...");
}
In an other file I have a second log
public void Start()
{
//initialize app
var logger = LogManager.GetLogger("FirstLogger");
logger.Info("App finished initializing...");
}
Now what I see both logs in both files.
LE: I've tried this and it still logs to both files.
var ruleInfo1 = new LoggingRule("FirstLogger", LogLevel.Trace, firstTarget )
{
Final = true
};
var ruleInfo2 = new LoggingRule("*", LogLevel.Trace, secondTarget );
config.LoggingRules.Add(ruleInfo1);
config.LoggingRules.Add(ruleInfo2);

Logging-rules with wildcard * for the Logger-name will match all loggers:
// Step 3. Define rules
var ruleInfo1 = new LoggingRule("*", NLog.LogLevel.Trace, firstTarget );
config.LoggingRules.Add(ruleInfo1);
var ruleInfo2 = new LoggingRule("*", NLog.LogLevel.Trace, secondTarget );
config.LoggingRules.Add(ruleInfo2);
Maybe change into this:
// Step 3. Define rules
var ruleErrors = new LoggingRule("*", NLog.LogLevel.Warn, firstTarget );
config.LoggingRules.Add(ruleErrors);
var ruleUsers = new LoggingRule("UserLog", NLog.LogLevel.Trace, firstTarget );
config.LoggingRules.Add(ruleUsers);
var ruleAll = new LoggingRule("*", NLog.LogLevel.Trace, secondTarget );
config.LoggingRules.Add(ruleAll);
Now the 3 rules will ensure:
ruleErrors - Will redirect warnings/errors into firstTarget (Filter on LogLevel)
ruleUsers - Will redirect events written to LogManager.GetLogger("UserLog") into firstTarget (Filter on Logger-name)
ruleAll - Will redirect all events to secondTarget (Without any filter)
See also: https://github.com/NLog/NLog/wiki/Configuration-file#rules

That's correct.
The only difference between the two loggers is an exception.
You're not throwing any exceptions, and thus none will be logged.

Related

Two processors can't log the information at the same time by using NLog

I have two projects A and B, both of them use a NLog libiary. Now I have an issue:
if A writes loginfo into the log file first, then the B never logs. And if B writes loginfo into the log file first, then A never logs.
As A and B use a same NLog libiary, so they use the same Nlog Config, but they will be built in two processors, here is the config info.
Does somebody have any good idea on this issue?
//Set NLog Config by:
//https://github.com/nlog/NLog/wiki/Configuration-API
private static Logger GenerateLogInstance()
{
// Step 1. Create configuration object
var config = new LoggingConfiguration();
// Step 2. Create targets
var fileTarget = new FileTarget()
{
FileName = #"C:\Logs\${shortdate}.log",
Layout = #"${longdate} ${uppercase:${level}} ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
};
//var wrapper = new AsyncTargetWrapper(fileTarget, 5000, AsyncTargetWrapperOverflowAction.Discard);
// Step 3. Define rules
config.AddTarget("myprojectLog", fileTarget);
config.LoggingRules.Add(new NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, fileTarget));
// Step 4. Activate the configuration
var factory = new LogFactory(config);
return factory.GetLogger("myprojectLog");
}
I don't use nlog but take a look at the following. You may need to set concurrentWrites="true"
File target
concurrentWrites - Enables support for optimized concurrent writes to
same log file from multiple processes on the same machine-host, when
using keepFileOpen = true. By using a special technique that lets it
keep the files open from multiple processes. If only single process
(and single AppDomain) application is logging, then it is faster to
set to concurrentWrites = False. Boolean Default: True. Note: in UWP
this setting should be false
Could you try this instead:
private static LogFactory GenerateLogFactory()
{
// Step 0. Create isolated LogFactory
var logFactory = new LogFactory();
// Step 1. Create configuration object
var config = new LoggingConfiguration(logFactory);
// Step 2. Create targets
var fileTarget = new FileTarget()
{
FileName = #"C:\Logs\${shortdate}.log",
Layout = #"${longdate} ${uppercase:${level}} ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
};
// Step 3. Define rules
config.AddTarget("myprojectLog", fileTarget);
config.LoggingRules.Add(new NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, fileTarget));
// Step 4. Activate the configuration
logFactory.Configuration = config;
return logFactory;
}
private static Logger GenerateLogInstance()
{
return GenerateLogFactory().GetLogger("myprojectLog");
}
Btw. if two projects in the same solution is using this same method, then you can consider doing this:
Lazy<LogFactory> LazyLogFactory = new Lazy<LogFactory>(() => GenerateLogFactory());
private static Logger GenerateLogInstance(string loggerName = "myprojectLog")
{
return LazyLogFactory.Value.GetLogger(loggerName);
}

Bind ConsoleLogger messages to TextBox

I am trying to write a simple IDE for C#. I have implemented Building projects like this:
var path = ProjectPath;
var props = new Dictionary<string, string>
{
{"Configuration", "Debug"},
{"Platform", "AnyCPU"},
{"OutputPath", ProjectPath}
};
var pc = new ProjectInstance(path, props, "14.0");
var logger = new ConsoleLogger();
logger.Verbosity = LoggerVerbosity.Diagnostic;
var buildParams = new BuildParameters()
{
DetailedSummary = true,
Loggers = new List<ILogger> { new ConsoleLogger() },
DefaultToolsVersion = "14.0"
};
var targets = new List<string> { "PrepareForBuild", "Build" };
var reqData = new BuildRequestData(pc, targets.ToArray());
BuildManager.DefaultBuildManager.BeginBuild(buildParams);
var buildResult = BuildManager.DefaultBuildManager.BuildRequest(reqData);
I have a TextBox in my XAML file that I would like to bind with ConsoleLogger messages. However, the ConsoleLogger object works on a very different basis than Control objects in WPF. My initial intuitive idea was to simply write an Event Handler to handle errors and messages, but ConsoleLogger doesn't allow me to do that. How would I got about solving this problem?
Note: there are many ConsoleLogger classes around .NET, I am talking specifically about: Microsoft.Build.BuildEngine.ConsoleLogger

Duplicating logs from multiple targets into single one

I have multiple file targets:
var config = new LoggingConfiguration();
for (int i = 1; i <= 5; i++)
{
var fileTarget = new FileTarget();
fileTarget.Layout = #"${date:format=dd-MM HH\:mm\:ss}[${var:passNumber" + i.ToString() + "}][${var:TestName" + i.ToString() + "}] - ${message}";
fileTarget.FileName = string.Format(#"log-cell{0}.txt", i);
fileTarget.KeepFileOpen = false;
config.AddTarget(string.Format("file{0}", i), fileTarget);
var ruleFile = new LoggingRule(string.Format("{0}", i), LogLevel.Debug, fileTarget);
config.LoggingRules.Add(ruleFile);
}
LogManager.Configuration = config;
Then I can access individual rule and write to corresponding target:
LogManager.Configuration.Variables["passNumber1"] = 33.ToString();
LogManager.Configuration.Variables["TestName1"] = "Test1";
LogManager.GetLogger("1").Debug("Test1");
LogManager.Configuration.Variables["passNumber2"] = 44.ToString();
LogManager.Configuration.Variables["TestName2"] = "Another Test";
LogManager.GetLogger("2").Debug("Test2");
This works fine and creating different file for each rule. Now what I want additionally is to have single RichTextBoxTarget or in general let's say special target:
Where all possible logs will be duplicated or only up to selective level
Layout will include same formatting/variables like in file target
I am not sure how to have single target with multiple layout. My attempt was to add the following into the loop
var ruleWnd = new LoggingRule(string.Format("{0}", i), LogLevel.Debug, rtTarget);
config.LoggingRules.Add(ruleWnd);
Which of course loggine the message, but I am losing the variables.
So the question is how to add single target, which will capture all logs(or level controller), including defined variables.
So the question is how to add single target, which will capture all logs(or level controller), including defined variables.
The variables are on global level, so you could use only one.
In this case "event properties" is where you're looking for. Usage:
Logger logger = LogManager.GetCurrentClassLogger();
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Test1");
theEvent.Properties["passNumber"] = 33;
logger.Debug(theEvent);
use in your config:
${event-properties:item=passNumber}
See ${event-properties} docs

Get the file name of the current log file Enterprise Library 5.0

In MS Enterprise Library 5.0, Logging application block, at runtime, Can I get the name of the log file (Flat File Listener) to which the log is going to?
You can get that information by using the configuration objects:
IConfigurationSource configSource = ConfigurationSourceFactory.Create();
var logSettings = configSource.GetSection(LoggingSettings.SectionName) as LoggingSettings;
var flatFileTraceListener = logSettings.TraceListeners
.First(t => t is FlatFileTraceListenerData) as FlatFileTraceListenerData;
string fileName = flatFileTraceListener.FileName;
This assumes that you are interested in the first trace listener that is a FlatFileTraceListener. If you wanted to get the trace listener by type and name then you could do that too:
IConfigurationSource configSource = ConfigurationSourceFactory.Create();
var logSettings = configSource.GetSection(LoggingSettings.SectionName) as LoggingSettings;
var flatFileTraceListener = logSettings.TraceListeners
.FirstOrDefault(t => t is FlatFileTraceListenerData && t.Name == "Flat File Trace Listener")
as FlatFileTraceListenerData;
string fileName = flatFileTraceListener.FileName;
Change the Config is no problem.
After Change the filename property aaa.log -> bbb.log the Logger not Write in the new Filename.
The changed config must saved/activated (?) or the Logger must new Initialized..!??
IConfigurationSource configSource = ConfigurationSourceFactory.Create();
var logSettings = configSource.GetSection(LoggingSettings.SectionName) as LoggingSettings;
var rollFileTraceListener = logSettings.TraceListeners
.FirstOrDefault(t => t is RollingFlatFileTraceListenerData && t.Name == "RollingFlatFileTraceListener")
as RollingFlatFileTraceListenerData;
string fileName = rollFileTraceListener.FileName;
rollFileTraceListener.FileName = fileName.Replace("aaa", "bbb");
LogWriterFactory f = new LogWriterFactory(configSource);
f.Create();
Logger.Reset();
LogEntry logEntry = new LogEntry();
logEntry.Message = $"{DateTime.Now} Count:{333}";
logEntry.Categories.Clear();
logEntry.Categories.Add("General");
Logger.Write(logEntry);

I need to change the layout for different uses

I need to change the layout of my log file when writing log entries from an aspect. I will likely need to change other settings as well in time.
I have created two FileTargets that create the layout as I require.
LoggingConfiguration config = new LoggingConfiguration();
LoggingConfiguration aspectConfig = new LoggingConfiguration();
FileTarget fileTarget = new FileTarget();
fileTarget.Name = "fileTarget";
fileTarget.Layout = "${longdate} ${machineName} ${callsite} ${message} ${exception:format=tostring}";
fileTarget.FileName = String.Format("{0}Admin.log", Config.DatabasePath);
fileTarget.KeepFileOpen = false;
FileTarget aspectTarget = new FileTarget();
aspectTarget.Name = "aspectTarget";
aspectTarget.Layout = "${longdate} ${machineName} ${message} ${exception:format=tostring}";
aspectTarget.FileName = String.Format("{0}Admin.log", Config.DatabasePath);
aspectTarget.KeepFileOpen = false;
LoggingRule rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget);
config.LoggingRules.Add(rule2);
LoggingRule aspectRule = new LoggingRule("aspects", LogLevel.Trace, aspectTarget);
aspectConfig.LoggingRules.Add(aspectRule);
LogManager.Configuration = config;
There may be other changes/differences to come but that is besides the point at this stage.
Using the following works fine for the 'default or '*' config ie by using :
var log = LogManager.GetCurrentClassLogger();
log.Fatal("##########################################");
What I want to do is be able to use the 'aspectRule' rather than the default '*' rule by calling logger and specifying the 'aspectRule'.
I had thought this would work:
var log = LogManager.GetLogger("aspects");
log.Fatal("########################################");
which is all well and good but I can't see how I define a new Logger called 'aspects' particularly as in code I don't even create a Logger called '*' :-)
Note that I am not using an XML config file and need a solution to use programatically.
Any ideas?
EDIT:
I need to write 1 log entry as in this example:
if (someCondition)
logger.Fatal("#############"); // use layout in fileTarget
else
logger.Fatal("##############"); // use layout in aspectTarget
EDIT: To answer your edited question, LoggingRule rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget) will log any output to the log file.
If you want a specific output then you'll have to be specific with NLog.
For example, log anything ranging from Trace up to Info with a fileTarget ("*"), and have two other specific targets aspects and rule2 with log levels from Error to Fatal.
Then you could log like this:
LogManager.GetCurrentClassLogger().Debug( "Non-specific message" );
LogManager.GetLogger( "rule2" ).Fatal( "From rule2" );
LogManager.GetLogger( "aspects" ).Fatal( "From Aspects" );
--
You're creating two configurations config and aspectConfig, setting up both and eventually setting LogManager.Configuration = config.
Eventually aspectConfig is not being used. To resolve, remove all aspectConfig related code and fix the acpects line to this:
config.LoggingRules.Add(aspectRule);

Categories

Resources