How should I log the extra details in webexception, soap exception etc? - c#

In enterprise library I wasn't getting enough detail put into my logs, so I started writing this handler to pull out of the exception specific properties and add them to the message string:
[ConfigurationElementType(typeof(CustomHandlerData))]
public class ExposeDetailExceptionHandler : IExceptionHandler
{
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
if (exception is System.Net.WebException)
return ExposeDetail((System.Net.WebException)exception);
if (exception is System.Web.Services.Protocols.SoapException)
return ExposeDetail((System.Web.Services.Protocols.SoapException)exception);
return exception;
}
private Exception ExposeDetail(System.Net.WebException Exception)
{
string details = "";
details += "System.Net.WebException: " + Exception.Message + Environment.NewLine;
details += "Status: " + Exception.Status.ToString() + Environment.NewLine;
return new Exception(details, Exception);
}
private Exception ExposeDetail(System.Web.Services.Protocols.SoapException Exception)
{
//etc
}
}
(As as aside is there a better way of picking which version of ExposeDetail gets run?)
Is this the best or accepted way to log these details, my initial thought is that I should be implementing an ExceptionFormatter but this seemed a lot simpler.

Use Exception.Data. You can collect any extra details you want to log at the point the exception is first caught and add them into Exception.Data. You can also add other information that wasn't part of the original exception such as the Url, http headers, ...
Your exception logging code can then pick up Exception.Data and add all that information to the log.
You don't need to wrap the exception nor do you need to lose any of the call stack when you handle it this way. Use throw to rethrow the original exception, catch it again further up the stack, add more context to the .Data on it and so on out until you get to your exception handler.

I think you are right: an ExceptionFormatter is probably a better way.
I would use the extended properties to add your details. I don't think that it is any more complicated than a handler.
E.g.:
public class AppTextExceptionFormatter : TextExceptionFormatter
{
public AppTextExceptionFormatter(TextWriter writer,
Exception exception,
Guid handlingInstanceId)
: base (writer, exception, handlingInstanceId)
{
if (exception is System.Net.WebException)
{
AdditionalInfo.Add("Status", ((System.Net.WebException)exception).Status.ToString());
}
else if (exception is System.Web.Services.Protocols.SoapException)
{
AdditionalInfo.Add("Actor", ((SoapException)exception).Actor);
}
}
}

Related

Handle Error with NLog and Try Catch

I log errors in my Actions using NLog to store errors with additional information, for example:
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x;
return RedirectToAction(MVC.Client.Index());
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
}
}
And I have Error handling configured in Web.config:
<customErrors mode="On" />
But I don't get redirected to the Error.cshtml when I execute the Action (the page remains in the same place), why?
Can I use Elmah to do the same thing? (logging additional information like client Id)
First of all, most people solve this error by not catching the exception. This way, the exception propagates to ASP.NET, which displays a "500 Internal Error" webpage, and all the pertinent information is logged.
If your server is configured for production, the error page will just say "an error occurred, details were logged."
If the server is configured for development, then you will get the famous yellow page with the exception type, the message, and the stack trace.
Swallowing the exception and manually redirecting to an error page is a bad practice because it hides errors. There are tools that examine your logs and give you nice statistics, for example about percentages of successful/failed requests, and these won't work any more.
So, not swallowing the exception is what people do, and at the very least, it solves your problem.
Now, I find this very clunky, because I do not like manually looking for the source files mentioned in the yellow page and manually going to the mentioned line numbers. I practically have no use for the yellow page, it might just as well just say "an error occurred, cry me a river, nah-nah-nah." I don't read the yellow page.
Instead, I do like to log exceptions on my own, and I have my logger begin each line with full-path-to-source-filename(line):, so that every line on the debug log in visual studio is clickable, and clicking on a line automatically opens up the right source file, and scrolls to the exact line that issued the log message. If you want this luxury, then go ahead and catch the exception, but right after logging the exception you have to rethrow it, so that things can follow their normal course.
Amendment
Here is some information that was added in comments:
So, you can do the following:
try
{
...
}
catch (Exception e)
{
log( "information" );
throw; //special syntax which preserves original stack trace
}
Or
try
{
...
}
catch (Exception e)
{
throw new Exception( "information", e ); //also preserves original stack trace
}
Do not do this: catch( Exception e ) { log( "information" ); throw e; } because it loses the original stack trace information of e.
In your code, error occur at the division portion(x/=x) so no execution of redirect line(index page) and jump to catch portion executing the logger. You have to define the redirect to Error.cshtml in catch portion also.
Note: when you use try catch block error will not occur at ASP.NET level resulting no redirect to Error.cshtml page
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x; /// error occur here
return RedirectToAction(MVC.Client.Index()); /// no execution of this line
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
/// add redirect link here
return RedirectToAction(MVC.Client.Error()); /// this is needed since the catch block execute mean no error at ASP.net level resulting no redirect to default error page
}
}
This will streamline your exception handling and allow you to manage the process more succinctly. Create an attribute like this:
public class HandleExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
// Pass in necessary data, etc
private string _data;
public string Data
{
get { return _data; }
set { _data = value; }
}
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// Logging code here
// Do something with the passed-in properties (Data in this code)
// Use the filterContext to retrieve all sorts of info about the request
// Direct the user
base.OnException(filterContext);
}
}
Now you can use it on a controller or method level with an attribute like this:
[HandleException(Data="SomeValue", View="Error")]
Or, register it globally (global.asax) like this:
GlobalFilters.Filters.Add(new HandleExceptionAttribute());

