Specflow BeforeTestRun Logging - c#

[BeforeFeature]
public static void BeforeFeature()
{
featureTitle = $"{FeatureContext.Current.FeatureInfo.Title}";
featureRollFileAppender = new RollingFileAppender
{
AppendToFile = true,
StaticLogFileName = true,
Threshold = Level.All,
Name = "FeatureAppender",
File = "test.log",
Layout = new PatternLayout("%date %m%newline%exception"),
};
featureRollFileAppender.ActivateOptions();
log.Info("test");
}
I am attempting to use log4net to output a simple string, however, once the file has been generated, it does not contain any data.
No errors are thrown and the test does complete successfully.

It turns out that the previously selected RollingFileAppender was still open and I needed to select another RollingFileAppender. This is one of the issues when using multiple log files. Once this was resolved, the Info() method would output to my desired log file.
I was able to resolve my issue by adding the following code:
BasicConfigurator.Configure(nameRunRollFileAppender);
log = LogManager.GetLogger(typeof(Tracer));
log.Info("Output some data");

Related

NLog Selectively flush async target wrappers

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)?

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);
}

Why is logging to a log4net.ILog appending to multiple logs?

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);

Modify programatically csproj files with Microsoft.Build.Evaluation (instead of Engine)

I would like to read, modify and write back csproj files.
I've found this code, but unfortunately Engine class is depreciated.
Engine engine = new Engine()
Project project = new Project(engine);
project.Load("myproject.csproj");
project.SetProperty("SignAssembly", "true");
project.Save("myproject.csproj");
So I've continued based on the hint I should use Evaluation.ProjectCollection instead of Engine:
var collection = new ProjectCollection();
collection.DefaultToolsVersion = "4.0";
var project = new Project(collection);
// project.Load("myproject.csproj") There is NO Load method :-(
project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// ... modify the project
project.Save(); // Interestingly there is a Save() method
There is no Load method anymore. I've tried to set the property FullPath, but the project still seems empty. Missed I something?
(Please note I do know that the .csproj file is a standard XML file with XSD schema and I know that we could read/write it by using XDocument or XmlDocument. That's a backup plan. Just seeing the .Save() method on the Project class I think I missed something if I can not load an existing .csproj. thx)
I've actually found the answer, hopefully will help others:
Instead of creating a new Project(...) and trying to .Load(...) it, we should use a factory method of the ProjectCollection class.
// Instead of:
// var project = new Project(collection);
// project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// use this:
var project = collection.LoadProject("myproject.csproj")
Since i can't comment:
This won't work in .net core without first setting the MSBuild.exe path variable. The code to do so can be found here
https://blog.rsuter.com/missing-sdk-when-using-the-microsoft-build-package-in-net-core/
and is written here
private static void SetMsBuildExePath()
{
try
{
var startInfo = new ProcessStartInfo("dotnet", "--list-sdks")
{
RedirectStandardOutput = true
};
var process = Process.Start(startInfo);
process.WaitForExit(1000);
var output = process.StandardOutput.ReadToEnd();
var sdkPaths = Regex.Matches(output, "([0-9]+.[0-9]+.[0-9]+) \\[(.*)\\]")
.OfType<Match>()
.Select(m => System.IO.Path.Combine(m.Groups[2].Value, m.Groups[1].Value, "MSBuild.dll"));
var sdkPath = sdkPaths.Last();
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", sdkPath);
}
catch (Exception exception)
{
Console.Write("Could not set MSBUILD_EXE_PATH: " + exception);
}
}

specifying the log path for IE driver

I am trying to specify the IE Driver log path for this I used the Command-Line.Code as follows
var optionsIe32 = new InternetExplorerOptions
{
IntroduceInstabilityByIgnoringProtectedModeSettings = true,
EnsureCleanSession = true,
BrowserCommandLineArguments = "--log-file=\"C:\\IE.log\"
};
var _webDriver = new InternetExplorerDriver(pathContainingIE32DriverServer, optionsIe32);
But the log file is not getting updated when an error occurs.
What am I doing wrong here? Thanks in advance
You are mostly there. It is specified in the InternetExplorerService class:
var service = InternetExplorerDriverService.CreateDefaultService();
service.LogFile = "C:\IE.log";
service.LoggingLevel = InternetExplorerDriverLogLevel.Trace;
Modify the LoggingLevel to what you actually want, you should see the possibilities are Debug, Error, Warn, Info, Fatal and Trace. I believe Trace level logging is the most comprehensive.
Remove the BrowserCommandLineArguments from the optionsIe32, and then pass the InternetExplorerService into the Driver construction like normal, along with your already made options:
var driver = new InternetExplorerDriver(service, optionsIe32);

Categories

Resources