2

This is probably not possible, but I'm wondering if there's a way to make it work.

I have a large ASP.NET MVC43 application that is already instrumented with logging statements.

Now we need to include a "Company Name" value from the Session object in each log entry. Is it possible to configure log4net to read Session data and include it in the log entry? Or some way to force it to...?

Thanks for any ideas.

[Edit] This question helped a lot: How can I include SessionID in log files using log4net in ASP.NET?

I ended up with this as my solution:

In Global.asax.cs:

    // After the session is acquired, push the organization code into log4net's thread context, in case it has to log anything.
    protected void Application_PostAcquireRequestState(object sender, EventArgs e)
    {
        if (Context.Handler is IRequiresSessionState && Session != null && Session[Constants.EMPLOYEE_DETAILS] != null)
                log4net.ThreadContext.Properties["Company"] = ((EmployeeDetails)Session[Constants.EMPLOYEE_DETAILS]).Company;
    }

In log4net configuration:

  <parameter>
    <parameterName value="@Company"/>
    <dbType value="String"/>
    <size value="10"/>
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%property{Company}" />
    </layout>
  </parameter>

Works great - I get a company name in my log output now. Thanks for the help, everyone.

Community
  • 1
  • 1
TimH
  • 1,012
  • 1
  • 14
  • 24
  • It is quite easy to implement AppenderSkeleton (see http://logging.apache.org/log4net/release/sdk/log4net.Appender.AppenderSkeleton.html) That implementation could reference a static Session instance and add the information. – Mads Ravn Dec 03 '12 at 20:40

3 Answers3

2

Use custom layout converter

public class CompanyLayoutConverter : PatternLayoutConverter
{
    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
    {
         var httpContext = HttpContext.Current;
         if (httpContext == null)
             return;

         writer.Write(httpContext.Session["Company Name"]);
    }
}

Add this converter to appender layout, and company name will appear in output (simply use %company at conversionPattern value):

<appender name="RollingLogFileAppender" 
          type="log4net.Appender.RollingFileAppender">
  <threshold value="All" />
  <file value="C:\LogFile" />
  <appendToFile value="true" />
  <rollingStyle value="Date" />
  <datePattern value="'.'yyyyMMdd'.log.txt'" />
  <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  <layout type="log4net.Layout.PatternLayout">
    <converter>
      <name value="company"/>
      <type value="Foo.Bar.CompanyLayoutConverter"/>
    </converter>
    <conversionPattern value="%date %-5level %logger - %company %message%newline" />
  </layout>
</appender>
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    This was a close second to the above answers. I was getting it to work, but couldn't sort out the values of the conversionPattern properly. Thanks, though. – TimH Dec 04 '12 at 16:52
1

See this answer for some ideas on how you can implement this.

Capture username with log4net

You should be able to do it without implementing your own Appender.

Here is an example of a custom PatternLayoutConverter that pulls a parameterized value from HttpContext.Current.Session. (I don't remember if tested it when I posted as part of the linked answer or not, but it should be close):

namespace Log4NetTest
{
  class HttpContextSessionPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Use the value in Option as a key into HttpContext.Current.Session
      string setting = "";

      HttpContext context = HttpContext.Current;
      if (context != null)
      {
        object sessionItem;
        sessionItem = context.Session[Option];
        if (sessionItem != null)
        {
          setting = sessionItem.ToString();
        }
        writer.Write(setting);
      }
    }
  }
}

Another simpler idea would be to do something like this... Create an object that will retrieve the Session parameter you want and put that object in the MDC, then reference the MDC in your layout. When log4net accesses the object from the MDC, it will call its ToString method to get the value to be written to the log.

public class HttpContextSessionParametreProvider
{
  private string _name;
  private string _notSet;

  public HttpContextSessionParameterProvider(string name)
  {
    _name = name;
    _notSet = string.Format("{0} not set", _name);
  }

  public override string ToString()
  {
    HttpContext context = HttpContext.Current;  
    if (context != null && context.Session != null)
    {
      object item = context.Session[_name];
      if (item != null)
      {
        return item.ToString();
      }
    }
    return _notSet;
  }
}

Use it like this somewhere near the entry point of your program:

MDC.Set("CompanyName", new HttpContextSessionParametreProvider("Company Name"));

(I don't have an example handy for how to configure log4net to pull a parameter from the MDC, but it should not be hard to find one).

EDIT:

You could configure your PatternLayout something like this:

  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="[%thread]|[%property{CompanyName}]|%message%newline"/>
  </layout>

Now, assuming that HttpContext.Session["Company Name"] is set to something, whenever you log a message, the value will be written to the log.

Community
  • 1
  • 1
wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • Thanks. Apparently MDC is deprecated, but the link you provided had some information that is proving helpful. Now if I could only figure out why HttpContext.Current is always null. Could be because I'm using spring.net to inject the log4net instance into a LogManager class. Hmmm. – TimH Dec 04 '12 at 16:51
  • Glad to help. I don't do any ASP.NET development, so I'm afraid I can't help with the null HttpContext.Current. I did find this (old) external post that describes some cases where HttpContext.Current can be null. http://www.pcreview.co.uk/forums/can-httpcontext-current-null-t3561352.html Maybe it will help. – wageoghe Dec 04 '12 at 17:09
0

I think you can do this using contexts and the pattern layout:

http://logging.apache.org/log4net/release/manual/contexts.html

http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html

The documentation doesn't help much though.

Philip Daniels
  • 994
  • 1
  • 7
  • 25