Skip to content

Use of asp-session causing stack overflow #951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dgee2 opened this issue Mar 29, 2016 · 26 comments · Fixed by #41
Closed

Use of asp-session causing stack overflow #951

dgee2 opened this issue Mar 29, 2016 · 26 comments · Fixed by #41
Assignees
Labels
Milestone

Comments

@dgee2
Copy link

dgee2 commented Mar 29, 2016

Hi,

I've got a situation where if dnx is restarted for any reason (and thus sessions invalidated) any logging that is performed that has a layout that includes aspnet-session causes a stack overflow. I assume that somewhere there is some code that is trying to log the fact that the session has expired and so given that nlog is then trying to access the session again, we are getting a stack overflow.

Is there any solution to this? We would like to log some information from the session but we obviously cannot have the server crash just from someone accessing the server with an expired session.

Below is a copy of the repeating section of the stack trace:

Microsoft.AspNet.Session.dll!Microsoft.AspNet.Session.DistributedSession.LoadAsync()    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<Microsoft.AspNet.Session.DistributedSession.<LoadAsync>d__19>(ref Microsoft.AspNet.Session.DistributedSession.<LoadAsync>d__19 stateMachine)  Unknown
    Microsoft.AspNet.Session.dll!Microsoft.AspNet.Session.DistributedSession.LoadAsync()    Unknown
    Microsoft.AspNet.Session.dll!Microsoft.AspNet.Session.DistributedSession.Load() Unknown
    Microsoft.AspNet.Session.dll!Microsoft.AspNet.Session.DistributedSession.TryGetValue(string key, out byte[] value)  Unknown
    Microsoft.AspNet.Http.Extensions.dll!Microsoft.AspNet.Http.SessionExtensions.Get(Microsoft.AspNet.Http.Features.ISession session, string key)   Unknown
    Microsoft.AspNet.Http.Extensions.dll!Microsoft.AspNet.Http.SessionExtensions.GetString(Microsoft.AspNet.Http.Features.ISession session, string key) Unknown
    NLog.Web.ASPNET5.dll!NLog.Web.LayoutRenderers.AspNetSessionValueLayoutRenderer.DoAppend.AnonymousMethod__0(string k)    Unknown
    NLog.Web.ASPNET5.dll!NLog.Web.Internal.PropertyReader.GetValue(string key, System.Func<string, object> getVal, bool evaluateAsNestedProperties) Unknown
    NLog.Web.ASPNET5.dll!NLog.Web.LayoutRenderers.AspNetSessionValueLayoutRenderer.DoAppend(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent)  Unknown
    NLog.Web.ASPNET5.dll!NLog.Web.LayoutRenderers.AspNetLayoutRendererBase.Append(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent)    Unknown
    NLog.dll!NLog.LayoutRenderers.LayoutRenderer.Render(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent)  Unknown
    NLog.dll!NLog.Layouts.SimpleLayout.GetFormattedMessage(NLog.LogEventInfo logEvent)  Unknown
    NLog.dll!NLog.Layouts.Layout.Render(NLog.LogEventInfo logEvent) Unknown
    NLog.dll!NLog.Layouts.Layout.Precalculate(NLog.LogEventInfo logEvent)   Unknown
    NLog.dll!NLog.Targets.Target.PrecalculateVolatileLayouts(NLog.LogEventInfo logEvent)    Unknown
    NLog.dll!NLog.Targets.Wrappers.AsyncTargetWrapper.Write(NLog.Common.AsyncLogEventInfo logEvent) Unknown
    NLog.dll!NLog.Targets.Target.WriteAsyncLogEvent(NLog.Common.AsyncLogEventInfo logEvent) Unknown
    NLog.dll!NLog.LoggerImpl.WriteToTargetWithFilterChain(NLog.Internal.TargetWithFilterChain targetListHead, NLog.LogEventInfo logEvent, NLog.Common.AsyncContinuation onException)    Unknown
    NLog.dll!NLog.LoggerImpl.Write(System.Type loggerType, NLog.Internal.TargetWithFilterChain targets, NLog.LogEventInfo logEvent, NLog.LogFactory factory)    Unknown
    NLog.dll!NLog.Logger.WriteToTargets(NLog.LogEventInfo logEvent) Unknown
    NLog.dll!NLog.Logger.Log(NLog.LogEventInfo logEvent)    Unknown
    NLog.Extensions.Logging.dll!NLog.Extensions.Logging.NLogLogger.Log(Microsoft.Extensions.Logging.LogLevel logLevel, int eventId, object state, System.Exception exception, System.Func<object, System.Exception, string> formatter)  Unknown
    Microsoft.Extensions.Logging.dll!Microsoft.Extensions.Logging.Logger.Log(Microsoft.Extensions.Logging.LogLevel logLevel, int eventId, object state, System.Exception exception, System.Func<object, System.Exception, string> formatter)    Unknown
    Microsoft.Extensions.Logging.Abstractions.dll!Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(Microsoft.Extensions.Logging.ILogger logger, string format, object[] args)   Unknown
