I want to implement global error handling. I have done this using the Enterprise Library Exception & Logging blocks and by using the Application_Error event in the Global.asax. I only catch errors that I need to do something with, most errors are left unhandled
to be caught by the Application_Error event. I need to include additional information in my error so I am trying to add the session, application, form (POST), & Querystring variables and their values to the Data of the error. When I step through using F11
in VS 2005 everything works great. The session, application, form & querystring collections are all iterated through and added to the error Data which in turn shows up in the e-mail that the Logging block sends me. However, when I post to a production server
all that is returned is the application variables. It seems like it is just impossible to get session variables in the application_error event. I have heard that if an unhandled exception occurs that a new session is started (which would be totally stupid),
and this would seem to mean that once something has made it to the Application_Error event, a new session is started and it is not possible to get session variables from the session that created the error. I'm not sure I believe this. I have also heard that
I need to call Server.ClearError, but that has no effect. I even tried using try/catch blocks on all code in user interface and session is STILL not being added to the error Data collection unless I am debugging through visual studio. What gives? I really
don't want to us ANY try/catch blocks unless I need to react to a specific error. I want my error handling in the Global.asax file and I want it to send me an e-mail with all session, application, querystring, and POST variables with their values. How can
I implement something like this? Why are all the session variables destroyed when an error occurs?
Hi,
Depending on where the error is occuring, the Session object may not yet be obtainable.
For example at Begin_Request stage, the session object has not yet been retrieve, so if an error is being thrown here then the session object is not available.
Do you know where the errors you are catching are occuring? at the Page/HttpHandler level or earlier in the request?
Without seeing some of your code that obtains the session information in the error handler, I can't advise more, but I can tell you that the creation of a new session on application error is false information.
What they are probably referring to is that .NET 2.0 any unhandled exception outside of "a request" causes the application to unexpectedly quit. This is default behaviour to ensure that managed thread exceptions do not get swallowed etc.
Exceptions that occur in the context of a request are handled and wrapped by an HttpException object - therefore you should be seeing Session in your event handler.
Are you able to post a snippet of that session retrieval code?
Also, you should be aware that not placing try.catch statements anywhere and allowing all exceptions to bubble up into a global handler is very expensive in terms of resource.
Each unhandled exception is wrapped, a stack trace walk-through occurs, all finally statements are executed..lots of things happen in the background... only then can ASP.NET issue the error.
If this occurs a lot, then your application performance will suffer.
You should handle exceptions where you able, and insert these directly into your database.. there should be a feature that allows you to record exceptions direct from code so that you don't have to rely on the unhandled one bubbling up:
e.g.
try
{
//do something
}
catch (Exception ex)
{
ExceptionRecorder.RecordExceptionInDatabase(ex);
}
The exceptionRecorder does not exist, this is an example.. you are specifically catching the exception, but still logging it, and even better you have more control over what occurs next in your code logic.
Well that is an interesting answer, i appreciate your insight. However I have also heard that using Try Catches is expensive and that global error handling is the way to go because of how expensive the try catches are. I was under the impression that you
should only use a catch if you know the error you are looking for and want to perform a specific action for that error (retry a database connection, etc), otherwise let the error bubble through. Regardless, global error handling is impossible in ASP.NET (if
you want any kind of details about your error) because the session variables are not available in the
Application_Error event, for whatever reason. I have read post after post and this is in fact the case, if your app makes it to the application_error event, any session variables that you had been previously using are not available. Not sure
if it is because a new session is started or because of the order events fire in the global.asax. But it really doesn't matter though because the end result is the same...you can't get the values of session variables, iterate through the querystring, or the
form variables. So, if you want to use application_error event in to handle errors, you better be sure that you don't need ANY context information about the error like what user caused it, what session variable values are, what form post or querystring values
are etc. I ended up having to make an Error page and then use try catches in all of my code on the user interface layer. The catch stores the error in the session and then redirects to the error page. The error page displayes a user friendly error message,
grabs all the session, form, querystring, & application variables and adds them to the data section of the exception. Then I use Enterprise library to handle the exception. IMO this is pretty lame, would be much better to have it all handled in the global.asax
and would save me lots of time and lines of code.
What is intersting is that in your original post you said that you can log ok when debugging, but in production web site the variables are not logged.
This is the root cause of your issue as you SHOULD be able to capture that data.
I'm sure you know some of this already, but I'll explain anyway(!):
In the Application_Error event, the sender object can be cast and you can retrieve the HttpApplication object and the HttpContext:
The HttpContext is the class that corresponds to the current HttpRequest in process. It is always available during the page lifecycle.
In fact there is a static method on the class:
HttpContext.Current
that retreives the current context, so you can in fact utilise it anywhere.. it doesn't have to be passed in as a method parameter.
When your code is executing in your page/business/data layers at the HttpHandler level, when an exception is encountered the HttpContext object is still current and instantiated.
Therefore the object's properties will still be valid... Request, Response, Session etc.
So it is very strange that these objects do not produce any data when an error happens at this stage of the request life cycle.
(To read more on the application life cycle, here is a great article from MSDN ASP.NET Life cycle overview )
You therefore, in my opinon, have a particular production issue where the data is not being catpured..
either there is something wrong in the lifce cycle OR your code for capturing is somehow not working due to different settings between dev and live set ups.
Contact me by email via my profile here on the site and I can send you my email address if you wish - if you share the portion of code that is actually processing inside the Application_Error event I might be able to advise if there is a specific code issue.
Otherwise, I am not really a production debugging expert, so cannot advise further on getting to the source of your problem.
What I can tell you is the information SHOULD be available to you.
Thanks a lot for the detailed post. Below is the code I was using in my Application_Error event:
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim ex as Exception = Server.GetLastError().GetBaseException()
For Each sKey as String in httpcontext.Current.Session.Keys
ex.Data.Add(sKey, httpcontext.Current.Session(sKey).ToString)
Next
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicy.HandleException(ex, "Global Policy")
Response.Redirect("~/ErrorOccurred.aspx")
End Sub
As you can see I was using HTTPContext.Current to retrieve the session variables. I didn't move on to trying to grab all the other collections because I couldn't get
this to work in production. I agree that it SHOULD work, but it just doesn't and I have seen posts all over the place from many other people with the exact same problem, so this is obviously a problem. I have no idea what
the difference would be between when I am running in debug mode and when it is running on the production server except for the "debug=false" on production but I wouldn't think this would affect session variables. I have heard many explanations from reading
the posts including: "When an unhandled exception occurs the session is killed", or "The Application_Error event occurs before the session is loaded into the global asax". I personally don't buy either one of these given the fact that it did work from F11.
If my code gives you any ideas I would love to hear them
Hi again,
Now what is interesting here is that your pinpointing of the problem being that session is "empty" is (perhaps) based on an assumption that :
1. The Exception.Data collection is being recorded correctly
2. The Enterprise Library ExceptionPolicy class is handling the exception correctly in production.
So I think if you have not already done so that you should put code in to log the session key and values to a text file to trace if there are actually session variables, but they are not being collected and handled correctly in production.
Outside of the loop write the timestamp, exception message and then the sessionID, and inside the loop write each of the key/value pairs out to the file.
You can then see if your problem really lies in a different direction - if the log file has empty session data for the exception (write something like "SessionID: could not be obtained" for null or empty strings) then you know it really is your session object/property
playing up.
That's the only real steer I have on that.
Another piece of advice on a different tangent is on the use of "Response.Redirect".
What that does is send a HttpHeader back to the client with a 302 "Temporary Move" response.
If in your resulting page "ErrorEncountered.aspx" you are not changing the HttpHeader to an error like a 500 "server error", you may be causing incorrect behaviour for spiders and agents.
e.g. if a bot goes to the "default.aspx" page (that errors on server), it receives a "this page has moved" response and then a "200 ok". So depending on the behaviour of the bot, it may see your error text/page as actual moved content.
Try using a Server.Transfer() instead. This moves the processing on the server to the new page, but does not inform the client. The url also stays the same for the client, yet they get a different response. Then ensure you are setting the 500 "server error"
response in the errorecountered page. In this new example:
the bot goes to "default.aspx" and gets a 500 "server error" with appropriate text. It knows there is something wrong with "default.aspx" and will come back later (or whatever its error detection routine is).
Sorry to go on(!), but it something we did recently to great effect.
I agree with jfkrueger that this is pretty lame. I am trying to do something very similar i.e. global error handling. It seems to me that this is how everyone would want to do it. My code in the
global.asax looks like this:
So the interesting thing is that the Server.GetLastError works just fine. So I am able to log the error just fine, which is a good first step. But what it I want to display the error to the developers (which is exactly what I want to do while we are testing).
I tried storing it in a session variable and then checking that from the error.aspx page, the totally obvious solution, but no luck because you can't set a session variable from the global.asax file. So the best thing I could figure out was to use an Application
variable to store the last error. That should work fine as long as no other errors are thrown between the time I store the variable and the time the server processes the Response.Redirect, but if there are a bunch of errors happening at once, there is a chance
that the wrong error could get reported. Lame.
jfkrueger
Member
120 Points
112 Posts
Access Session Variables from Application_Error event in Global.asax
Oct 19, 2007 06:31 PM|LINK
I want to implement global error handling. I have done this using the Enterprise Library Exception & Logging blocks and by using the Application_Error event in the Global.asax. I only catch errors that I need to do something with, most errors are left unhandled to be caught by the Application_Error event. I need to include additional information in my error so I am trying to add the session, application, form (POST), & Querystring variables and their values to the Data of the error. When I step through using F11 in VS 2005 everything works great. The session, application, form & querystring collections are all iterated through and added to the error Data which in turn shows up in the e-mail that the Logging block sends me. However, when I post to a production server all that is returned is the application variables. It seems like it is just impossible to get session variables in the application_error event. I have heard that if an unhandled exception occurs that a new session is started (which would be totally stupid), and this would seem to mean that once something has made it to the Application_Error event, a new session is started and it is not possible to get session variables from the session that created the error. I'm not sure I believe this. I have also heard that I need to call Server.ClearError, but that has no effect. I even tried using try/catch blocks on all code in user interface and session is STILL not being added to the error Data collection unless I am debugging through visual studio. What gives? I really don't want to us ANY try/catch blocks unless I need to react to a specific error. I want my error handling in the Global.asax file and I want it to send me an e-mail with all session, application, querystring, and POST variables with their values. How can I implement something like this? Why are all the session variables destroyed when an error occurs?
foreachbiscu...
Participant
1598 Points
210 Posts
Re: Access Session Variables from Application_Error event in Global.asax
Oct 23, 2007 06:48 PM|LINK
Hi,
Depending on where the error is occuring, the Session object may not yet be obtainable.
For example at Begin_Request stage, the session object has not yet been retrieve, so if an error is being thrown here then the session object is not available.
Do you know where the errors you are catching are occuring? at the Page/HttpHandler level or earlier in the request?
Without seeing some of your code that obtains the session information in the error handler, I can't advise more, but I can tell you that the creation of a new session on application error is false information.
What they are probably referring to is that .NET 2.0 any unhandled exception outside of "a request" causes the application to unexpectedly quit. This is default behaviour to ensure that managed thread exceptions do not get swallowed etc.
Exceptions that occur in the context of a request are handled and wrapped by an HttpException object - therefore you should be seeing Session in your event handler.
Are you able to post a snippet of that session retrieval code?
Also, you should be aware that not placing try.catch statements anywhere and allowing all exceptions to bubble up into a global handler is very expensive in terms of resource.
Each unhandled exception is wrapped, a stack trace walk-through occurs, all finally statements are executed..lots of things happen in the background... only then can ASP.NET issue the error.
If this occurs a lot, then your application performance will suffer.
You should handle exceptions where you able, and insert these directly into your database.. there should be a feature that allows you to record exceptions direct from code so that you don't have to rely on the unhandled one bubbling up:
e.g.
try
{
//do something
}
catch (Exception ex)
{
ExceptionRecorder.RecordExceptionInDatabase(ex);
}
The exceptionRecorder does not exist, this is an example.. you are specifically catching the exception, but still logging it, and even better you have more control over what occurs next in your code logic.
Read these posts to show you that I'm not just giving you a crank opinion.
Getting better information on unhandled exceptions
Exceptions are expensive
Error Handling
foreachbiscuit
blog @ http://foreachbiscuit.wordpress.com
jfkrueger
Member
120 Points
112 Posts
Re: Access Session Variables from Application_Error event in Global.asax
Oct 30, 2007 08:08 PM|LINK
Well that is an interesting answer, i appreciate your insight. However I have also heard that using Try Catches is expensive and that global error handling is the way to go because of how expensive the try catches are. I was under the impression that you should only use a catch if you know the error you are looking for and want to perform a specific action for that error (retry a database connection, etc), otherwise let the error bubble through. Regardless, global error handling is impossible in ASP.NET (if you want any kind of details about your error) because the session variables are not available in the Application_Error event, for whatever reason. I have read post after post and this is in fact the case, if your app makes it to the application_error event, any session variables that you had been previously using are not available. Not sure if it is because a new session is started or because of the order events fire in the global.asax. But it really doesn't matter though because the end result is the same...you can't get the values of session variables, iterate through the querystring, or the form variables. So, if you want to use application_error event in to handle errors, you better be sure that you don't need ANY context information about the error like what user caused it, what session variable values are, what form post or querystring values are etc. I ended up having to make an Error page and then use try catches in all of my code on the user interface layer. The catch stores the error in the session and then redirects to the error page. The error page displayes a user friendly error message, grabs all the session, form, querystring, & application variables and adds them to the data section of the exception. Then I use Enterprise library to handle the exception. IMO this is pretty lame, would be much better to have it all handled in the global.asax and would save me lots of time and lines of code.
That's my 2 cents. Thanks agian for your post!
foreachbiscu...
Participant
1598 Points
210 Posts
Re: Access Session Variables from Application_Error event in Global.asax
Oct 31, 2007 09:40 AM|LINK
What is intersting is that in your original post you said that you can log ok when debugging, but in production web site the variables are not logged.
This is the root cause of your issue as you SHOULD be able to capture that data.
I'm sure you know some of this already, but I'll explain anyway(!):
In the Application_Error event, the sender object can be cast and you can retrieve the HttpApplication object and the HttpContext:
private void Application_Error(object sender, EventArgs e) { HttpApplication application = (HttpApplication)sender; HttpContext context = application.Context; }The HttpContext is the class that corresponds to the current HttpRequest in process. It is always available during the page lifecycle.
In fact there is a static method on the class:
HttpContext.Current
that retreives the current context, so you can in fact utilise it anywhere.. it doesn't have to be passed in as a method parameter.
When your code is executing in your page/business/data layers at the HttpHandler level, when an exception is encountered the HttpContext object is still current and instantiated.
Therefore the object's properties will still be valid... Request, Response, Session etc.
So it is very strange that these objects do not produce any data when an error happens at this stage of the request life cycle.
(To read more on the application life cycle, here is a great article from MSDN
ASP.NET Life cycle overview )
You therefore, in my opinon, have a particular production issue where the data is not being catpured..
either there is something wrong in the lifce cycle OR your code for capturing is somehow not working due to different settings between dev and live set ups.
Contact me by email via my profile here on the site and I can send you my email address if you wish - if you share the portion of code that is actually processing inside the Application_Error event I might be able to advise if there is a specific code issue.
Otherwise, I am not really a production debugging expert, so cannot advise further on getting to the source of your problem.
What I can tell you is the information SHOULD be available to you.
httpcontext
foreachbiscuit
blog @ http://foreachbiscuit.wordpress.com
jfkrueger
Member
120 Points
112 Posts
Re: Access Session Variables from Application_Error event in Global.asax
Nov 01, 2007 07:22 PM|LINK
Hello foreachbiscuit ,
Thanks a lot for the detailed post. Below is the code I was using in my Application_Error event:
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Dim ex as Exception = Server.GetLastError().GetBaseException() For Each sKey as String in httpcontext.Current.Session.Keys ex.Data.Add(sKey, httpcontext.Current.Session(sKey).ToString) Next Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicy.HandleException(ex, "Global Policy") Response.Redirect("~/ErrorOccurred.aspx") End SubAs you can see I was using HTTPContext.Current to retrieve the session variables. I didn't move on to trying to grab all the other collections because I couldn't get this to work in production. I agree that it SHOULD work, but it just doesn't and I have seen posts all over the place from many other people with the exact same problem, so this is obviously a problem. I have no idea what the difference would be between when I am running in debug mode and when it is running on the production server except for the "debug=false" on production but I wouldn't think this would affect session variables. I have heard many explanations from reading the posts including: "When an unhandled exception occurs the session is killed", or "The Application_Error event occurs before the session is loaded into the global asax". I personally don't buy either one of these given the fact that it did work from F11. If my code gives you any ideas I would love to hear themThanks again!
foreachbiscu...
Participant
1598 Points
210 Posts
Re: Access Session Variables from Application_Error event in Global.asax
Nov 01, 2007 07:45 PM|LINK
Hi again,
Now what is interesting here is that your pinpointing of the problem being that session is "empty" is (perhaps) based on an assumption that :
1. The Exception.Data collection is being recorded correctly
2. The Enterprise Library ExceptionPolicy class is handling the exception correctly in production.
So I think if you have not already done so that you should put code in to log the session key and values to a text file to trace if there are actually session variables, but they are not being collected and handled correctly in production.
Outside of the loop write the timestamp, exception message and then the sessionID, and inside the loop write each of the key/value pairs out to the file.
You can then see if your problem really lies in a different direction - if the log file has empty session data for the exception (write something like "SessionID: could not be obtained" for null or empty strings) then you know it really is your session object/property playing up.
That's the only real steer I have on that.
Another piece of advice on a different tangent is on the use of "Response.Redirect".
What that does is send a HttpHeader back to the client with a 302 "Temporary Move" response.
If in your resulting page "ErrorEncountered.aspx" you are not changing the HttpHeader to an error like a 500 "server error", you may be causing incorrect behaviour for spiders and agents.
e.g. if a bot goes to the "default.aspx" page (that errors on server), it receives a "this page has moved" response and then a "200 ok". So depending on the behaviour of the bot, it may see your error text/page as actual moved content.
Try using a Server.Transfer() instead. This moves the processing on the server to the new page, but does not inform the client. The url also stays the same for the client, yet they get a different response. Then ensure you are setting the 500 "server error" response in the errorecountered page. In this new example:
the bot goes to "default.aspx" and gets a 500 "server error" with appropriate text. It knows there is something wrong with "default.aspx" and will come back later (or whatever its error detection routine is).
Sorry to go on(!), but it something we did recently to great effect.
foreachbiscuit
blog @ http://foreachbiscuit.wordpress.com
gprayback
Member
2 Points
1 Post
Re: Access Session Variables from Application_Error event in Global.asax
Nov 13, 2007 05:32 AM|LINK
I agree with jfkrueger that this is pretty lame. I am trying to do something very similar i.e. global error handling. It seems to me that this is how everyone would want to do it. My code in the global.asax looks like this:
void Application_Error(object sender, EventArgs e){
// Code that runs when an unhandled error occurs Exception lastError = Server.GetLastError();
WebFramework.ExceptionHandler.Handle(lastError, state);Application["lasterror"] = lastError;
Response.Redirect("~/error.aspx");}
So the interesting thing is that the Server.GetLastError works just fine. So I am able to log the error just fine, which is a good first step. But what it I want to display the error to the developers (which is exactly what I want to do while we are testing). I tried storing it in a session variable and then checking that from the error.aspx page, the totally obvious solution, but no luck because you can't set a session variable from the global.asax file. So the best thing I could figure out was to use an Application variable to store the last error. That should work fine as long as no other errors are thrown between the time I store the variable and the time the server processes the Response.Redirect, but if there are a bunch of errors happening at once, there is a chance that the wrong error could get reported. Lame.