How to initialize log4net before the log file is created? - c#

I would like to set the full path and the file name for the log file (I use log4net) and I would like to use the c:\ProgramData\Logs folder. I get the path of the ProgramData folder using the environment variable #PROGRAMDATA#.
I would like to set the path for the log file in the next way: I use a property in the App.config, and I set the value for this property in the class where I do the loggings.
My App.config file:
<configuration>
...
<log4net>
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<!-- Pattern to output the caller's file name and line number -->
<conversionPattern value="%5level [%thread] (%file:%line) - %message%newline" />
</layout>
</appender>
<appender name="AppRollingFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="%property{ProgramDataPath}\Logs\Application.log" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<maximumFileSize value="1000KB" />
<maxSizeRollBackups value="5" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%-5level][%d{yy-MM-dd HH:mm:ss,fff}] %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="Console" />
<appender-ref ref="AppRollingFile" />
</root>
</log4net>
</configuration>
And the code where I set the property:
static void Main(string[] args)
{
log4net.GlobalContext.Properties["ProgramDataPath"] = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
log4net.Config.XmlConfigurator.Configure();
My problem is that an empty log file is created before the value of the property is set (then the property it is null). The initialization of the log4net it is done before entering in the main function of the class where I set the property, so it will always be created an empty file in the application's folder: bin\Debug(null)\Logs\Application.log. After I set the property, everything works fine, because another Apllication.log file will be created in c:\ProgramData\Logs folder.
My question is that how can I set the value of the property before the empty log file is created/before entering in the main function of the class where I set the property, or what other solution is there?
Using the environment variable in the App.config as it is shown below did not work.
<file type="log4net.Util.PatternString" value="${PROGRAMDATA}\Logs\Application.log"/>

Try
<file type="log4net.Util.PatternString" value="${ProgramData}\Logs\Application.log"/>
The environment variables are case sensitive, which is why ${PROGRAMDATA} does not work.

Removing the assembly attribute should fix the problem:
[assembly: log4net.Config.XmlConfigurator(Watch=true)]
If you want log4net to watch app.config for configuration changes, use this code (I prefer to put log4net configuration in a separate log4net.config file for easier reuse):
log4net.GlobalContext.Properties["ProgramDataPath"] = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
FileInfo configFile = new FileInfo(Process.GetCurrentProcess().MainModule.FileName + ".config");
log4net.Config.XmlConfigurator.ConfigureAndWatch(configFile);

Related

Can log4net create log files in a yyyy\\MM\\ddMMyyyy_file.log structure?

I am trying to make log4net create it's log files in a folder structure, but so far I'm not successful.
What I'd like to accomplish is that log4net creates log files like this:
..\log\2017\10\30-10-2017_eva360Recorder.log
..\log\2017\10\31-10-2017_eva360Recorder.log
..\log\2017\11\01-11-2017_eva360Recorder.log
..\log\2017\11\02-11-2017_eva360Recorder.log
In my app.config I now have the following:
<!-- Log4net Logging Setup -->
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
</root>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender, log4net">
<file type="log4net.Util.PatternString" value="log\\%date{yyyy}\\%date{MM}\\%date{dd-MM-yyyy}_eva360Recorder.log" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<maxDateRollBackups value="60" />
<datePattern value="dd-MM-yyyy" />
<preserveLogFileNameExtension value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger %M - %message%newline" />
</layout>
</appender>
</log4net>
This, however, results in the following:
d:\Eva360\Recorder\log\2017\10\31-10-2017_eva360Recorder.log
d:\Eva360\Recorder\log\2017\10\31-10-2017_eva360Recorder31-10-2017.log
As you can see, this is not what I wanted. It did not create a month folder (..\2017\11..) and also did not change the name of the log file, but added a date within the file name.
Is what I want even possible?
If so, how should I do that?
And also importantly, where is this to be found in the documentation of log4net?
I realise that this conjecture, rather than a definitive answer, but this could probably be achieved through the use of a custom log4net appender. See IAppender in the log4net SDK.
Google is chock full of articles about creating custom log4net appenders, so no need to repeat anything here.

Different appenders based on build configuration in log4net

My main application either starts in a console environment or as a Windows Service, based on certain indicators (launch flags, #if directive,...). As long as the application runs in 'production mode', e.g. without a user context as a startup service, I want to log both in a file (info, warnings) as well as into the Windows event log. However when I'm debugging, I want log messages only been carried out to the console window.
Is there any way to achieve this using log4net that doesn't include touching the application configuration during runtime?
Frankly the code approach doesn't sound like a very good idea to me, since it hides some behavior to the person that will manage the system in the end. Since you probably have two build configurations (release and debug?) why not use them instead to change the configuration file depending on the build you're doing.
If you really don't want to do this, then here is how to do it: you can set a custom property in the global context of your log4net library
// declare this code as soon as you've determined what context you're in
log4net.GlobalContext.Properties["ApplicationMode"] = "Service";
// or 'Console", with an #IF directive
Then in your log4net configuration, you can filter out the messages that will be caught by your appenders by using the PropertyFilter of log4net
The console appender for your dev:
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender,log4net">
<filter type="log4net.Filter.PropertyFilter">
<key value="ApplicationMode" />
<stringToMatch value="Console" />
<acceptOnMatch value="true" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %-25.25logger{1} - %message%newline" />
</layout>
</appender>
and the file appender for your production
<appender name="FileAppender" type="log4net.Appender.FileAppender,log4net">
<filter type="log4net.Filter.PropertyFilter">
<key value="ApplicationMode" />
<stringToMatch value="Service" />
<acceptOnMatch value="true" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<file value="mylogfile.txt" />
<appendToFile value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %-25.25logger{1} - %message%newline" />
</layout>
</appender>
Then declare all appenders for your logs:
<root>
<level value="DEBUG"/>
<appender-ref ref="FileAppender"/>
<appender-ref ref="ConsoleAppender"/>
</root>
This will dispatch your message to the correct appender depending on the value that is in the global property. The DenyAllFilter is important; log4net works by letting all messages go through by default, and you only want the messages matching your PropertyFilter
All the appenders can be created, modified, or removed programatically. Why not just remove the appenders you want to avoid? This allows you to use the same code for detecting you are debugging and/or running in console.
To programatically disable the appender:
log4net.Appender.IAppender[] appenders = log4net.LogManager.GetRepository().GetAppenders();
for (int i = 0; i < appenders.Length; ++i)
{
Log4net.Appender.FileAppender appender = appenders[i] as log4net.Appender.FileAppender;
if (appender != null && appender.Name == "RollingFile")
appender.Threshold = log4net.Core.Level.Off;
}

Not able to get log4net working to write to file

I am using sharpdevelop to create a console application in C#. I have added in the reference for log4net and I added my logging statements while I was writing the code but I never looked at the log file. Now I am done with the code I need to get the log file working. My program runs fine, even the logging statements, but I can't find the log file.
I have tried to cobble together a couple of examples on getting log4net working. I have the lines to read from the configuration and then to instantiate the object, these are the first lines in my program to run:
log4net.Config.XmlConfigurator.Configure();
ILog datalogger = LogManager.GetLogger("myLog"); //initiate the data logger
Then in various places throughout the code I have this:
datalogger.Info(DateTime.Now.ToString() + ": using file: " + ProDirectory.ToString() + #"\" + myProFile.ToString());
I have also put the following in my app.config file:
<appender name="myLogAppender" type="log4net.Appender.RollingFileAppender" >
<file value="myLog.log" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level - %message%n" />
</layout>
</appender>
<logger name="myLog">
<level value="All"></level>
<appender-ref ref="myLogAppender" />
</logger>
No matter what I do, I can't see the log file being produced. I have changed the directory and even paused the program to see if I could find a handle open to the log file. Each time I come up empty. Not sure what I could be doing wrong.
not sure if this will help. I also had hard times making log4net work in different scenarios.
I use log4net root xml node to specify the appender. hope it will help.
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="250KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingFileAppender" />
</root>
UPDATE
You might also want to check Visual Studio output log. First setup the output log to log "ALL" (somewhere in VS settings). Then you should see exact error message of log4net in the output window of Visual Studio.
Whenever I have had issues with log4net creating the log file, it usually wound up being a file / directory permissions issue.
Change your <file value="log.txt" /> to something that you know will be accessible by the current user / process running devhost.
For example: <file value="%USERPROFILE%\Documents\log.txt" /> will create a log file in the user's My Documents folder. This is a folder that the user has permissions to write data to, and shouldn't give you any troubles with.
For more information on special folder values, see this link.
Looks like I just had to clean up my XML a little, after I did the log file is working fine:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections> <!-- Level 1 -->
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,
log4net"/> <!-- Level 2 -->
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
<log4net>
<appender name="myLogAppender" type="log4net.Appender.RollingFileAppender" >
<file value="myLog.log" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level - %message%n" />
</layout>
</appender>
<logger name="myLog">
<level value="All"></level>
<appender-ref ref="myLogAppender" />
</logger>
</log4net>
</configuration>

Log4Net Not Logging When Deployed

Using version 1.2.11. Logging works on my dev machine, but won't create the directory or log when deployed.
I tried:
giving full directory access to IUSR, Everyone, local users.
running the app pool as a local admin account.
to use internal debugging as Phil Haack describes here.
Stopped and started the app pool after each change.
Nothing is produced in the output file.
My log4Net config is below. Any thoughts on what to try next?
<?xml version="1.0"?>
<log4net debug="true">
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="..\Logging\log.txt" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyyMMdd" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1MB" />
<threshold value="DEBUG" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="console" type="log4net.Appender.DebugAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="file" />
<appender-ref ref="console" />
</root>
</log4net>
If the directory and the file is not being created, then most likely, the configuration is not being read (and therefore used) at runtime.
I always forget to add the single line of code for Log4net that hooks up the configuration. This code usually appears in the bootstrap class in the application (e.g. Global.asax for an ASP.NET app).
XmlConfigurator.Configure(new System.IO.FileInfo(configFile)); // configFile being the path to the file.
Instead of the above in-line, you can add this attribute to the AssemblyInfo.cs file:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Either way, this will wire up log4net. More information is found in the Manual Configuration section of the log4net docs.
If you're using IIS, make sure the correct group has modify access to the Logs folder (usually IIS_USERS).
Sounds like a permissions issue to me. I almost always use a directory where I don't have to enable any special permissions for the applications to write the log files to.
Here is what I generally use with log4net:
<file type="log4net.Util.PatternString" value="${ALLUSERSPROFILE}/<Company Name>/Logs/<Program Name>/<Log file name>.txt" />
Of cource you'll need to substitute Company Name, Program Name and Log file name in the above with actual values.
This will write to the ProgramData folder where access is typically not restricted. You can navigate to this folder in File Explorer by typing %ProgramData% or %AllUsersProfile%
Another thing I like about this method is that it works on nearly every microsoft O/S. XP, Vista, 7, 8
You probably do not know where you are logging:
<file value="..\Logging\log.txt" />
Will is derived from your running directory, which is iis its directory. You can better use a full path like:
<file value="c:\Logging\log.txt" />
Then you can give the right access rights to the logging directory. Log4net will not create any directories as far as I know. So you have to create the c:\logging directory first.
Non of the answers worked for me until I put these lines to the web.config app settings:
<add key="log4net.Config" value="Log.config" />
<add key="log4net.Config.Watch" value="True" />

log4net not working in dll

I'm currently having issues with getting log4net to work within a particular dll. I'm currently using log4net in other dlls being called by my test app and logging is working fine within those dlls and also within my test app. It's this one particular dll that I'm having trouble with. Here is snippet of code from the dll I'm having trouble with.
//This is from ABC.dll
public class SessionFactory
{
protected static ISessionFactory sessionFactory;
private static readonly ILog log = LogManager.GetLogger(typeof(SessionFactory));
private static void Init()
{
try
{
//Read the configuration from hibernate.xml.cfg or app.config
Configuration normalConfig = new Configuration().Configure();
ConfigureNhibernateValidator(normalConfig);
log.Debug("Initializing session factory");
sessionFactory = Fluently.Configure(normalConfig)
.Mappings(m =>
m.FluentMappings
.AddFromAssemblyOf<OrderHeaderMap>()
.Conventions.AddFromAssemblyOf<PascalCaseColumnNameConvention>())
.ProxyFactoryFactory("NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu")
.BuildSessionFactory();
log.Debug("Finished initializing the session factory");
}
catch(Exception ex)
{
//Code not shown
}
}
}
In my test app I am calling:
log4net.Config.XmlConfigurator.Configure();
Here is my log4net configuration in my App.config file:
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<!-- ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
<root>
<level value="DEBUG"/>
<appender-ref ref="SpeedTest"/>
</root>
<!-- This is a default logger that nhibernate uses to push out all the SQL statements to-->
<logger name="NHibernate.SQL" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="NHibernateConsoleLog"/>
<appender-ref ref="NHibernateFileLog"/>
</logger>
<!-- This is a default logger that nhibernate uses to push out all the debugging type information to-->
<logger name="NHibernate" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="NHibernateFileLog"/>
</logger>
<appender name="NHibernateConsoleLog" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
</layout>
</appender>
<appender name="NHibernateFileLog" type="log4net.Appender.RollingFileAppender">
<file value="Logs/nhibernate.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="SpeedTest" type="log4net.Appender.RollingFileAppender">
<file value="Logs/SpeedTest.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n" />
</layout>
</appender>
</log4net>
</configuration>
Again logging is working fine within my test application using the SpeedTest appender but the logging within the above snippet of code is not working. I set breakpoints above on when it initializes the logger and it seems to hit it. I can post the log4net debug output if necessary but I didn't really see much. Just let me know if you need it and I will post.
Any suggestions as to why logging is not being recorded in the above snippet of code?
It seems that this issue was stemming from me changing the directory to all my external dependencies (log4net being one of them) awhile back in TFS. All I did was drop all my references in my visual studio project and re-add them from my new dependencies folder and everything worked as expected after this. Thanks for all those that helped here.
My suspicion would be that it isn't reading the configuration from the configuration file when you call configure.
If you add the following lines to your application, what do you see (either on the console, or in IDE output window, or by stepping through in the debugger):
var log4netConfig = ConfigurationManager.GetSection("log4net");
var log4netConfigIsNull = log4netConfig == null;
Console.WriteLine(log4netConfigIsNull);
Does it look like the configuration is actually available from the configuration file?
[Edit: in response to your comment]
If you now add another line of debug code to your app:
Console.WriteLine(log.IsDebugEnabled);
what output do you get? Does the logger think it is configured for debug logging? (I'm particularly interested in the behaviour of this in, or after, SessionFactory.Init).
The immediate thought would be that your logging code isn't getting hit, possibly due to an exception being thrown prior to your first log4net call. Can you put a log entry into a finally block, just to test that the logger works?

Categories

Resources