25

I'm new to log4net, so hopefully this is a really easy question for someone?!

I've got log4net working with the RollingLogFileAppender for my web application. I'm using logging to try and find where some performance issues are coming from. In order to do this, it'd be useful to include the ASP.NET SessionID in the log output so that I can make sure I'm looking at log entries for a specific user.

Is there any way I can do this through the conversionPattern setting for the appender? Is there a %property{??} setting I can use?

UPDATE: This question still hasn't been answered - does anybody have any ideas?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Chris Roberts
  • 18,622
  • 12
  • 60
  • 67
  • I have got the same problem: http://stackoverflow.com/questions/8985693/how-to-use-aspnet-session-pattern-layout – mynkow May 28 '12 at 11:29
  • I have the answer below. If it does help, please upvote it. People who don't understand the situation have downvoted it. http://stackoverflow.com/a/24048883/3481183 – Believe2014 Jun 05 '14 at 03:51
  • You shouldn't log session IDs as people with access to the logfiles could hijack sessions. At least hash the session id before logging it! – Kutzi Nov 27 '15 at 16:25

5 Answers5

20

Alexander K. is nearly correct. The only problem with it is that the PostAcquireRequestState event also occurs for static requests. A call to Session in this situation will cause a HttpException.

Therefore the correct solution becomes:

protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
    if (Context.Handler is IRequiresSessionState)
    {
        log4net.ThreadContext.Properties["SessionId"] = Session.SessionID;
    }
}
Dan Turner
  • 2,233
  • 18
  • 19
16

UPDATE (2014-06-12): Starting from log4net 1.2.11 you can use %aspnet-request{ASP.NET_SessionId} in conversion pattern for this purpose.

References: https://issues.apache.org/jira/browse/LOG4NET-87 http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html


You should create Application_PostAcquireRequestState handler in Global.asax.cs (it is called in every request):

protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
    log4net.ThreadContext.Properties["SessionID"] = Session.SessionID;
}

And add [%property{SessionID}] to conversionPattern.

Alexander K.
  • 1,222
  • 15
  • 20
  • 1
    This will fail if the request is split in different threads. One way for this to happen is if the application uses asyncrhonous pages/handlers/services. Also it seems like WCF AJAX services for some reason do switch threads for the same request after PostAcquireRequestState. – Stilgar May 28 '12 at 12:13
6

I was looking answer for this too and found out that %aspnet-request{ASP.NET_SessionId} works fine for me.

Hari Menon
  • 33,649
  • 14
  • 85
  • 108
Kappas
  • 61
  • 1
  • 1
  • This causes an exception in the middle of logging in the latest version of log4net, 1.2.11.0, if there is no session started when you log a message. You can spot the error by seeing a partially complete line in your logfile, but with no newline to separate it from other log entries. – Katie Kilian Jun 10 '13 at 22:17
5

Someone corrects me if I am wrong, but one ASP.NET thread can handle multiple sessions, so you can not use Session_Start as it is called once when the session starts. What it means is that as soon as a different user accesss the web site, your log4net.ThreadContext might be overwritten by the new user information.

You can either put the below code in Application_AcquireRequestState, or create a HttpModule and do that in AcquireRequestState method. AcquireRequestState is called when ASP.NET runtime is ready to acquire the Session state of the current HTTP request. If you interested in getting username, you can do that in AuthenticateRequest which is raised when ASP.NET runtime is ready to authenticate the identity of the user (and before the AcquireRequestState).

    private void AcquireRequestState(Object source, EventArgs e)
    {
        HttpApplication application = (HttpApplication)source;
        HttpContext context = application.Context;
        log4net.ThreadContext.Properties["SessionId"] = context.Session.SessionID;
     }

After that you can set up your log4net.config (or in web.config) like this.

<appender name="rollingFile"
      type="log4net.Appender.RollingFileAppender,log4net" >
  <param name="AppendToFile" value="false" />
  <param name="RollingStyle" value="Date" />
  <param name="DatePattern" value="yyyy.MM.dd" />
  <param name="StaticLogFileName" value="true" />

  <param name="File" value="log.txt" />
  <layout type="log4net.Layout.PatternLayout,log4net">
    <param name="ConversionPattern"
      value="%property{SessionId} %d [%t] %-5p %c - %m%n" />
  </layout>
</appender>

Hope this helps!

kimsk
  • 2,221
  • 22
  • 23
  • 1
    Tried this for a WCF app and context.Session is null, that leads to a null reference exception. – Vaccano May 01 '13 at 05:11
1

You can try:

<conversionPattern
    value="%date %-5level %logger ${COMPUTERNAME} [%property{SessionID}] - %message%newline" />

...in your Web.config, and in Global.asax.cs:

protected void Session_Start(object sender, EventArgs e)
{
    log4net.ThreadContext.Properties["SessionID"] = Session.SessionID;
    log4net.Config.XmlConfigurator.Configure();
}
Richard Ev
  • 52,939
  • 59
  • 191
  • 278
  • Hmm - this seems to work, but it looks like the session ID only appears for things that happen in the first postback. Subsequent trips back to the server have SessionID = null. Any other thoughts? – Chris Roberts Dec 12 '08 at 12:10
  • Not sure I'm afraid. We used this approach a while ago and had a suspicion that this setting would set a SessionID property for _everyone_ using the web app at that time (i.e. user1's SessionID propery value would get overridden by user2's). Sorry I can't offer any more real insight... – Richard Ev Dec 12 '08 at 14:43
  • 1
    I don't think this will work. Session_Start is only called on the first request that establishes a session. The properties assigned to that ThreadContext will die when the thread dies. So when the next request comes in, session is already established and no property will be set in ThreadContext. For this to work you should use the Application_BeginRequest event. – Peter Lillevold May 17 '09 at 20:19