3

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.

Community
  • 1
  • 1
Jay
  • 33
  • 1
  • 1
  • 3

3 Answers3

3

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>
Phil
  • 53
  • 7
  • I dont understand, I have to have both log4net.Config.XmlConfigurator.Configure(); AND[assembly: XmlConfigurator(ConfigFileExtension = "config", Watch = true)] in order for the config to be picked up, any idea why? I thought I would just need the assembly reference? – Sam Mar 30 '18 at 20:50
  • in dotnet core(3.1) there's no place to specify `assembly: log4net...`, any ideas? – Lei Yang Sep 29 '20 at 07:30
3

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.
Yola
  • 18,496
  • 11
  • 65
  • 106
2

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.

stuartd
  • 70,509
  • 14
  • 132
  • 163
  • Thanks @stuartd. I thought I tried that, maybe not. They got me on something else now. Will try suggestion and get back to you. – Jay Jun 30 '15 at 22:50
  • I tried to add the assembly attribute to the main.app.config. I had to add a reference to log4net to the Main project. However, I still get this error message: {log4net:ERROR XmlConfigurator: Failed to find configuration section 'log4net' in the application's .config file. Check your .config file for the and elements. The configuration section should look like:
    }
    – Jay Jul 01 '15 at 16:04
  • Did you remove it from `Utilities:AssemblyInfo.cs`? (Note that when your configuration is the app.config file, the `ConfigFile` field is not required in the assembly attribute.) – stuartd Jul 01 '15 at 16:07
  • 1
    .. and what does this return: `XmlElement log4NetSection = (XmlElement)ConfigurationManager.GetSection("log4net");` – stuartd Jul 01 '15 at 16:08
  • Thanks again @stuartd. Yes, attribute was removed from `Utilities:AssemblyInfo.cs`. I also removed the `ConfigFile` field in `MainApp:AssemblyInfo.cs`. This line `System.Xml.XmlElement log4NetSection = (System.Xml.XmlElement)System.Configuration.ConfigurationManager.GetSection( "log4net" );` returned a null. I'm guessing that might be the source of the problem, right? – Jay Jul 07 '15 at 15:31
  • @Jay yes, it should return the configuration section, the config file must be being mislaid somehow. – stuartd Jul 07 '15 at 15:36
  • thx @stuartd -getting warmer. I realized that maybe the app.config wasn't being loaded (duh), so I added these lines: 'Configuration configFile = ConfigurationManager.OpenExeConfiguration( "MainApp.exe" ); ConfigurationManager.RefreshSection( "log4net" );` I can enumerate `appSettings` but, when I try `log4net` I get this exception: `An unhandled exception of type 'System.Configuration.ConfigurationErrorsException' An error occurred creating the configuration section handler for log4net: Could not load file or assembly 'log4net' or one of its dependencies.` log4net.dll is in the directory. – Jay Jul 07 '15 at 16:29
  • You've been most helpful, @stuartd. Went back to RTFM. After several go throughs this catches my eye: `ConfigFile = If specified, this is the filename of the configuration file to use with the XmlConfigurator. This file path is relative to the application base directory (AppDomain.CurrentDomain.BaseDirectory).` My app is started from within IE, so that value = `C:\Program Files (x86)\Internet Explorer\ ` Back to `MainApp:app.config` and: `[assembly: log4net.Config.XmlConfigurator( ConfigFile = "../My Company/My App/MainApp.exe.config", Watch = false )]` Still getting `null` for `XmlElement` – Jay Jul 07 '15 at 22:12
  • Well, not completely the way that it seems to work for everyone else. Now that I know I've got `MainApp.exe.config` loaded, this still doesn't work `log4net.Config.XmlConfigurator.Configure(XMLElement);` However, if I set a FileInfo object to `MainApp.exe.config` , this works `log4net.Config.XmlConfigurator.Configure(FileInfo);` The `ConfigurationManager.GetSection()` just cannot find the `` section. Thanks @stuartd for your time. I marked this as answered. It's working using `FileInfo`, just not the preferred way of using `XMLElement`. – Jay Jul 08 '15 at 14:59