@304NotModified
Copy link
Member

Could you post your config? What's the value of throwExceptions?

@dgee2
Copy link
Author

dgee2 commented Mar 29, 2016

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      internalLogFile="C:\logging\NLog.log"
      internalLogLevel="Warn"
      autoReload="true">
  <extensions>
    <!--enable NLog.Web for ASP.NET5-->
    <add assembly="NLog.Web.ASPNET5"/>
  </extensions>
  <!-- 
  See https://github.com/nlog/nlog/wiki/Configuration-file 
  for information on customizing logging rules and outputs.
   -->
  <variable name="message_inline" value="${replace:inner=${message}:searchFor=\\r\\n|\\n:replaceWith=\\n:regex=true}"/>
  <variable name="exception_inline" value="${replace:inner=${exception:format=ToString}:searchFor=\\r\\n|\\n:replaceWith=\\n:regex=true}"/>
  <targets>
    <target name="logFileWrapped" xsi:type="AsyncWrapper" overflowAction="Discard" queueLimit="10000" batchSize="100" timeToSleepBetweenBatches="50">
      <target xsi:type="File"
        fileName="C:\logging\app.log"
        archiveFileName="C:\logging\archive\app-archive.{#}.log"
        archiveEvery="Day"
        archiveOldFileOnStartup="true"
        archiveNumbering="DateAndSequence"
        maxArchiveFiles="0"
        layout="${machinename}|${windows-identity}|${iis-site-name}|${longdate}|${level:uppercase=true}|${logger}|${aspnet-session:Variable=appLoginID}|${callsite-linenumber:skipFrames=3}|${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true:skipFrames=3}|${callsite:className=false:fileName=true:includeSourcePath=false:methodName=false:skipFrames=3}|${message_inline}|${exception_inline}"/>
    </target>
    <target name="csvFileWrapped" xsi:type="AsyncWrapper" overflowAction="Discard" queueLimit="10000" batchSize="100" timeToSleepBetweenBatches="50">
      <target xsi:type="File"
        fileName="C:\logging\app.csv"
        archiveFileName="C:\logging\archive\app-archive.{#}.csv"
        archiveEvery="Day"
        archiveOldFileOnStartup="true"
        archiveNumbering="DateAndSequence"
        maxArchiveFiles="0">
        <layout xsi:type="CsvLayout">
          <column name="hostname" layout="${machinename}"/>
          <column name="username" layout="${windows-identity}"/>
          <column name="application" layout="${iis-site-name}"/>
          <column name="level" layout="${level:uppercase=true}"/>
          <column name="time" layout="${longdate}"/>
          <column name="logger" layout="${logger}"/>
          <column name="appLoginID" layout="${aspnet-session:Variable=AppLoginID}"/>
          <column name="message" layout="${message}"/>
          <column name="file" layout="${callsite:className=false:fileName=true:includeSourcePath=false:methodName=false:skipFrames=3}"/>
          <column name="lineNumber" layout="${callsite-linenumber:skipFrames=3}"/>
          <column name="class" layout="${callsite:className=true:fileName=false:includeSourcePath=false:methodName=false:skipFrames=3}"/>
          <column name="method" layout="${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true:skipFrames=3}"/>
          <column name="stack" layout="${stacktrace:topFrames=10:skipFrames=3}" />
          <column name="exception" layout="${exception}"/>
        </layout>
      </target>
    </target>
    <target name="jsonFileWrapped" xsi:type="AsyncWrapper" overflowAction="Discard" queueLimit="10000" batchSize="100" timeToSleepBetweenBatches="50">
      <target xsi:type="File"
        fileName="C:\logging\app.json"
        archiveFileName="C:\logging\archive\app-archive.{#}.json"
        archiveEvery="Day"
        archiveOldFileOnStartup="true"
        archiveNumbering="DateAndSequence"
        maxArchiveFiles="0">
        <layout xsi:type="JsonLayout">
          <attribute name="hostname" layout="${machinename}"/>
          <attribute name="username" layout="${windows-identity}"/>
          <attribute name="application" layout="${iis-site-name}"/>
          <attribute name="level" layout="${level:uppercase=true}"/>
          <attribute name="time" layout="${longdate}"/>
          <attribute name="logger" layout="${logger}"/>
          <attribute name="appLoginID" layout="${aspnet-session:Variable=AppLoginID}"/>
          <attribute name="message" layout="${message}"/>
          <attribute name="file" layout="${callsite:className=false:fileName=true:includeSourcePath=false:methodName=false:skipFrames=3}"/>
          <attribute name="lineNumber" layout="${callsite-linenumber:skipFrames=3}"/>
          <attribute name="class" layout="${callsite:className=true:fileName=false:includeSourcePath=false:methodName=false:skipFrames=3}"/>
          <attribute name="method" layout="${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true:skipFrames=3}"/>
          <attribute name="stack" layout="${stacktrace:topFrames=10:skipFrames=3}" />
          <attribute name="exception" layout="${exception}"/>
        </layout>
      </target>
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Trace" writeTo="logFileWrapped"/>
    <logger name="*" minlevel="Trace" writeTo="csvFileWrapped"/>
    <logger name="*" minlevel="Trace" writeTo="jsonFileWrapped"/>
  </rules>
