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
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);
}
Hi I have created a task using the QMS API and have had success populating all general, reload, and reduce tabs but I am now having trouble when working with the distribute category. I'm trying to "Distribute to QlikView Server " and add a view users to distribute to. Except whenever I include distribute in the DocumentTaskScope (DocumentTaskScope.Distribute), the task seems to lose reference to the SourceDocument and the reduce category gets wiped, along with options from general and reload being lost. This is indicated by this dialog appearing when I click manage users when on the distribute tab.
Also here is what my distribute panel actually looks like, as you can see it is missing the "Distribute to QlikView Server" area that should normally be there:
Here is my code for the distribute section, I am writing this program in c# and the program builds and executes with no errors, it just doesn't produce the intended result.
I was thinking if it had something to do with the server name syntax or if the ID member. The documentation indicated that this is meant to be the "The ID of the QlikView Server. ", so I used the id of the qlikview service but I am not certain this is what it is looking for.
//Distribute static ("Manually" in QV Management Console)
DocumentTask.TaskDistribute.TaskDistributeStatic tds = new DocumentTask.TaskDistribute.TaskDistributeStatic();
TaskDistributionDestination.TaskDistributionDestinationQlikViewServer tddqvs = new TaskDistributionDestination.TaskDistributionDestinationQlikViewServer();
tddqvs.Name = "QVS#qlikviewdev";
tddqvs.Mount = "Root Folder";
tddqvs.ID = qvsGuid;
TaskDistributionEntry newEntry = new TaskDistributionEntry();
newEntry.Destination = new TaskDistributionDestination();
TaskDistributionDestination tdd = new TaskDistributionDestination();
TaskDistributionDestinationType tddt = new TaskDistributionDestinationType();
tddt = TaskDistributionDestinationType.QlikViewServer;
newEntry.Destination.Type = tddt;
newEntry.Destination.QlikViewServer = tddqvs;
TaskDistributionDestination.TaskDistributionDestinationFolder tddf = new TaskDistributionDestination.TaskDistributionDestinationFolder();
tddf.Name = "N/A";
newEntry.Destination.Folder = tddf;
DirectoryServiceObject[] serviceObject = new DirectoryServiceObject[2];
DirectoryServiceObject dso1 = new DirectoryServiceObject();
DirectoryServiceObject dso2 = new DirectoryServiceObject();
dso1.Name = "QlikView_Admin";
dso1.Type = DirectoryServiceObjectType.Named;
dso1.OtherProperty = "N/A";
dso2.Name = "Qlikview_PowerUser";
dso2.Type = DirectoryServiceObjectType.Named;
dso2.OtherProperty = "N/A";
serviceObject[0] = dso1;
serviceObject[1] = dso2;
newEntry.Recipients = serviceObject;
TaskDistributionEntry[] tdeArray = new TaskDistributionEntry[1];
tdeArray[0] = newEntry;
tds.DistributionEntries = tdeArray;
docTask.Distribute = new DocumentTask.TaskDistribute();
docTask.Distribute.Static = tds;
DocumentTask.TaskDistribute.TaskDistributeDynamic taskDistributeDynamic = new DocumentTask.TaskDistribute.TaskDistributeDynamic();
taskDistributeDynamic.Destinations = new List<TaskDistributionDestination>().ToArray();
taskDistributeDynamic.FieldName = string.Empty;
taskDistributeDynamic.IdentityType = UserIdentityValueType.DisplayName;
docTask.Distribute.Dynamic = taskDistributeDynamic;
DocumentTask.TaskDistribute.TaskDistributeNotification tdn = new DocumentTask.TaskDistribute.TaskDistributeNotification();
tdn.SendNotificationEmail = false;
docTask.Distribute.Notification = tdn;
DocumentTask.TaskDistribute.TaskDistributeOutput tdo = new DocumentTask.TaskDistribute.TaskDistributeOutput();
tdo.Type = TaskDistributionOutputType.QlikViewDocument;
docTask.Distribute.Output = tdo;
Turns out I was using the wrong GUID for the TaskDistributionDestinationQlikViewServer Objects' ID field. Once I set it to be the ID of the QVS#(servername) service, it worked.
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);