Sunday, March 9, 2014

Problem with DotNetOpenAuth logging when log4net is located in GAC

DotNetOpenAuth is popular open source library which simplifies implementation of own OAuth server (it also have other functionalities, like OpenID). The first thing which you will need when will use it is logger. DotNetOpenAuth may work with log4net – popular logging library for .Net. If it doesn’t finds log4net assembly it uses simple trace logger which writes records to the trace output (which you can read e.g. with DebugView utility). By default trace logger is switched off. In order to enable it you need to add the following section into your web.config file:

   1: <system.diagnostics>
   2:    <switches>
   3:       <!-- "1" gives error messages, "2" gives errors 
   4:          and warnings, "3" gives more detailed error information,
   5:          and "4" gives verbose trace information -->
   6:       <add name="DotNetOpenAuth.Messaging" value="4" />
   7:    </switches>
   8: </system.diagnostics>

Trace logger may be good for local development environments, but it is not very useful on production where you don’t always monitor logs with external utilities. For production environment log4net is more suitable choice.

The problem however is that when log4net assembly is installed to the GAC DotNetOpenAuth can’t find it and thus uses trace logger (issue was found in all-in-one DotNetOpenAuth assembly of 4.3.0.0 version). First of all in this case we need to specify fully qualified assembly names in web.config’s sections for configuration of log4net:

   1: <configSections>
   2:   <section name="log4net"
   3:         type="log4net.Config.Log4NetConfigurationSectionHandler, log4net,
   4:         Version=1.2.11.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a"
   5:         requirePermission="false" />
   6:  
   7:  
   8: <log4net>
   9:   <appender name="FileAppender" type="log4net.Appender.FileAppender">
  10:     <file value="log-file.txt" />
  11:     <appendToFile value="true" />
  12:     <layout type="log4net.Layout.PatternLayout, log4net,
  13:         Version=1.2.11.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a">
  14:         <conversionPattern value="%d [%t] %-5level %l [%p{NDC}] - %m%n" />
  15:     </layout>
  16:   </appender>
  17:   <root>
  18:     <level value="ALL" />
  19:     <appender-ref ref="FileAppender" />
  20:   </root>
  21:   <logger name="DotNetOpenAuth">
  22:     <level value="ALL" />
  23:   </logger>
  24: </log4net>

But it is not enough. Another problem comes from DotNetOpenAuth.Loggers.Log4NetLogger.IsLog4NetPresent property:

   1: private static bool IsLog4NetPresent
   2: {
   3:     get
   4:     {
   5:         try
   6:         {
   7:             Assembly.Load("log4net");
   8:             return true;
   9:         }
  10:         catch (FileNotFoundException)
  11:         {
  12:             return false;
  13:         }
  14:     }
  15: }

Assembly.Load(“log4net”) call throws FileNotFoundException. In order to fix the problem attach custom handler to AppDomain.AssemblyResolve event (e.g. in Application_Start in Global.asax.cs or in http module. In last case ensure that it is done once) and specify fully qualified assembly name:

   1: private void Application_Start(object sender, EventArgs e)
   2: {
   3:     AppDomain.CurrentDomain.AssemblyResolve +=
   4: CurrentDomain_AssemblyResolve;
   5: }
   6:  
   7: private static Assembly CurrentDomain_AssemblyResolve(object sender,
   8: ResolveEventArgs args)
   9: {
  10:     if (args.Name == "log4net")
  11:     {
  12:         return Assembly.Load("log4net, Version=1.2.11.0, " +
  13: "Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a");
  14:     }
  15:     return null;
  16: }

After that DotNetOpenAuth logging should work.

1 comment:

  1. Seems like someone should make a quick log4net GAC wrapper that can be deployed as a local assembly, and simply forwards the call to the GAC'ed assembly.

    ReplyDelete