How to add information in ASP.NET Exception Stack

I want to put some extra info on how program works, if theres an exception, then I like these extra info be wrote in the exception report, the normal whole lot info that ELMAH recorded.
And if theres no exception, then I do not need the info, just let it go. So I do not think write everything in log is a good idea for me.
Please advice how can I write extra info that will recorded with the exception. Thanks
You could create your own exception class
public class YourCustomException : Exception
{
public YourCustomException(Exception inner, string message)
: base(inner, message)
{
}
}
... and add additional information to the Message.
try
{
// ...
}
catch(Exception ex)
{
throw new YourCustomException(ex, "More information");
}

How to catch the original (inner) exception in C#?

i'm calling a function that throws a custom exception:
GetLockOwnerInfo(...)
This function in turn is calling a function that throws an exception:
GetLockOwnerInfo(...)
ExecuteReader(...)
This function in turn is calling a function that throws an exception:
GetLockOwnerInfo(...)
ExecuteReader(...)
ExecuteReader(...)
And so on:
GetLockOwnerInfo(...)
ExecuteReader(...)
ExecuteReader(...)
ExecuteReaderClient(...)
Fill(...)
One of these functions throws an SqlException, although that code has no idea what an SqlException is.
Higher levels wrap that SqlException into another BusinessRuleException in order to include some special properties and additional details, while including the "original" exception as InnerException:
catch (DbException ex)
{
BusinessRuleExcpetion e = new BusinessRuleException(ex)
...
throw e;
}
Higher levels wrap that BusinessRuleException into another LockerException in order to include some special properties and additional details, while including the "original" exception as InnerException:
catch (BusinessRuleException ex)
{
LockerException e = new LockerException(ex)
...
throw e;
}
The problem now is that i want to catch the origianl SqlException, to check for a particular error code.
But there's no way to "catch the inner exception":
try
{
DoSomething();
}
catch (SqlException e)
{
if (e.Number = 247)
{
return "Someone";
}
else
throw;
}
i thought about catching SqlException right when it's thrown, and copy various values to the re-thrown exception - but that code is not dependant on Sql. It is experiencing an SqlException, but it has no dependency on SqlException.
i thought about catching all exceptions:
try
{
DoSomething(...);
}
catch (Exception e)
{
SqlException ex = HuntAroundForAnSqlException(e);
if (ex != null)
{
if (e.Number = 247)
{
return "Someone";
}
else
throw;
}
else
throw;
}
But that's horrible code.
Given that .NET does not let you alter the Message of an Exception to include additional information, what is the intended mechanism to catch original exceptions?
You need c# 6 / visual studio 2015 in order to do this using a predicate:
catch (ArgumentException e) when (e.ParamName == “…”)
{
}
Official C# Try/Catch Documentation
I hate to have to tell you this, but you cannot catch an inner exception.
What you can do is inspect one.
I suggest you catch your high-level exception (I believe it was LockerException) and inspect the InnerException property of that exception. Check the type, and if it's not a SqlException, check the InnerException of that exception. Walk each one until you find a SqlException type, then get the data you need.
That said, I agree with dasblinkenlight that you should consider -- if possible -- a heavy refactor of your exception framework.
Checking the error code of a wrapped exception is not a good practice, because it hurts encapsulation rather severely. Imagine at some point rewriting the logic to read from a non-SQL source, say, a web service. It would throw something other than SQLException under the same condition, and your outer code would have no way to detect it.
You should add code to the block catching SQLException to check for e.Number = 247 right then and there, and throw BusinessRuleException with some property that differentiates it from BusinessRuleException thrown in response to non-SQLException and SQLException with e.Number != 247 in some meaningful way. For example, if the magic number 247 means you've encountered a duplicate (a pure speculation on my part at this point), you could do something like this:
catch (SQLException e) {
var toThrow = new BusinessRuleException(e);
if (e.Number == 247) {
toThrow.DuplicateDetected = true;
}
throw toThrow;
}
When you catch BusinessRuleException later, you can check its DuplicateDetected property, and act accordingly.
EDIT 1 (in response to the comment that the DB-reading code cannot check for SQLException)
You can also change your BusinessRuleException to check for SQLException in its constructor, like this:
public BusinessRuleException(Exception inner)
: base(inner) {
SetDuplicateDetectedFlag(inner);
}
public BusinessRuleException(string message, Exception inner)
: base(message, inner) {
SetDuplicateDetectedFlag(inner);
}
private void SetDuplicateDetectedFlag(Exception inner) {
var innerSql = inner as SqlException;
DuplicateDetected = innerSql != null && innerSql.Number == 247;
}
This is less desirable, because it breaks encapsulation, but at least it does it in a single place. If you need to examine other types of exceptions (e.g. because you've added a web service source), you could add it to the SetDuplicateDetectedFlag method, and everything would work again.
Having an outer application layer care about the details of a wrapped exception is a code smell; the deeper the wrapping, the bigger the smell. The class which you now have wrapping the SqlException into a dbException is presumably designed to expose an SqlClient as a generic database interface. As such, that class should include a means of distinguishing different exceptional conditions. It may, for example, define a dbTimeoutWaitingForLockException and decide to throw it when it catches an SqlException and determines based upon its error code that there was a lock timeout. In vb.net, it might be cleaner to have a dbException type which exposes an ErrorCause enumeration, so one could then say Catch Ex as dbException When ex.Cause = dbErrorCauses.LockTimeout, but unfortunately exception filters are not usable in C#.
If one has a situation where the inner-class wrapper won't know enough about what it's doing to know how it should map exceptions, it may be helpful to have the inner-class method accept an exception-wrapping delegate which would take an exception the inner class has caught or would "like" to throw, and wrap it in a way appropriate to the outer class. Such an approach would likely be overkill in cases where the inner class is called directly from the outer class, but can be useful if there are intermediate classes involved.
Good question and good answers!
I just want to supplement the answers already given with some further thoughts:
On one hand I agree with dasblinkenlight and the other users. If you catch one exception to rethrow an exception of a different type with the original exception set as the inner exception then you should do this for no other reason than to maintain the method's contract. (Accessing the SQL server is an implementation detail that the caller is not/must not/cannot be aware of, so it cannot anticipate that a SqlException (or DbException for that matter) will be thrown.)
Applying this technique however has some implications that one should be aware of:
You are concealing the root cause of the error. In your example you are reporting to the caller that a business rule was invalid(?), violated(?) etc., when in fact there was a problem accessing the DB (which would be immediately clear if the DbException were allowed to bubble up the call stack further).
You are concealing the location where the error originally occurred. The StackTrace property of the caught exception will point to a catch-block far away from the location the error originally occurred. This can make debugging notoriously difficult unless you take
great care to log the stack traces of all the inner exceptions as well. (This is especially true once the software has been deployed into production and you have no means to attach a
debugger...)
Given that .NET does not let you alter the Message of an Exception to include additional information, what is the intended mechanism to catch original exceptions?
It is true that .NET does not allow you to alter the Message of an Exception. It provides another mechanism however to supply additional information to an Exception via the Exception.Data dictionary. So if all you want to do is add additional data to an exception, then there is no reason to wrap the original exception and throw a new one. Instead just do:
public void DoStuff(String filename)
{
try {
// Some file I/O here...
}
catch (IOException ex) {
// Add filename to the IOException
ex.Data.Add("Filename", filename);
// Send the exception along its way
throw;
}
}
As other peeps say, you cannot catch an the InnerException. A function such as this could help you get the InnerException out of the tree though:
public static bool TryFindInnerException<T>(Exception top, out T foundException) where T : Exception
{
if (top == null)
{
foundException = null;
return false;
}
Console.WriteLine(top.GetType());
if (typeof(T) == top.GetType())
{
foundException = (T)top;
return true;
}
return TryFindInnerException<T>(top.InnerException, out foundException);
}
I agree with the other comments that this is a code smell 🦨 and should be avoided. But if a refactor is not possible you could try something like this...
Create an extension method...
public static bool HasInnerException(this Exception ex, Func<Exception, bool> match)
{
if (ex.InnerException == null)
{
return false;
}
return match(ex.InnerException) || HasInnerException(ex.InnerException, match);
}
And use it like...
catch (Exception ex) when (ex.HasInnerException(e => e is MyExceptionThatIsHidden))
{
...
But really you should be solving for 👇
var exception = new Exception("wrapped exception 3",
new Exception("wrapped exception 2",
new Exception("wrapped exception 1",
new MyExceptionThatIsHidden("original exception")))); // <--- ???

ASP.net logging errors that occur on different pages

How would i go about to do this?
protected void Page_Load(object sender, EventArgs e)
{
try
{
doSomething();
}
catch (Exception ex)
{
// Here i would like to know that the page is "Problem.aspx"
// and that it was caused from the doSomething() function
}
}
private void doSomething()
{
logToSomething();
}
Exception object has a stack trace property, which tells you exactly where the error took place.
Also, check out Microsoft Enterprise Library (more specifically the Logging Block).
The logged errors provide a stack trace, among other things, letting you know exactly where the error occurred.
I'm using this little class to log errors, have a look on how i get the page and the function(Stacktrace):
public static class ErrorLog
{
public static void WriteError(Exception ex)
{
try {
string path = "~/error/logs/" + System.DateTime.Now.ToString("dd-MM-yyyy") + ".txt";
if ((!System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath(path)))) {
System.IO.File.Create(System.Web.HttpContext.Current.Server.MapPath(path)).Close();
}
using (System.IO.StreamWriter w = System.IO.File.AppendText(System.Web.HttpContext.Current.Server.MapPath(path))) {
w.WriteLine(System.Environment.NewLine + "Log Entry : {0}", System.DateTime.Now.ToString(System.Globalization.CultureInfo.InvariantCulture));
var page = VirtualPathUtility.GetFileName(HttpContext.Current.Request.Url.AbsolutePath);
w.WriteLine("Error in: " + page);
string message = "Message: " + ex.Message;
w.WriteLine(message);
string stack = "StackTrace: " + ex.StackTrace;
w.WriteLine(stack);
w.WriteLine("__________________________");
w.Flush();
w.Close();
}
} catch (Exception writeLogException) {
try {
WriteError(writeLogException);
} catch (Exception innerEx) {
//ignore
}
}
}
}
It's entirely sufficient for me.
Note: converted from VB.NET, hence untested
You can determine all of that by parsing the Exception message.
Look at your message and use a regex to extract the information you need.
Another option that you may want to look into is ELMAH ( Error Logging Modules and Handlers for ASP.NET ) http://code.google.com/p/elmah/ . I guess it really depends on what your specific needs are.
Use log4net, for logging the error messages. For help look at these article 1 and article 2.
Whatever logging method you use, do something like this.(Hand typed may not compile)
try
{
DoignStuff();
}
catch( Exception ex)
{
Trace.WriteLine( "Exception in <Page Name> while calling DoingStuff() Ex:"+ ex.ToString() );
}
It will start with the page name & method (which is redundant, but makes life easier)
then it will convert the EX to a string which shows call stack and all kinds fo other good stuff and put it in the log file
Note: you have to Type the name of the page in the place of <Page Name> .
Log4Net and Elmah are great to make life easier too.

Programmatically suppressing exceptions in C#

I have the following try-catch statement and I do not want to not throw the exception if the message property contains 'My error' in the text.
How can I programmatcially accomplish this? Also, would this be considered code-smell?
try
{
}
catch(Exception e)
{
if(e.Messages.Contains("My error"))
{
//want to display a friendly message and suppress the exception
}
else
{
throw e;
}
}
You shouldn't catch errors based on the error test. You should make your own exception class that extends exception:
class MyErrorException : Exception { }
and throw and catch those. (Excuse my syntax if it's wrong, I haven't done C# in a while).
That being said, throwing and catching your own Exceptions instead of propagating them is perfectly normal, and it is how you actually should do exception handling.
You should be catching the specific exception you're looking for. Quite frankly, that code is shocking. You should have something like ...
public class MyCoolException : Exception {
public MyCoolException(string msg) : base(msg) {}
}
public void MyCoolMethod() {
// if bad things happen
throw new MyCoolException("You did something wrong!");
}
Then later in your code you can use it like ...
try {
MyCoolMethod();
} catch (MyCoolException e) {
// do some stuff
}
Your code creates maintainability issues because a simple text change can have strange side effects. You can have your own exception class which inherits from System.Exception. Then instead of having an if you could do the following:
try
{
}
catch(MyException myException) //or just catch(MyException)
{
//display a friendly message
}
also you don't want to do throw e because it doesn't preserver the Stack, just throw; will do.
When I throw Exception rather than a derived class I always mean a failed assertion. I don't like failing out the backend because we are still able to receive a request (just not that one again). If we're really toast it will just error out on the next request anyway.
When the back end needs to generate an error message I have a ErrorMessage class that inherits from Exception and takes ErrorMessage and ErrorMessageTitle as constructor arguments.

Categories