How to set log4net create filename with logger name and date? - c#

I would like to have files named for example:
loggername_yyyy-MM-dd.log
I try to possible:
<file type="log4net.Util.PatternString" value="Log\%logger_%date{yyyy-MM-dd}.txt" />
But he only take effect %date{yyyy-MM-dd}
How is this possible with log4net?
I want my different job use different logger, like : ILog log = LogManager.GetLogger("LogName");, he should save to 'LogName_2019-03-07.log' file
The %property{LogName} only take effect in layout

Related

NLog: variable with cached layout yields different values if used multiple times

I log to Azure Blob Storage and want to have one three logfiles (containing different things) per start and I use a cached timestamp for this. Ideally the timestamp should be the same in all three files, but they differ if I log to the specific targets at different times.
I set the variable like this
<variable name='blobprefix' value='${cached:cached=true:Inner=${date:format=yyyy-MM-dd\\THH.mm.ss}' />
And this is how I use it for every blob:
<target type="AzureBlobStorage"
name="general-azureblob"
connectionString="${blobConnection}"
blobName="${blobprefix}.log">
</target>
How can I have the variable evaluate exactly once.
You can use the ${processinfo:StartTime} like this:
<variable name='blobprefix' value='${processinfo:StartTime:format=yyyy-MM-dd\\THH.mm.ss:cached=true}' />
If you want to control the StartTime, then you can use NLog GDC and do the following at application startup (Before creating the first NLog Logger-object):
NLog.GlobalDiagnosticsContext.Set("StartupTime", DateTime.UtcNow);
And have the following in NLog.config (whenEmpty is only there as fallback):
<variable name='blobprefix' value='${gdc:StartupTime:format=yyyy-MM-dd\\THH.mm.ss:cached=true:whenEmpty:${date:format=yyyy-MM-dd\\THH.mm.ss:cached=true}}' />

Specflow - Create Pre-defined data to be shared between all scenarios in test execution with parallel execution

I am trying to Re-create my BeforeTestRun step to run my setup only once per whole execution not per thread.
I had a look a Custom Deployment steps I have implemented some already but For my setup i need to bring in some values from the app.config file I am trying something like this
my Default.srprofile file contains:
<DeploymentTransformation>
<GlobalSteps>
<Custom type="Test.CustomDeploymentStep, Test"></Custom>
</GlobalSteps>
</DeploymentTransformation>
and my CustomDeploymentStep.cs:
public class CustomDeploymentStep : IDeploymentTransformationStep
{
public static string baseUrl;
public void Apply(IDeploymentContext deploymentContext)
{
baseUrl = ConfigurationManager.AppSettings["URL"];
}
public void Restore(IDeploymentContext deploymentContext)
{
DoSomething();
}
}
My app config contains the following:
<add key="URL" value="http://google.com" />
But That does not work, The ConfigurationManager.AppSettings only returns one key and one value
"key" : "TestProjectRetargetTo35Allowed" "value":"true"
How can I load my configuration from app.config into the Apply() method in CustomDeploymentStep?
Also If there is a better/more efficient way of generating pre-defined data in specflow with thread safe execution, please do let me know
I ran into the same problem once I needed to use custom deployment steps in more than one project in a large solution. This appears to be a bug within the TechTalk.SpecRun.Framework. The error is likely "Error applying global deployment step. Global steps cannot contain test assembly specific settings." and if you look inside the TestAssembly while debugging you will see the TestAssemblyConfigFilePath is null and/or swallowing another exception.
It doesn't register a project specific configuration file. My workaround was to save the config file into debug and access what I need like so:
string appConfigFilePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\App.config";
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = appConfigFilePath;
var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
var baseUrl = config.AppSettings.Settings["URL"].Value;

How to dynamically set log file using App.config and System.Diagnostics?

