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());
Related
I have created a Blob Trigger Azure Function, and I wanted to be able to create mail alerts when I have an exception, and among the information sent in the mail alert, there should be the name of the file the exception occurred on.
I noticed that exceptions are automatically logged in Azure, but I found no way of customizing the message or the information sent along the exception. So I decided to inject a telemetry service in my Function App, and add the file name as a custom property, as you can see in the code below :
public class Function1
{
private readonly IGremlinService _gremlinService;
private readonly TelemetryClient _telemetryClient;
public Function1(IGremlinService gremlinService, TelemetryConfiguration telemetryConfiguration)
{
this._gremlinService = gremlinService;
this._telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName(nameof(Function1))]
public async Task Run([BlobTrigger("files/{directory}/{name}.00.pdf", Connection = "AzureWebJobsStorage")] Stream myBlob, string name, ILogger logger)
{
try
{
//some code not related to the issue
}
catch (Exception e)
{
var properties = new Dictionary<string, string>
{{"Filename", name}};
_telemetryClient.TrackException(e, properties);
if (e is ResponseException)
{
ResponseException re = (ResponseException) e;
var statusCode = (long) re.StatusAttributes["x-ms-status-code"];
_telemetryClient.TrackTrace("Error on file " + name + ". Status code: " + statusCode + " " + re.StackTrace, SeverityLevel.Error, properties);
}
else
{
_telemetryClient.TrackTrace("Error on file " + name, SeverityLevel.Error, properties);
}
throw;
}
}
}
}
But I still cannot customize the message to provide the user with additional information. I know I can send alerts on trace messages instead, and send customized messages this way, and this is currently what I'm doing, but I would find it cleaner to send alert on exceptions.
My second issue is that my exceptions are still logged automatically on top of being logged by the telemetry service, and for some reason I can't understand, they are logged twice, as you can see in the screenshot below from Application Insights :
Is there a way I can turn off the automatic logging of exceptions ? Or is there a way to customize these exceptions messages that I'm not aware of, instead of using the telemetry service ?
I believe, the 3 exceptions are being logged due to following reasons:
The implementation service of IGremlinService throwing exception which is being logged.
You are logging via _telemetryClient.TrackException(e, properties);
Azure infrastructure is handling when throw is invoked.
Now coming to your question
I found no way of customizing the message or the information sent along the exception
I would suggest you to use ILogger for LogException and use BeginScope (read here) to define the scope properties which will be logged as Custom Properties in application insights for all the logs which are invoked during the lifetime of created scope.
Using the ILogger object, your code will be simplified as follows and all traces and exceptions inside scope will have FileName as custom property in application insights.
[FunctionName(nameof(Function1))]
public async Task Run([BlobTrigger("files/{directory}/{name}.00.pdf", Connection = "AzureWebJobsStorage")] Stream myBlob, string name, ILogger logger)
{
using (logger.BeginScope(new Dictionary<string, object>()
{
["FileName"] = name,
}))
{
try
{
//some code not related to the issue
}
catch (Exception e)
{
logger.LogError(e, "Error occurred with {StatusCode}", (long) re.StatusAttributes["x-ms-status-code"]);
throw;
}
}
}
Following summarizes the definition of statement logger.LogException(e, "Error occurred with {StatusCode}", (long) re.StatusAttributes["x-ms-status-code"]);
e represents the actual exception which occurred.
the code part {StatusCode} will be logged the StatusCode as Custom Property in application insights for the logged exception so you don't need to create any dictionary.
FileName will be logged as Custom property as defined by Scope.
You can view a sample implementation at here.
In System.IO there is a function:
string File.ReadAllText( string path );
I am trying to write a function that would call File.ReadAllText, take care of all possible exceptions and return true/false and store error message.
What I have is this:
public static class FileNoBS
{
public static bool ReadAllText( string path, out string text, out string errorMessage )
{
errorMessage = null;
text = null;
bool operationSuccessful = false;
try
{
text = System.IO.File.ReadAllText( path );
operationSuccessful = true;
}
catch ( ArgumentNullException e )
{
errorMessage = "Internal software error - argument null exception in FileNoBs.ReadAllText\nMessage: " + e.Message;
}
catch ( ArgumentException e )
{
errorMessage = "Internal software error - path is a zero-length string, contains only white space, or contains one or more invalid characters as defined by InvalidPathChars in FileNoBs.ReadAllText.\nMessage: " + e.Message;
}
catch ( PathTooLongException e )
{
errorMessage = "The specified path was too long.\nMessage: " + e.Message;
}
catch ( DirectoryNotFoundException e )
{
errorMessage = "The specified directory was not found.\nMessage: " + e.Message;
}
catch ( FileNotFoundException e )
{
errorMessage = "The file specified in path was not found.\nMessage: " + e.Message;
}
catch ( IOException e )
{
errorMessage = "An I/O error occurred while opening the file.\nMessage: " + e.Message;
}
catch ( UnauthorizedAccessException e )
{
errorMessage = #"UnauthorizedAccessException
path specified a file that is read-only.
-or-
This operation is not supported on the current platform.
-or-
path specified a directory.
-or-
The caller does not have the required permission.\nMessage: " + e.Message;
}
catch ( NotSupportedException e )
{
errorMessage = "path is in an invalid format.\nMessage: " + e.Message;
}
catch ( SecurityException e )
{
errorMessage = "You do not have the required permission.\nMessage: " + e.Message;
}
return operationSuccessful;
}
}
I don't understand how how control flow goes with functions that return value.
Let's say UnauthorizedAccessException gets caught, errorMessage is set to
errorMessage = "You do not have the required permission..."
I know that finally gets executed every time, but compiler won't let me do return inside finally block. So will my return get reached or not?
Another question is how to simplify this while still following official guidelines:
"In general, you should only catch those exceptions that you know how to recover from. "
I dread going through all functions that I will need from File class (Move, Copy, Delete, ReadAllText, WriteAllText) and then Directory class and doing all these long blocks of code just to catch all exceptions I don't care about and not catch too many of them cause Microsoft says it's bad.
Thank you.
EDIT: I got comments like this is not handling exceptions this is "something else".
I am client for my code and I want to do something like this:
if ( !FileNoBS.ReadAllText( path, text, errorMessage ) ) {
MessageBox.Show( errorMessage );
return;
}
// continue working with all errors taken care of - don't care for whatever reason file wasn't opened and read, user is notified and I am moving on with my life
Your return will be reached as there isn't a return in the try block or the catch block.
Generally, you only want to catch exceptions that you expect may occur and have a way of handling them. For example, you may want to handle the file not being found from the given path and return a default file instead. You should allow other exceptions not to be caught so you know that something unexpected has happened and not hide it by catching all exceptions.
As I said in my comment, you are better off handling the exceptions at a higher level and simply displaying the exception message rather than manually setting each message. I think in this case the message from the exception will be descriptive enough.
public static class FileNoBS
{
public static string ReadAllText(string path)
{
return System.IO.File.ReadAllText( path );
}
}
then use it like this at some higher level in your application. I typically have a general handler to handle all application exceptions and log them and display a message box if necessary.
try
{
var text = FileNoBS.ReadAllText("file.ext");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Instead of catching the exceptions you should try to avoid the situation that will lead to those exceptions being thrown in the first place. In your case you should have some input validation before calling ReadAllText
never accept a path that is null - you know this will lead to an exception so handle it before it does
never accept a path that leads to a file that does not exist - use File.Exists(path) prior to the call
never accept a malformed path E.g. the empty string or one with invalid characters - this will lead to an exception
These tests should be performed where the input originates. That is if the user types them in, validate them before using them. If they come from a DB or somewhere else validate there before use. If it's not user input they are all indications of a system error and should be treated as such, not as something the user should worry about.
Security exceptions can be somewhat harder to test up front and in many cases it is exceptional to get a violation and therefor perfectly ok to get an exception. It shouldn't crash the program of course but be handled with an errormessage to the user (if it's based on user input, if it's system generated data that leads to this, it's an idication of a system error that should be fixed at code level). It's often more appropriate to do this where the call happens than in some library method.
for IOExceptions they can be put into two buckets. Recoverable once (usually a retry) and unrecoverable once. As a minimum give the user feedback on the exception, so the user might have the option of retrying.
A very general rule that should be part of the error correction logic is to never have invalid data floating around the system. Make sure that all objects manage the invariants (Tools are available for this such as code contracts). Reject invalid input from the user (or other systems) when they are received instead of when they result in an exception.
If you do all the input validation and still have E.g. ArgumentNullException then that points to an error in the logic of the program, something that you want to be able to easily find in a test and correct before you release the bug. You shouldn't try and mask this error.
Provided no other error occurs, yes.
I'd add at the end:
catch (Exception e)
{
errormessage = "An unexpected error has occured.";
}
return operationSuccessful;
Though, this will always return the successful even if you got an error. I'm not sure if that's what you want, or if your variables are badly named.
The return statement is going to be called in case of any exception in your code, before it is placed at the end of the program before it exits.
I will suggest placing a single exception handler with a high level Exception type, like the 'Exception' type itself, and print or log the exception message. Specifying so many exception handlers in each method is going to take a lot of energy which your should actually put in the method itself.
try
{
return ReadAllText("path", "text", "error");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
return false;
So if the method gets called, it will return immediately, otherwise the exception gets printed/logged and the method will return false.
You can however, mention a couple or few explicit exception handlers in some cases, where you think it will be beneficial.
Yes It will return the value.
But, better you handle return value in finally statement.
If in any case you want to return operationSuccessful value, then write finally block after catch blocks as follows,
finally
{
return operationSuccessful;
}
I have setup so that if an Exception is thrown I can display it with my custom error page. But in some cases I don't want to be navigated to the error page, but want it to display a simple dialog window.
public ActionResult Page1()
{
//The custom error page shows the exception, if one was thrown
throw new Exception( "An exception was thrown" );
return View();
}
public ActionResult Page2()
{
//A dialog should show the exception, if one was thrown
try
{
throw new Exception( "An exception was thrown" );
}
catch( Exception ex )
{
ViewData["exception"] = ex;
}
return View();
}
Is it possible to have a CustomAttribute to handle an exception which has been thrown in an Controller action? If I added CatchException to Page2, can I automate the process of storing the exception in the ViewData, each time an exception was thrown. I don't have much experience of CustomAttributes and I'd be much appreciated if you could help me.
The Page2 example works perfectly fine, I just want to make the code cleaner as it isn't really pretty to have try catches in every action (where I want to show a dialog).
I am using .NET MVC 4.
You can create a base controller that catch the exceptions and handle it for you.
Also, looks like the Controllers already have a mechanism to do that for you. You'll have to override the OnException method inside the controller. You can get a good example here:
Handling exception in ASP.NET MVC
Also, there's another answer on how to use the OnException here:
Using the OnException
By using that, your code will be cleaner, since you will not be doing a lot of try/catch blocks.
You'll have to filter the exception you wanna handle. Like this:
protected override void OnException(ExceptionContext contextFilter)
{
// Here you test if the exception is what you are expecting
if (contextFilter.Exception is YourExpectedException)
{
// Switch to an error view
...
}
//Also, if you want to handle the exception based on the action called, you can do this:
string actionName = contextFilter.RouteData.Values["action"];
//If you also want the controller name (not needed in this case, but adding for knowledge)
string controllerName = contextFilter.RouteData.Values["controller"];
string[] actionsToHandle = {"ActionA", "ActionB", "ActionC" };
if (actionsTohandle.Contains(actionName))
{
//Do your handling.
}
//Otherwise, let the base OnException method handle it.
base.OnException(contextFilter);
}
You can create subclass of Exception class, and catch it in your Page 2
internal class DialogException : Exception
{}
public ActionResult Page2()
{
//This should a dialog if an exception was thrown
try
{
//throw new Exception( "An exception was thrown, redirect" );
throw new DialogException( "An exception was thrown, show dialog" );
}
catch( DialogException ex )
{
ViewData["exception"] = ex;
}
return View();
}
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);
}
}
}
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.