</nlog>

@dgee2
Copy link
Author

dgee2 commented Mar 29, 2016

There you go. Taking out the aspnet-session variables fixes everything. I'm not sure what you mean by throwExceptions. It doesn't appear to be in our log file though.

@304NotModified
Copy link
Member

ThrowExceptions is an attribute on <nlog AFAIK it's false by default.

I see that you have enabled the internal logger. Can you post the exception raised by NLog?

@304NotModified
Copy link
Member

Can you also post the platform an versions? (versions in NuGet )

@dgee2
Copy link
Author

dgee2 commented Mar 29, 2016

Hi, I'm on my phone on the way home so limited in what I can give you but
do know off the top of my head it is asp.net 5 rc 1 update 1. Not sure of
the nlog version though. We're running dnx451
On 29 Mar 2016 18:38, "Julian Verdurmen" [email protected] wrote:

Can you also post the platform an versions? (versions in NuGet )


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#951

@304NotModified
Copy link
Member

I'm thinking for a solution. There are some directions possible:

  • remember that we have been there before (in session renderer) - not stored in session ;)
  • disable logging when retrieving session value
  • Do possible better my checking before log has been raised in MS code

@304NotModified
Copy link
Member

PS: We think it is very important to fix this issue .

@dgee2
Copy link
Author

dgee2 commented Mar 30, 2016

