We are implementing our own custom errors for our custom actions to help with logging in our product. I have properly created a handful of errors that are put into the error table when the project is built, and I have successfully been able to log errors properly. My question is, I want to be able to attach the exception message (if any) to the custom error to help us get a better idea of what went wrong.
I know that I could log the events separately, that is, I could have a try/catch block and if an exception is caught, I could handle our custom error and right after, I could handle the exception message, like so;
try
{
//My Code
}
catch (Exception ex)
{
MsiLogging.HandleError(session, InstallerErrors.CError_IsUserAdmin);
MsiLogging.HandleError(session, ex.Message);
return false;
}
Then, my logging class would handle it like this
public class MsiLogging
{
//Placeholder for errors without a custom error message - default error message will appear
public static void HandleError(Session session)
{
HandleError(session, 25000);
}
public static void HandleError(Session session, int error)
{
Record record = new Record(2);
record[1] = error;
session.Message(InstallMessage.Error, record);
}
public static void HandleError(Session session, string message)
{
Record record = new Record(2);
record.FormatString = message;
session.Message(InstallMessage.Error, record);
}
}
That would generate two log entries, one with our custom error (which we pretty much use to identify the method in which the error occured and helps our customers IT admins who communicate with us give us better information when error occurs), and one with the exception message if one exists.
Is there a way to append our custom error message with the exception message directly and create only one log entry? This isn't super important, but it's just one of those things I'd really like to make happen. I have tried using the GetString() and SetString() methods for the Record object, but other than crashing my installer I've only been able to over write the custom error message instead of append to it. Any ideas?
I don't know exactly what you have in your Error table, but I suspect that a template is what you need. You'd have something like Error [1] Additional Info [2] and then you should be able to use the equivalent of MsiFormatRecord to get all the info into one record. This is what you'd do with C++, and I assume that the (DTF?) library exposes the equivalent to create one message out of multiple pieces of data.
I was able to get it to work by implementing a new MSI property, EXCEPTIONTEXT. I appended my custom errors to include this property, which by default would just read "No Exception". Then, I simply added another HandleError method:
public static void HandleError(Session session, int error, string message)
{
Record record = new Record(2);
session["EXCEPTIONTEXT"] = message;
session.Message(InstallMessage.Error, record);
}
By setting the exception text property before logging the error, I was able to output the log. Kudos to PhilDW for pointing me in the right direction.
Related
In my quest to implement best custom error handling practices, I came up with an idea to not use try catch any where in my code. Instead, I have decided to use customErrors mode="On" and redirect to error page and show exception detail in this page.
//My test code from which error will come
public ActionResult Index()
{
AAA aa = null;
aa.a = "a";
}
//My web.config file
<customErrors mode="On" defaultRedirect="~/Errors/Error.aspx">
<error statusCode="404" redirect="~/Errors/404.html" />
</customErrors>
//My error handling page(Error.aspx):
protected void Page_Load(object sender, EventArgs e)
{
Exception error;
error = Server.GetLastError();
}
I believe I should get error message in error in my error handling page. But I always get null.
How do I get the exception message in error handling page?
Let me shed some light in how I generally handle exceptions in the projects I work on. But let's break down into sections.
Error pages
The error pages should not show the real exception when on Production. The user has no need to know that the DB had a failure, which could expose your system to security issues. A page with a generic error or a well documented error code would do the job.
But, of course, on your dev environment it's ok to show exceptions. I'd suggest to use customErrors mode="RemoteOnly" in this case.
Error code
Depending on the system you are developing it would be important to have an error code with the message. For example, the user could see "Unable to connect (XYZ_1234)" or "Unable to connect (ABC_9876)" - same message, different codes - and send it to the support team. If the support team has a document matching the codes with the real exceptions they will be able to send a proper report to the devs.
Try/Catch blocks
Try/Catch is your best friend when it comes to exception. Especially because it will help you to customize the exception if necessary. You could have a series of custom exception classes - each with its own characteristic - that would help you to know the problem even before debugging. One simple example:
public class ExceptionWithCode : Exception
{
public ExceptionWithCode(string code, string message) : base(message)
{
this.Code = code;
}
public string Code { get; }
}
In the code you should approach it in more or less this way:
try
{
// Do whatever database operation here
}
catch (SqlException ex)
{
// Log the exception
_logService.Log(ex);
// Throw something else to the user
throw new ExceptionWithCode("XYZ_1234", "Unable to connect");
}
catch (Exception ex)
{
// Log the exception
_logService.Log(ex);
// Throw something else to the user
throw new ExceptionWithCode("ABC_9876", "Unable to connect");
}
Notice that I am using 2 catches. The first is because I know this exception may happen, since I am connecting to the DB, the second is in case any other thing may happen. Besides, the user doesn't know the real exception as he/she is getting just a random exception with code instead of a db connection failure.
Logs
That's a very important part. Remember: You should never show the real exceptions to the user. Instead, log them in a place where you can easily access. That could be in a file in the server, the database or even in the Windows Event Logs. You don't necessarily need to write your own logging tool, you can use anything available on the internet. My favorite is SeriLog, since I log most of my events/exceptions in text files. But I've used ELMAH for quite some time with .NET Framework and it was pretty good for XML formatted logs.
That works for me because:
User is informed of the problem and can communicate with the support
I am not tipping off any intruders regarding the flaws of my system (at least not clearly)
I know what kind of exception the user saw thanks to the error code he gave me
There are logs to be analyzed whenever I need
I am handling errors using IErrorHandler in WCF REST. I need to log class name, method name, line number, error code etc. and i find out the soultions to all this.
But I am thinking any possibility or options to get 1ErrorLevel1 like windows event viewer. Eg: Warning, Information, Error.
Actually if I do like this my applciation exceptions can be put as Information. This can be work simple create an custom Exception class.
But my question is for other exceptions (other than my application exceptions) any option to get this level based on the error. I don't need to set this level for other types of erros.
Actually i am logging the exception in HandleError event. Now i am just throw my exceptions as applicationexception.
Just see my Service method code below:
if(!long.TryParse("534543534", out value))
{
throw new ApplicationException("Input is Not Valid!!!");
}
So in my error handling i got this as application exception. Here i dont knwo this is because of FormatException, Overflow Exception, ArgumentNullException. How i handle this. I can add the original exception as innerexception but need to wrap try..catch in each service method to get the exception. Is this good practice when i have the IErrorHandler methods. Please suggest good approach for this
In .NET, an exception is a severe error that interrupts the process of your application. The framework does (for the most part) not provide an error level as all exceptions are basically errors. If an exception is thrown, this always means that there is an error that the application cannot continue with.
If you want to discern between the exception types and write some as informational messages or warnings to your log, you'd have to inspect the exceptions in your error handler, e.g. by code similar to this:
public class MyErrorHandler : IErrorHandler
{
// ...
private static readonly Dictionary<Type, TraceLevel> _exceptionTraceLevelMappings;
static MyErrorHandler()
{
_exceptionTraceLevelMappings = new Dictionary<Type, TraceLevel>();
_exceptionTraceLevelMappings.Add(typeof(ApplicationException), TraceLevel.Information);
_exceptionTraceLevelMappings.Add(typeof(ArgumentException), TraceLevel.Warning);
}
private static TraceLevel GetLevelByExceptionType(Type exType)
{
// You might want to add a more sophisticated approach here (e.g. for base classes)
if (_exceptionTraceLevelMappings.ContainsKey(exType))
return _exceptionTraceLevelMappings[exType];
return TraceLevel.Error;
}
// ...
}
Based upon the comments, you want to discern between errors that are raised to coding mistakes and input data validation errors. In this case, you'd need to implement a CustomException type and use TryParse to validate input data:
public class MyValidationException : Exception
{
public MyValidationException(string message)
: base(message)
{
}
// A custom exceptions needs several constructors, so add them also
}
In your service code, you'd use TryParse as in your sample:
if(!long.TryParse("534543534", out value))
{
throw new MyValidationException("Input is Not Valid!!!");
}
In the mapping dictionary, you can register your exception type and assign TraceLevel.Information:
// ...
_exceptionTraceLevelMappings.Add(typeof(MyValidationException), TraceLevel.Information);
// ...
I have a web service that's pretty simple; something like this:
public class LeadService : System.Web.Services.WebService {
[WebMethod(EnableSession = true)]
public string MyService(string TheIncomingData)
{
string ReturnData = "";
MyClass TheClass = new MyClass();
ReturnData = TheClass.MyMethod(TheIncomingData);
return ReturnData;
}
}
You might have guessed it, the MyMethod is a pretty long-running method with some room for errors (for now). If I add a try/catch statement around the method call like this:
try { ReturnData = TheClass.MyMethod(TheIncomingData); }
catch { ReturnData = ""; }
Is this going to make the service and the app exception-proof? And, is using a try statement like this going to have any performance impact even if no error occurs?
Thanks for your advice.
Is using a try statement like this going to have any performance impact even if no error occurs?
No.
Is the application exception-proof?
Yes. However if you have onApplicationError event in the Global.asax you won't be able to see the error since you are not throwing a new one.
But the way I see it for now, your code is safe from exceptions.
Is this going to make the service and the app exception-proof?
Yes, it seems that's the only place where exception can raise. So you will catch every exception using this try catch
And, is using a try statement like this going to have any performance impact even if no error occurs?
No, it won't cause any performance issue in case of no error
On the side note it is not a good practise to just ignore the exception like this. May be you can return a customized error to user so that user can do something instead of just wondering why I am getting back a empty string
That's a way to make the app "exception-proof", yes, at least when seen by a client. But it won't make it error-proof, and it will make it harder to find an error whenever your client gets an empty string.
If you are willing to accept the risk of swallowing exceptions and potentially leaving your application in an undefined state, you could at least return the exception info as the result so that someone can understand what really happened. If there was an unanticipated input, you must inform your clients and log the error for yourself.
Did your client enter invalid input data? Do you know which data it was, and how to repeat the problem?
Did it happen with valid data? Where and how did it happen?
A much better solution would be to add the Application_Error handler method in your Global.ashx file, log the exception (possibly notifying the admin by e-mail when this happens), and use a custom error page for your users.
This article explains it well: Global Error Handling in ASP.NET.
Is it exception proof? Yes. Will it hurt performance? Not as far as the execution of this code block is concerned. However, I suggest you rewrite the code so that it allows you to handle the exception and get the desired result:
public class LeadService : System.Web.Services.WebService {
[WebMethod(EnableSession = true)]
public string MyService(string TheIncomingData)
{
MyClass TheClass = new MyClass();
try{
return TheClass.MyMethod(TheIncomingData);
}
catch(Exception ex){
//handle your exception, log, etc.
}
return "";
}
}
I have a UI application, that accesses a database and must also be able to perform various actions on files. Needless to say various/numerous exceptions could be thrown during the course of the application, such as:
The database is offline.
The file (previously scanned into a database), is not found.
The file is locked by another user/process and cannot be accessed.
The file's read-only attribute is set and the file cannot be modified.
Security permissions deny access to the file (read or write).
The precise details of the error is known at the point where the exception is raised. However, sometimes you need to let the exception be caught higher up the call stack to include context with the exception, so that you can create and present a user friendly error message; e.g. a file being locked by another process could be encountered during a file copy, file move or file delete operation.
Let's say for discussion purposes we have a single method that must perform various actions on a file; it must read a file into memory, modify the data and write the data back out as in the below example:
private void ProcessFile(string fileName)
{
try
{
string fileData = ReadData(fileName);
string modifiedData = ModifyData(fileData);
WriteData(fileName, modifiedData);
}
catch (UnauthorizedAccessException ex)
{
// The caller does not have the required permission.
}
catch (PathTooLongException ex)
{
// The specified path, file name, or both exceed the system-defined maximum length.
// For example, on Windows-based platforms, paths must be less than 248 characters
// and file names must be less than 260 characters.
}
catch (ArgumentException ex)
{
// One or more paths are zero-length strings, contain only white space, or contain
// one or more invalid characters as defined by InvalidPathChars.
// Or path is prefixed with, or contains only a colon character (:).
}
catch (NotSupportedException ex)
{
// File name is in an invalid format.
// E.g. path contains a colon character (:) that is not part of a drive label ("C:\").
}
catch (DirectoryNotFoundException ex)
{
// The path specified is invalid. For example, it is on an unmapped drive.
}
catch (FileNotFoundException ex)
{
// File was not found
}
catch (IOException ex)
{
// Various other IO errors, including network name is not known.
}
catch (Exception ex)
{
// Catch all for unknown/unexpected exceptions
}
}
When logging and presenting error messages to the user we want to be as descriptive as possible as to what went wrong along with any possible recommendations that could lead to a resolution. If a file is locked, we should be able to inform the user of such, so that he could retry later when the file is released.
In the above example, with all the exception catch clauses, we would still not know which action (context) lead to the exception. Did the exception occur while opening and reading the file, when modifying the data or when writing the changes back out to the file system?
One approach would be to move the try/catch block to within each of the these "action" methods. This would mean copying/repeating the same exception handling logic into all three methods. And of course to avoid repeating the same logic in multiple methods we could encapsulate the exception handling into another common method, which would call for catching the generic System.Exception and passing it on.
Another approach would be to add an "enum" or other means of defining the context, so that we know where the exception occurred as follows:
public enum ActionPerformed
{
Unknown,
ReadData,
ModifyData,
WriteData,
...
}
private void ProcessFile(string fileName)
{
ActionPerformed action;
try
{
action = ActionPerformed.ReadData;
string fileData = ReadData(fileName);
action = ActionPerformed.ModifyData;
string modifiedData = ModifyData(fileData);
action = ActionPerformed.WriteData;
WriteData(fileName, modifiedData);
}
catch (...)
{
...
}
}
Now, within each catch clause, we would know the context of the action being performed when the exception was raised.
Is there a recommended approach in addressing this problem of identifying context related to an exception? The answer to this problem maybe subjective, but if there is a design pattern or recommended approach to this, I would like to follow it.
When you create the exception, set it's Message property to something descriptive before throwing it. Then higher up you can just display this Message to the user.
We normally log the exception (with either log4net or nlog) and then throw a custom exception with a friendly message that the user can understand.
My opinion is that the MS approach to localize the message property of exceptions is all wrong. Since there are language packs for the .NET framework you get from a Chinese installation cryptic (e.g. Mandarin) messages back. How am I supposed to debug this as a developer who is not a native speaker of the deployed product language?
I would reserve the exception message property for the technical developer oriented message text and add a user message in its Data property.
Every layer of your application can add a user message from its own perspective to the current thrown exception.
If you assume the that the first exception knows exactly what did go wrong and how it could be fixed you should display the first added user message to the user. All architectural layers above will have less context and knowledge about the specific error from a lower layer. This will result in less helpful error messages for the user. It is therefore best to create the user message in the layer where you have still enough context to be able to tell the user what did go wrong and if and how it can be fixed.
To illustrate the point assume a software where you have a login form, a web service, a backend and a database where the user credentials are stored. Where would you create the user message when a database problem is detected?
Login Form
Web Service
Backend
Database Access Layer
IResult res = WebService.LoginUser("user", "pwd");
IResult res = RemoteObject.LoginUser("user","pwd");
string pwd = QueryPasswordForUser("user");
User user = NHibernate.Session.Get("user"); -> SQLException is thrown
The Database throws a SQLException because the db it is in maintainance mode.
In this case the backend (3) does still have enough context to deal with DB problems but it does also know that a user tried to log in.
The UI will get via the web service a different the exception object because type identity cannot be preserved accross AppDomain/Process boundaries. The deeper reason is that the remote client does not have NHibernate and SQL server installed which makes it impossible to transfer the exception stack via serialization.
You have to convert the exception stack into a more generic exception which is part of the web service data contract which results in information loss at the Web Service boundary.
If you try at the highest level, the UI, try to map all possible system errors to a meaningful user message you bind your UI logic to the inner workings in your backends. This is not only a bad practice it is also hard to do because you will be missing context needed for useful user messages.
catch(SqlException ex)
{
if( ex.ErrorCode == DB.IsInMaintananceMode )
Display("Database ??? on server ??? is beeing maintained. Please wait a little longer or contact your administrator for server ????");
....
Due to the web service boundary it will be in reality more something like
catch(Exception ex)
{
Excepton first = GetFirstException(ex);
RemoteExcepton rex = first as RemoteExcepton;
if( rex.OriginalType == "SQLException" )
{
if( rex.Properties["Data"] == "DB.IsMaintainanceMode" )
{
Display("Database ??? on server ??? is beeing maintained. Please wait a little longer or contact your administrator for server ????");
Since the exception will be wrapped by other exceptions from other layers you are coding in the UI layer against the internals of your backend.
On the other hand if you do it at the backend layer you know what your host name is, you know which database you did try to access. Things become much easier when you do it at the right level.
catch(SQLException ex)
{
ex.Data["UserMessage"] = MapSqlErrorToString(ex.ErrorCode, CurrentHostName, Session.Database)'
throw;
}
As a general rule you should be adding your user messages to the exception in the deepest layer where you still know what the user tried to do.
Yours,
Alois Kraus
You should throw different exception types if possible from each method that can. For example, your ModifyData method could internally catch shared exception types and rethrow them if you are worried about .NET exception collision.
You could create your own exception class and throw it back up the user from your catch block and put the message into your new exception class.
catch (NotSupportedException ex)
{
YourCustomExceptionClass exception = new YourCustomExceptionClass(ex.message);
throw exception;
}
You can save as much info as you want into your exception class and that way the user has all the information and only the information that you want them to have.
EDIT:
In fact, you could make an Exception member in your Custom Exception class and do this.
catch (NotSupportedException ex)
{
YourCustomExceptionClass exception = new YourCustomExceptionClass(ex.message);
exception.yourExceptionMemberofTypeException = ex;
throw exception;
}
This way, you can give the user a nice message, but also give them the underlying inner exception. .NET does this all the time with InnerException.
i know this could be a little weird but a doubt is a doubt afterall...
what would happen in the following situation...
private void SendMail()
{
try
{
//i try to send a mail and it throws an exception
}
catch(Exception ex)
{
//so i will handle that exception over here
//and since an exception occurred while sending a mail
//i will log an event with the eventlog
//All i want to know is what if an exception occurs here
//while writing the error log, how should i handle it??
}
}
Thank you.
I would personally wrap the call to write to event log with another try\catch statement.
However, ultimately it depends on what your specification is. If it is critical to the system that the failure is written to the event log then you should allow it to be thrown. However, based on your example, I doubt this is what you want to do.
You can simply catch errors in the error logging method. However I wouldn't personally do that, as broken error logging is a sign your application can't function at all.
private void SendMail()
{
try
{
//i try to send a mail and it throws an exception
}
catch(Exception ex)
{
WriteToLog();
}
}
private void WriteToLog()
{
try
{
// Write to the Log
}
catch(Exception ex)
{
// Error Will Robinson
// You should probably make this error catching specialized instead of pokeman error handling
}
}
Each exception is caught only when inside a try-catch block. You could nest try-catch but is generally not a good idea.
You could add a try-catch block in your catch block as well.
Considering the kind of exceptions when writing to a file (rights, disk space...) I would advice not to handle it in here. If it fails the first time, there's good chance you won't be able to write to the event log that it's not possible to write in the event log...
Let it bubble up and be handled by an upper level try/catch.
Chris S. has the best answer. Placing a try-catch block inside a catch block is very rarely a good idea. and in your case it will just convolute your code. If you check to see if you were successful in writing to your log file here, you will have to do it in every place where you try to write into your log file. You can easily avoid this unnecessary code duplication by having all your individual modules be self contained when it comes to notifying/handling of error conditions within these modules. When sending your mail fails you perform the proper actions inside your catch block to handle this exceptional condition like:
disposing of the contents of your mail object
making sure your socket is closed
writing an entry into your log file to note the error
Inside your catch block just call whatever API you have defined to writing a log entry into your logfile and forget about about the rest. Inside your logging API is where you should handle any logging related exceptional cases (the disk is full, no permission to write to file, file not found, etc...). Your mailing module does not need to know if the logging was successful or not, that responsibility should be delegated to the logging module.
I personally handle this situation using a simple extension method.
public static class MyExtentions
{
public static void LogToErrorFile(this Exception exception)
{
try
{
System.IO.File.AppendAllText(System.IO.Path.Combine(Application.StartupPath, "error_log.txt"),
String.Format("{0}\tProgram Error: {1}\n", DateTime.Now, exception.ToString()));
}
catch
{
// Handle however you wish
}
}
}
The usage is simple:
try
{
...
}
catch(Exception ex)
{
ex.LogToErrorFile();
}
You can then handle the caught exception inside the extension method however you want, or simply don't catch it and let it bubble up to the top. I've found this design to be a simple, reproducible way to handle exceptions throughout the application.
Firstly I would say don't catch "Exception" in catch block. You could instead, for mailing, check for all validity and then catch specific exception(SmtpException, ) that you can do something about(and informing user with a friendly message). Throwing exception from your code and informing the UI about is not a bad idea. If your methods accepts inputs with certain specification and if they are not met, your method should/can throw error and inform user about it.
For exceptions that have no control over, use global handling exception, like Application_Error for web.
Getting Better Information on Unhandled Exceptions Peter Bromberg explains this better.
Also for any privildged resource you are accessing, like eventlogs, make sure you assembly has access to it.
Useful links Build a Really Useful ASP.NET Exception Engine By Peter A. Bromberg
and
Documenting Exceptional Developers By Peter A. Bromberg
For web application look into
Health monitoring
Exception logging
One more thing, if your application goes wrong/ throws error that can't handle( at all) its better to let it go down gracefully and not continue. Application in unstable state is not good idea.