title: log4net database logging with custom parameters
body: <p>I have database logging in place using the AdoNetAppender. What I'd like to do is log the user identity on each log statement. However, I don't want to use the standard log4net %identity parameter for two reasons:</p>
  
  <ol>
  <li>log4net warn that its extremely slow as it has to look up the context identity.</li>
  <li>In some service components the standard identity is a service account but we have already captured the user identity in a variable and I'd like to use that.</li>
  </ol>
  
  <p>I have seen code where some people use the log4net.ThreadContext to add additional properties but I understand that this is 'unsafe' due to thread interleaving (and it is also a performance drain).</p>
  
  <p>My approach has been to extend the AdoNetAppenderParameter class thus:</p>
  
  <pre><code>public class UserAdoNetAppenderParameter : AdoNetAppenderParameter
  {
  
      public UserAdoNetAppenderParameter()
      {
          DbType = DbType.String;
          PatternLayout layout = new PatternLayout();
          Layout2RawLayoutAdapter converter = new Layout2RawLayoutAdapter(layout);
          Layout = converter;
          ParameterName = "@username";
          Size = 255;
      }
  
  
      public override void Prepare(IDbCommand command)
      {            
          command.Parameters.Add(this);
      }
  
  
      public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
      {            
          string[] data = loggingEvent.RenderedMessage.Split('~');
          string username = data[0];
          command.Parameters["@username"] = username;
      }
  
  }
  </code></pre>
  
  <p>and then programmatically add this to the current appender like so:</p>
  
  <pre><code>ILog myLog = LogManager.GetLogger("ConnectionService");
  IAppender[] appenders = myLog.Logger.Repository.GetAppenders();
  AdoNetAppender appender = (AdoNetAppender)appenders[0];                    
  
  appender.AddParameter(new UserAdoNetAppenderParameter());
  
  myLog.InfoFormat("{0}~{1}~{2}~{3}", userName, "ClassName", "Class Method", "Message");
  </code></pre>
  
  <p>The intention here is to use a standard format for messages and parse the first part of the string which should always be the username. The FormatValue() method of the custom appender parameter should then use only that part of the string so that it can be written to a separate field in the log database.</p>
  
  <p>My problem is that no log statements are written to the database. Oddly, when debugging, a breakpoint in the FormatValue() method is only hit when I stop the service.</p>
  
  <p>I've trawled through loads of stuff relating to this but haven't yet found any answers.
  Has anyone managed to do this, or am I on the wrong trail.
  P.S. I've also tried extending the AdoNetAppender but it doesnt give you access to set the parameter values.</p>