Thanks for seeing it as important. It did kind of blow everything up for us (and left me very confused that it was taking down the whole process after changing no code!) Please be careful with disabling the logger as there are instances where the same logger is potentially being used on multiple threads/requests in asp.net. It may be best to put a try/catch around the call to the layout renderer and put those exceptions into the NLog file so that there is no risk of those exceptions causing a loop. I like your first solution though. (btw this is just comments based on our use case. I have very little knowledge of the internals.

Versions:

"dependencies": {
    "Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final",
    "Microsoft.AspNet.Http.Features": "1.0.0-rc1-final",
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.Abstractions": "6.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.Session": "1.0.0-rc1-final",
    "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
    "Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
    "Microsoft.Net.Http.Server": "1.0.0-rc1-final",
    "Microsoft.AspNet.Hosting.Abstractions": "1.0.0-rc1-final",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-rc1-final",
    "NLog.Extensions.Logging": "1.0.0-rc1-final-2016-02-06",
    "NLog.Web.ASPNET5": "4.2.1"
  }

Here's the NLog log:

Warn Exception in layout renderer. Exception: System.InvalidOperationException: Session has not been configured for this application or request.
   at Microsoft.AspNet.Http.Internal.DefaultHttpContext.get_Session()
   at NLog.Web.LayoutRenderers.AspNetSessionValueLayoutRenderer.DoAppend(StringBuilder builder, LogEventInfo logEvent)
   at NLog.Web.LayoutRenderers.AspNetLayoutRendererBase.Append(StringBuilder builder, LogEventInfo logEvent)
   at NLog.LayoutRenderers.LayoutRenderer.Render(StringBuilder builder, LogEventInfo logEvent)

@304NotModified
Copy link
Member

Hi!

It's good news that there is an exception thrown in AspNetSessionValueLayoutRenderer. Then I can fix it easily with a try catch

@304NotModified 304NotModified self-assigned this Mar 30, 2016
@304NotModified
Copy link
Member

What's the platform (net 4.5?. Net core?) and what is the NLog version? (4.2.3, 4.3, 4.4 beta?)

I'm thinking to fix this in NLog 4.3 (rc) and 4.4 beta. Because it could be also an problem with other layout renderers

@304NotModified
Copy link
Member

I checked the code, but don't get it. There is already a try-catch and the exception isn't rethrow by default

image

@304NotModified
Copy link
Member

I think the issue is maybe (also) here: https://github.com/aspnet/Session/blob/1.0.0-rc1/src/Microsoft.AspNet.Session/DistributedSession.cs#L142

not sure how to fix this.

@dgee2
Copy link
Author

dgee2 commented Apr 1, 2016

Hmm it really does look like the only way to prevent this from happening is to turn the logger off while we're getting the value from the session. Not sure what the further impact is if doing this.

It may be worth getting in contact with the asp.net team to a. let them know that this is a problem in this use case and b. see if they know if there is another solution.

@304NotModified
Copy link
Member

@dgee2

I have implemented a fix, can you confirm it's working? It's released as 4.2.2-RC: https://www.nuget.org/packages/NLog.Web.ASPNET5/4.2.2-RC

@dgee2
Copy link
Author

dgee2 commented Apr 6, 2016

Sorry I've only just had a chance to look at this. From a brief look, it looks like it is no longer crashing but it doesn't look like the value is being placed in the JSON output at least. I'll have to have another look in the morning.

@304NotModified
Copy link
Member

Hi @dgee2, any luck on this?

@dgee2
Copy link
Author

dgee2 commented Apr 15, 2016

Hi, it seems to have worked well. My only comment would be the the NLog log file is growing as we have a significant amount of logging outside of web requests. Currently have the following for 115000 lines...

Warn Exception in layout renderer. Exception: System.InvalidOperationException: Session has not been configured for this application or request.
   at Microsoft.AspNet.Http.Internal.DefaultHttpContext.get_Session()
   at NLog.Web.LayoutRenderers.AspNetSessionValueLayoutRenderer.DoAppend(StringBuilder builder, LogEventInfo logEvent)
   at NLog.Web.LayoutRenderers.AspNetLayoutRendererBase.Append(StringBuilder builder, LogEventInfo logEvent)
   at NLog.LayoutRenderers.LayoutRenderer.Render(StringBuilder builder, LogEventInfo logEvent)

It might be worth looking into having an option to not log exceptions in a layout item or swallow this specific exception. I'm assuming that there isn't another error message that is sitting in the NLog log file that I should be aware of and it's virtually impossible to find anything else atm.

@dgee2
Copy link
Author

dgee2 commented Apr 15, 2016

BTW for anyone that sees this, the build suggested doesn't log all of the log levels correctly if you are using RC2 NLog with RC1 asp.net. This is due to the change in logging between RC1 and RC2 on Microsofts part.

@304NotModified
Copy link
Member

Indeed, it's not rc2 compatible. We are waiting for rc2 on nuget, which takes ages.

I dunno how to test if the session is available without exception. If we can do that, we can solve this.

@dgee2
Copy link
Author

dgee2 commented Apr 15, 2016

I think I would probably wrap it in a try catch and inspect the exception
before throwing it again or just carrying on. Has anyone spoken to the
asp.net team about this yet? They may be able to do something that may help
your end. E.g. a more specific exception to check for.
On 15 Apr 2016 18:28, "Julian Verdurmen" [email protected] wrote:

Indeed, it's not rc2 compatible. We are waiting for rc2 on nuget, which
takes ages.

I dunno how to test if the session is available without exception. If we
can do that, we can solve this.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#951

@304NotModified
Copy link
Member

Nope didn't speak to aspnet team yet.

A try catch without logging is bad.

@dgee2
Copy link
Author

dgee2 commented Apr 15, 2016

Well if you expect that this may happen and have a conduit option for it
then I'm not sure I would consider this situation bad. It's currently fast
worse not having a hope of finding real error messages.
On 15 Apr 2016 18:35, "Julian Verdurmen" [email protected] wrote:

Nope didn't speak to aspnet team yet.

A try catch without logging is bad.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#951

@304NotModified
Copy link
Member

I think I would probably wrap it in a try catch and inspect the exception
before throwing it again or just carrying on

@dgee2

I will do a try-catch and write the error as debug level as work around. If you set the internalloglevel on off, info, warn, error or fatal, this issues has been solved. Will fix this and will release NLog.Web.ASPNET5 4.2.2 afterwards

@dgee2
Copy link
Author

dgee2 commented Apr 15, 2016

Cool thanks
On 15 Apr 2016 19:32, "Julian Verdurmen" [email protected] wrote:

I think I would probably wrap it in a try catch and inspect the exception
before throwing it again or just carrying on

@dgee2 https://github.com/dgee2

I will do a try-catch and write the error as debug level as work around.
If you set the internalloglevel on off, info, warn, error or fatal, this
issues has been solved. Will fix this and will release NLog.Web.ASPNET5
4.2.2 afterwards


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#951

@304NotModified
Copy link
Member

Has anyone spoken to the
asp.net team about this yet?

Done and now a more proper fix. Released in 4.2.2: https://www.nuget.org/packages/NLog.Web.ASPNET5/4.2.2

@snakefoot snakefoot transferred this issue from NLog/NLog.Extensions.Logging Jun 10, 2023
@snakefoot snakefoot added this to the 4.2.2 milestone Jun 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants