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);
Related
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.
I have a large number of async target wrappers created like this:
var fileTarget = new FileTarget("file_"+unique_id)
{
FileName = $"{unique_id}.log"),
Layout = layout_string,
DeleteOldFileOnStartup = false,
KeepFileOpen = true,
AutoFlush = false
};
var asyncFileTarget =
new NLog.Targets.Wrappers.AsyncTargetWrapper(fileTarget, 10000, NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction.Block);
asyncFileTarget.Name = unique_id;
m_job_logger_factory.Configuration.AddTarget(asyncFileTarget);
m_job_logger_factory.Configuration.AddRule(TranslateEnum(logLevel), NLog.LogLevel.Fatal, asyncFileTarget, assignedLogger.Name);
m_job_logger_factory.ReconfigExistingLoggers();
Each target has a separate logger assigned to it.
Then I log a couple of lines to each of the created targets through their assigned loggers.
Nothing is logged to the file yet, as they are async, and the queue is not full.
Now, if I do
Target t = m_job_logger_factory.Configuration.FindTargetByName(unique_id);
t.Flush((e) => { });
I'd expect only the unique_id file to have the new logs, instead I find all the files updated (as if I had called flush on the LogManager/LogFactory, and not this specific target).
Is this configuration incorrect or is this a bug with target.Flush(AsyncContinuation)?
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);
}
I'm developing a plugin for a third-party application, and for each 'run' of this plugin I want an exclusive log file.
I've built the following class.
public class LogFileRepository
{
private readonly Common.Configuration.Settings _configSettings;
private const string InstanceName = "AutomationPlugin.Logging";
private readonly ILoggerRepository _repository;
public LogFileRepository (Common.Configuration.Settings configSettings)
{
_configSettings = configSettings;
var repositoryName = $"{InstanceName}.Repository";
_repository = LoggerManager.CreateRepository(repositoryName);
}
public ILog GetLog(string name)
{
var logger = LogManager.Exists(_repository.Name, name);
if (logger != null)
{
return logger;
}
var filter = new LevelMatchFilter {LevelToMatch = Level.All};
filter.ActivateOptions();
var appender = new RollingFileAppender
{
AppendToFile = false,
DatePattern = "yyyy-MM-dd",
File = String.Format(_configSettings.Paths.LogFileTemplate, name),
ImmediateFlush = true,
Layout = new PatternLayout("%n%date{ABSOLUTE} | %-7p | %m"),
LockingModel = new FileAppender.MinimalLock(),
MaxSizeRollBackups = 1,
Name = $"{InstanceName}.{name}.Appender",
PreserveLogFileNameExtension = false,
RollingStyle = RollingFileAppender.RollingMode.Once
};
appender.AddFilter(filter);
appender.ActivateOptions();
BasicConfigurator.Configure(_repository, appender);
return LogManager.GetLogger(_repository.Name, name);
}
}
What I intended this function to do is for the GetLog method to return a log file (with the specified name) if the LogManager already has one; if there isn't an existing log file then it should instantiate and return it.
This does happen. On the first run of the plugin a log file is created and written to; on a second run of the plugin a new log file is created and written to, but all messages are also written to the first log file. And on a third run all messages are written to the two existing log files as well as the new third log file.
Why? Is there something in the RollingFileAppender that I've seemingly misunderstood/misconfigured? I want an exclusive log file for each name parameter.
Assuming you've created _repository using LogManager.CreateRepository(), this actually creates a Hierarchy, and when you configure this with your new appender via BasicConfigurator.Configure(_repository, appender); this adds the appender to the Hierarchy's Root appender collection.
All loggers then created from the repository are child loggers of the "Root" and are configured to be "additive" in that they append to all appenders defined directly against them, and any of their parent loggers, all the way up to the Root. In your case the loggers themselves have no appenders of their own, so are just picking up appenders from the Root, which in your case contains all the appenders. As a result all messages get logged to every file.
What you want to do is to attach the appender to its specific logger, and disable additivity so that it doesn't then log to appenders higher in the hierarchy. There doesn't appear to be a "nice" way to do this, but the following worked in my testing:
...
appender.AddFilter(filter);
appender.ActivateOptions();
// Add the appender directly to the logger and prevent it picking up parent appenders
if (LoggerManager.GetLogger(_repository.Name, name) is Logger loggerImpl)
{
loggerImpl.Additivity = false;
loggerImpl.AddAppender(appender);
}
BasicConfigurator.Configure(_repository, appender);
return LogManager.GetLogger(_repository.Name, name);
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