6

I'm developing with C#, ASP.NET MVC Web Api, Entity Framework and .NET Framework 4.0.

I have this code to log an exception:

public void LogCompleteException(
    string controllerName,
    string actionName,
    Exception exception)
{
    string exceptionMessage = string.Empty;

    Exception e = exception;
    if (e.InnerException == null)
        e = null;
    else
        while (e.InnerException != null) e = e.InnerException;

    if (e == null)
        exceptionMessage = exception.Message;
    else
        exceptionMessage = string.Format("{0}\n\rInnerException: {1}", exception.Message, e.Message);

    _logger.ErrorFormat(
        LogTextFormatString,
        ExceptionText,
        controllerName,
        actionName,
        exceptionMessage);
}

But on my log file I have found this:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

When I wrote 'See 'EntityValidationErrors' property for more details.', I'm only showing an example where I haven't log an important property.

There are a lot of kind of exceptions; but with my method I'm not logging all the relevant information because there could be properties like 'EntityValidationErrors' that I don't log.

When I pass the exception to log it I don't know what properties it has, and I don't know how to log each property it has.

Do you know a method to log an exception completely? My code doesn't long exceptions with an EntityValidationErrors property or any other important property.

I'm doing the logging with log4net.

VansFannel
  • 45,055
  • 107
  • 359
  • 626
  • I assume the logging is done through log4net? – Brad C Apr 22 '15 at 12:25
  • Yes, I'm logging with log4net. – VansFannel Apr 22 '15 at 12:32
  • Why not to use exception.ToString()? – Giorgi Nakeuri Apr 22 '15 at 12:33
  • 1
    Have you seen [similar posts](http://stackoverflow.com/questions/15820505/dbentityvalidationexception-how-can-i-easily-tell-what-caused-the-error) on SO? Is it safe to assume you don't want to catch `DbEntityValidationException` exceptions and create complete error message yourself? – Michael Apr 22 '15 at 12:34
  • @Michael `DbEntityValidationException` exceptions it's only an example that I could get many kind of exceptions and don't any important information. – VansFannel Apr 22 '15 at 13:45

2 Answers2

3

Since the inner exception is an exception itself, perhaps you can just recurse and reuse the method you already have:

if (e.InnerException != null)
{
    LogCompleteException(controllerName, actionName, e.InnerException);
}

If this does work, however, you will still be missing the EntityValidationErrors.

Another method, which I used recently is to just explicitly trap and log the exception where it occurs:

try
{
    db.Add(something);
    db.SaveChanges();
}
catch (DbEntityValidationException ex)
{
    StringBuilder sb = new StringBuilder();

    // collect some extra info about the exception before logging
    foreach (var eve in ex.EntityValidationErrors)
    {
        sb.AppendLine(String.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State));
        foreach (var ve in eve.ValidationErrors)
        {
            sb.AppendLine(String.Format("Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage));
        }
    }

    logger.Error("There was an error while trying to parse and save something!\n" + sb.ToString(), ex);
}

If you want the Exception.Data dictionary entries, you can add those as well:

foreach (DictionaryEntry entry in ex.Data)
{
    // you will want to use a StringBuilder instead of concatenating strings if you do this
    exceptionMessage = exceptionMessage + string.Format("Exception.Data[{0}]: {1}", entry.Key, entry.Value);
}

As for properties for Custom exception classes just as the EntityValidationErrors, I would just trap and parse those where they occur. Otherwise you would have to override ToString() on every exception type or use some reflection hackery to enumerate all the properties which would significantly clutter the logs with properties you dont care about.

Brad C
  • 2,868
  • 22
  • 33
  • I think I haven't explained well in my question. When I wrote " See 'EntityValidationErrors' property for more details." I'm showing an example that I haven't log an important property. There are a lot of kind of exceptions and with my method I'm not logging in all of that information. – VansFannel Apr 22 '15 at 13:47
  • Can you give more examples of 'important properties'? A large majority of exceptions store this data in the [Exception.Data dictionary](https://msdn.microsoft.com/en-us/library/system.exception.data%28v=vs.110%29.aspx). There are outliers though (such as EntityValidationErrrors) and you would either need to use reflection to enumerate all the exception's properties or explicitly check them beforehand. The best thing for these outliers is to handle them when thrown, as needed, such as in my example code IMO. – Brad C Apr 22 '15 at 15:58
  • I can't give you more examples of 'import properties' because I don't know what kind of exception I would have to log. This is why I have asked about log an exception completely because I want to log any property with information. By the way, I log the most inner `IneerException` with this line `while (e.InnerException != null) e = e.InnerException;`. – VansFannel Apr 23 '15 at 06:13
0

you could use elmah and the log4net appender. Elmah logs catches all exceptions and can log them to a log4net instance.

Gelootn
  • 601
  • 6
  • 16