I was looking for a solution to provide logging to my latest project when I came across an article ( http://www.daveoncsharp.com/2009/09/create-a-logger-using-the-trace-listener-in-csharp/ ) that talked about using System.Diagnostics and App.config to log via the Trace method. I was able to successfully implement both the class and the App.config but I'd really like to be able to dynamically assign the value/location (inside initializeData) of the log file and I don't have a clue how to do it. If you have any suggestions, please feel free to post!
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="myListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="fileSizeThreshold=512, fileSizeUnit=kilobytes,
fileAgeThreshold=1, fileAgeUnit=months, fileNameTemplate='{0}\MyApp-{1:MMM-yy}.log'"/>
<remove name="Default" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
Logger Class:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace MyCurrentProject
{
class Logger
{
public void Error(string module, string message)
{
WriteEntry(message, "ERROR:", module);
}
public void Error(Exception ex, string module)
{
WriteEntry(ex.Message, "ERROR:", module);
}
public void Warning(string module, string message)
{
WriteEntry(message, "WARNING:", module);
}
public void Info(string module, string message)
{
WriteEntry(message, "INFO:", module);
}
private void WriteEntry(string message, string type, string module)
{
Trace.WriteLine(
string.Format("{0} {1} [{2}] {3}",
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
type,
module,
message));
}
}
}
RE: Sorry for not being clear... just to be clear, I need the file path that the log file output is save to to be dynamically set. I'd like the path to save to a location in %AppData%. The problem I had with the config was that once I set the value for 'initializeData' I couldn't find a way to change or dynamically set/reset that value. Truthfully... at this point I just want a solution that works and allows me to manage the location of the log file.
Here is a worked example using Systems.Diagnostics, which has two advantages over non-base class libraries. It is always allowed (in large organizations), always available, and to the extent that developers are familiar with the base class library, the next batch of maintenance developers will have heard of it.
using System.Diagnostics;
namespace DynamicTraceLog
{
class Program
{
static void Main(string[] args)
{
//Use TraceSource, not Trace, they are easier to turn off
TraceSource trace = new TraceSource("app");
//SourceSwitches allow you to turn the tracing on and off.
SourceSwitch level =new SourceSwitch("app");
//I assume you want to be dynamic, so probalby some user input would be here:
if(args.Length>0 && args[0]=="Off")
level.Level= SourceLevels.Off;
else
level.Level = SourceLevels.Verbose;
trace.Switch = level;
//remove default listner to improve performance
trace.Listeners.Clear();
//Listeners implement IDisposable
using (TextWriterTraceListener file = new TextWriterTraceListener("log.txt"))
using (ConsoleTraceListener console = new ConsoleTraceListener())
{
//The file will likely be in /bin/Debug/log.txt
trace.Listeners.Add(file);
//So you can see the results in screen
trace.Listeners.Add(console);
//Now trace, the console trace appears immediately.
trace.TraceInformation("Hello world");
//File buffers, it flushes on Dispose or when you say so.
file.Flush();
}
System.Console.ReadKey();
}
}
}
Re: how to format the output
Try either of these two for trace listeners that implement templated trace formating/output using Systems.Diagnostics classes:
http://essentialdiagnostics.codeplex.com
or http://ukadcdiagnostics.codeplex.com/
Systems.Diagnostics doesn't provide for any particular formating or outputing of standard tokens.
I know I may get down voted for this, but I'm going to go off the ranch for a minute and I'm going to suggest you use a different tool from the toolbox. Log4Net is a very powerful and succinct logging framework. For example, below is a console application that uses it and it's fully functional as you see it.
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// Set up a simple configuration that logs on the console.
BasicConfigurator.Configure();
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
But let's say we wanted to do that with a configuration file just like you're implying you would like to use. Well, that's pretty straight forward! Below is the configuration we'd place in the App.config to accomplish the same thing:
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="DEBUG" />
<appender-ref ref="A1" />
</root>
</log4net>
Then that configuration can be used like this:
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// BasicConfigurator replaced with XmlConfigurator.
XmlConfigurator.Configure(new System.IO.FileInfo(args[0]));
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
And don't let the pattern stuff catch you off guard, it's just configuring what you're coding above so that you can have some consistency in the messages. It really keeps it easy though because all you ever have to log is the information that needs to be plugged into the pattern and the pattern is then encapsulated away.
This is a crash course in Log4Net, but the reason I'm really recommending it is because in the two examples above you see that they logged to the console, but you have a myriad of possible loggers, just look at this list:
log4net.Appender.AdoNetAppender: Writes logging events to a database using either prepared statements or stored procedures.
log4net.Appender.AnsiColorTerminalAppender: Writes color highlighted logging events to a an ANSI terminal window.
log4net.Appender.AspNetTraceAppender: Writes logging events to the ASP trace context. These can then be rendered at the end of the ASP page or on the ASP trace page.
log4net.Appender.ColoredConsoleAppender: Writes color highlighted logging events to the application's Windows Console.
log4net.Appender.ConsoleAppender: Writes logging events to the application's Console. The events may go to either the standard our stream or the standard error stream.
log4net.Appender.DebugAppender: Writes logging events to the .NET system.
log4net.Appender.EventLogAppender: Writes logging events to the Windows Event Log.
log4net.Appender.FileAppender: Writes logging events to a file in the file system.
log4net.Appender.LocalSyslogAppender: Writes logging events to the local syslog service (UNIX only).
log4net.Appender.MemoryAppender: Stores logging events in an in memory buffer.
log4net.Appender.NetSendAppender: Writes logging events to the Windows Messenger service. These messages are displayed in a dialog on a users terminal.
log4net.Appender.OutputDebugStringAppender: Writes logging events to the debugger. If the application has no debugger, the system debugger displays the string. If the application has no debugger and the system debugger is not active, the message is ignored.
log4net.Appender.RemoteSyslogAppender: Writes logging events to a remote syslog service using UDP networking.
log4net.Appender.RemotingAppender: Writes logging events to a remoting sink using .NET remoting.
log4net.Appender.RollingFileAppender: Writes logging events to a file in the file system. The RollingFileAppender can be configured to log to multiple files based upon date or file size constraints.
log4net.Appender.SmtpAppender: Sends logging events to an email address.
log4net.Appender.SmtpPickupDirAppender: Sends logging events to an email address but writes the emails to a configurable directory rather than sending them directly via SMTP.
log4net.Appender.TelnetAppender: Clients connect via Telnet to receive logging events.
log4net.Appender.TraceAppender: Writes logging events to the .NET trace system.
log4net.Appender.UdpAppender: Sends logging events as connectionless UDP datagrams to a remote host or a multicast group using a UdpClient.
So, as you can see, it's extremely capable OOB. I hope this post has been helpful.

A design for adding resources to a project

I have classes Project,Resource and File.
where
A Project contains LIST of Resources.
Each Resource contains LIST of of Files of particular type.
This is mapped to an XML :
<Project>
<Resource id=1>
<File id="1" path="" type="A" />
<File id="2" path="" type="B" />
<File id="3" path="" type="B" />
<File id="4" path="" type="B" />
</Resource>
<Resource id=2>
<File id="1" path="" type="A" />
<File id="2" path="" type="B" />
<File id="3" path="" type="B" />
<File id="4" path="" type="B" />
</Resource>
</Project>
So basically every resource has to have at-most one file of type "A" and any number of files of type "B" . The file type is selected by user from a dialog where he selects the file and adds to the resource.
The problem is for every file of type "A", i need to create a new Resource and hence and new node in XML.(which my current code isn't able to do)
Initially i came with the following (generalised for brevity)
Project p =new Project("Untitled project"); //Will happen once per project
Resource res = p.CreateProjectResource("resource1");
//various params to create resource
p.AddResource(res);
//now lets add files to a resource
AddFileHelper(res,"C:\myfile1.bin","A",guid.toString());
AddFileHelper(res,"C:\myfile32.bin","B",guid.toString());
AddFileHelper(res,"C:\myfile56.bin","B",guid.toString());
//The next statement should create a new resource and add the file to
//the new created design
AddFileHelper(res,"C:\myfile4.bin","A",guid.toString()); //
//some helper class :
//Adds a file of type "type" to a resource "res" with file ID as "id"
private AddFileHelper(Resource res,string path,FileType type,string id)
{
// path is user defined file path from OpenFile dialog,
//type is selected from a Dropdown (of Enum values "A","B",...)
//id is GUID
res.AddFile(path,type,id);
//************ OR it could be also written as *******
//ResFile file =new ResFile(path,type,id);
//res.AddFile(file);
//Update XML file here..
}
The main problem is the User does not create the resources "explicitly" (except for the first resource) and creation of new Resource depends on the type of the file being added by the user.
Also due this design it is difficult to figure out the Resource given a File id.
Only way to track is using the file collection in each Resource class.
Any help??
Thanks All.
This is in reference to a question I asked before post
The problem as I understand it:
As of now, your AddFileHelper only adds files to your project resource labeled ''resource1'' which is a problem because every time a filetype “A” is passed your AddFileHelper, you to make a new resource for your project (''resource2'') and add it to that.
There is a very simple way to do this. Within AddFileHelper test the FileType of the added file and determine whether or not you need a new resource to be added to your project. If the type isn't “A” you'll call the code that you have now and add the file with:
res.AddFile(path, type, id);
If the type to add is “A” and you need a new resource, just redefine res and increment a counter variable of how many resources you have in your project:
Resource res = p.CreateProjectResource(resourceName);
resourceCounter++;
Where resourceName is the string:
string resourceName = ''resource'' + resourceCounter;
All this should be implemented as your AddFileHelper method.
Regarding the overall structure of your code, the AddFileHelper should be a project class method. Here's why:
The AddFile method, and the AddFileHelper method sound similar but do two very different things. The AddFile method belongs to the resource class because it acts on a well defined resource object. However, the AddFile method is not enough for your purposes because the resource file to append to is not immediately apparent to the client who has a file and wants to add it to your project. Bbefore the AddFile method can be called, the target resource needs to be determined. The job of the AddFileHelper method is to determine which resource will call the AddFile method. Therefore, the AddFileHelper method belongs to the project class and the AddFile method to the resource class.
The logical connection between the AddFileHelper method and the project class might be more apparent if you renamed the method to FileResourceAssignment or something like that.

Best way to dynamically set an appender file path

I am trying to find somebody smarter than me to validate some syntax I wrote up. The idea is to configure the filename of my RollingFileAppender to the name of the assembly in order to make it more re-usable for my projects.
I've seen this previous SO article but it wasn't exactly able to answer my question...
I've had a dickens of a time trying to understand the inner components of Log4net and this is what I came up with (residing in the Global.asax file - Application_Start method):
// Bind to the root hierarchy of log4net
log4net.Repository.Hierarchy.Hierarchy root =
log4net.LogManager.GetRepository()
as log4net.Repository.Hierarchy.Hierarchy;
if (root != null)
{
// Bind to the RollingFileAppender
log4net.Appender.RollingFileAppender rfa =
(log4net.Appender.RollingFileAppender)root.Root.GetAppender("RollingLogFileAppender");
if (rfa != null)
{
// Set the file name based on the assembly name
string filePath =
string.Format("~/App_Data/{0}.log", GetType().Assembly.GetName().Name);
// Assign the value to the appender
rfa.File = Server.MapPath(filePath);
// Apply changes to the appender
rfa.ActivateOptions();
}
}
Can anyone tell me, 'this is hideous', or 'this should work fine'? Also, if I set the file dynamically can I still expect the log4net behavior to rotate the files based on the log4net.config file settings?
Much appreciated!
You are doing this the hard way! Define your log4net config as XML in your application's configuration file and use %property{} to advantage:
<appender name="YourAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="~/App_Data/%property{LogName}" />
....
</appender>
This is dynamic -- you just have to set the log4net property "LogName" before you initialize log4net. Thus, in your code any time before you configure log4net, set the desired value of this property:
string LogName = GetType().Assembly.GetName().Name + ".log";
log4net.GlobalContext.Properties["LogName"] = LogName;
Of course, you may use any property name. I've chosen "LogName" for a simple example, but you can have one per application if you want, as long as your code knows what the correct property name is and what the correct value should be.
Here is way to set or change the logfile of the first appender at runtime:
var appender = (log4net.Appender.FileAppender)LogManager.GetRepository().GetAppenders()[0];
appender.File = "C:\whatever.log";
appender.ActivateOptions();
In 2015, we do it like this:
<file type="log4net.Util.PatternString">
<conversionPattern value="%appdomain.log" />
</file>
No other code required.
App domain is the executing assembly's file name.
it worked for me with the date
<file type="log4net.Util.PatternString" value="./Log/logQueueService%date{yyyy_MM_dd}.log" />

Categories