Jay
Jay

Reputation: 33

How to get Log4Net to use app.config

The Story: I have a WinForms application with multiple assemblies - MainApp and Utilities. I use Utilities for my logging via log4net. All is fine with the world if I use a separate config file for log4net (aka log4net.config) - no issues.

However, IT staff finds it challenging to tweak two .config files (MainApp.exe.config and log4net.config). So, I'm trying to add my log4net config settings into my app.config file. Not having any luck.

I've tried several solutions posted in this forum. This one seemed to experience the same error I get: log4net configuration - failed to find section

I've tried putting this line in my Utilities:AssemblyInfo.cs

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "MainApp.exe.config", Watch = false)]

In my Utilities module where log4net is referenced, I have this:

const string TEMP_VARIABLE = "TEMP";
string tempDir = Environment.GetEnvironmentVariable( TEMP_VARIABLE );
StringBuilder userTempDirLogFile = new StringBuilder( tempDir );
userTempDirLogFile.Append( @"\" );
userTempDirLogFile.Append( Environment.MachineName );
userTempDirLogFile.Append( "_MAIN_" );
userTempDirLogFile.Append( DateTime.Now.DayOfWeek );
userTempDirLogFile.Append( ".log" );
log4net.GlobalContext.Properties["MainLogFileName"] = userTempDirLogFile.ToString();
StringBuilder utilityLogFile = ( userTempDirLogFile.Replace( "_MAIN_", "_UTILITIES_" ) );
log4net.GlobalContext.Properties["UtilityLogFileName"] = utilityLogFile.ToString();
log4net.Config.XmlConfigurator.Configure();
_mainLogger = LogManager.GetLogger( "MAIN" );

_mainLogger gets this error message:

log4net:ERROR XmlConfigurator: Failed to find configuration section 'log4net' in the application's .config file. Check your .config file for the <log4net> and <configSections> elements. The configuration section should look like: <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />

I noticed in log4net source code that log4net.Config.XmlConfigurator.Configure() calls Assembly.GetCallingAssembly(). I've verified that GetCallingAssembly() is indeed MainApp. My program directory contains all necessary files.

This is my app.config

<?xml version="1.0"?>
<configuration>
    <!-- configSections MUST be first! -->
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>

    <log4net>
        <appender name="MainLogFile" type="log4net.Appender.RollingFileAppender">
            <file type="log4net.Util.PatternString" value="%property{MainLogFileName}"/>
            <appendToFile value="false" />
            <maximumFileSize value="20MB" />
            <maxSizeRollBackups value="3" />
            <param name="Encoding" value="unicodeFFFE" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date{ISO8601}&#9;%property{messageId}&#9;%-5level&#9;%message%newline" />
            </layout>
            <filter type="log4net.Filter.LevelRangeFilter">
                <param name="LevelMin" value="ALL" />
                <param name="LevelMax" value="OFF" />
            </filter>
        </appender>

        <appender name="UtilityLogFile" type="log4net.Appender.RollingFileAppender">
            <file type="log4net.Util.PatternString" value="%property{UtilityLogFileName}"/>
            <appendToFile value="false" />
            <maximumFileSize value="20MB" />
            <maxSizeRollBackups value="3" />
            <param name="Encoding" value="unicodeFFFE" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date{ISO8601}&#9;%property{messageId}&#9;%-5level&#9;%message%newline" />
            </layout>
            <filter type="log4net.Filter.LevelRangeFilter">
                <param name="LevelMin" value="ALL" />
                <param name="LevelMax" value="OFF" />
            </filter>
        </appender>

        <logger name="MAIN">
            <level value="DEBUG" />
            <appender-ref ref="MainLogFile" />
        </logger>

        <logger name="UTILITY">
            <level value="DEBUG" />
            <appender-ref ref="UtilityLogFile" />
        </logger>
    </log4net>

    <startup>
        <!-- Leave sku out so that both 4.0 and 4.5 are supported -->
        <supportedRuntime version="v4.0" />
    </startup>

    <system.windows.forms jitDebugging="true" />

    <system.diagnostics>
        <trace useGlobalLock="false" />
    </system.diagnostics>

    <appSettings>
        <add key="PRINT_CALL_STACK" value="false"/>
        <add key="TRACK_PERFORMANCE" value="false"/>
        <add key="USING_TEST_MODE" value="false"/>
        <add key="WAIT_FOR_LOGON" value="0"/>
    </appSettings>

</configuration>

I figure I'm missing something, but no clue as to what. Thanks for your time.

Note: using VS2013 and log4net 1.2.13.0. Solution targets .NET 4.0 full and x86.

Upvotes: 3

Views: 20293

Answers (3)

Yola
Yola

Reputation: 19021

If you put log4net configuration setting into the app.config or the web.config file the put into the AssemblyInfo.cs file in the Properties folder of your project the following line:

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

Quote from the manual:

// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(Watch=true)]
// This will cause log4net to look for a configuration file
// called TestApp.exe.config in the application base
// directory (i.e. the directory containing TestApp.exe)
// The config file will be watched for changes.

Upvotes: 3

Phil
Phil

Reputation: 53

If you want to put everything in your app.config, make sure you have this in the AssemblyInfo.cs:

    [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", Watch = true)]

true or false can be changed if you want to monitor dynamically or not the config file for changes.

and in your config file, make sure you have the appropriate sections. For example in mine I have:

<configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>

  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <param name="file" value="logs/App.log"/>
      <param name="appendToFile" value="True"/>
      <param name="encoding" value="utf-8"/>
      <param name="staticLogFileName" value="False"/>
      <param name="RollingStyle" value="Size"/>
      <param name="MaxSizeRollBackups" value="1"/>
      <param name="MaxFileSize" value="10485760"/>
      <param name="threshold" value="Debug"/>
      <layout type="log4net.Layout.PatternLayout">
        <param value="%d [%t] %-5p %c{2} - %m%n" name="conversionPattern"/>
      </layout>
    </appender>

    <root>
      <level value="INFO" />
      <appender-ref ref="FileAppender" />
    </root>
  </log4net>

Upvotes: 3

stuartd
stuartd

Reputation: 73243

The assembly attribute must be in the MainApp project, not in the Utilities project.

As it says in the documentation for assembly attributes

Therefore if you use configuration attributes you must invoke log4net to allow it to read the attributes. A simple call to LogManager.GetLogger will cause the attributes on the calling assembly to be read and processed. Therefore it is imperative to make a logging call as early as possible during the application start-up, and certainly before any external assemblies have been loaded and invoked.

Upvotes: 2

Related Questions