如何记录异常信息以便进行故障排除?

3

我目前在对Windows服务进行维护,代码中的某些部分会涉及异常处理(例如,在计时器回调和其他外部事件中):

try {
  ...
}
catch (Exception ex) {
   _logger.Log("Unhandled exception: {0}\r\n{1}", ex.Message, ex.StackTrace);
   Environment.FailFast("Fatal error.");
}

记录异常信息有助于排查出错原因。然而,有时候解决问题的关键在于内部异常信息,这会使得确定问题的根本原因变得困难。例如,TypeInitializationException 就很难理解。

是否有更好的方法来记录异常信息以协助排查问题呢?


1
尝试一下“自问自答”的东西。目的真正是为了教育,因为我在许多代码库中都看到过这种相对低劣的日志记录风格。 - Martin Liversage
3个回答

7

有没有更好的记录异常信息以进行故障排除的方法?

是的,有更好的方法。不要聪明地使用ex.Messageex.StackTrace。只需使用ex.ToString()。它将递归进入内部异常(如果需要,可以多级)并显示完整的堆栈跟踪。

try {
  ...
}
catch (Exception ex) {
   _logger.Log("Unhandled exception:\r\n{0}", ex);
   Environment.FailFast("Fatal error.");
}

为了举个小例子,如果您创建一个在类的静态构造函数中抛出异常的实例,会得到以下结果。此异常将被包装在TypeInitializationException中。
之前:
未处理异常:类型“SomeClass”的类型初始化程序引发了异常。
   在 SomeNamespace.SomeClass..ctor() 中
   在 c:\ExceptionHandlingDemo.cs 的 SomeNamespace.Callback() 中的第34行。
不是很有帮助。很难确定出问题的原因。
之后:
未处理异常:
System.TypeInitializationException: 类型“SomeClass”的类型初始化程序引发了异常。 ---> System.ArgumentException: 已经添加具有相同键的项。
   在 System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   在 System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   在 System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   在 SomeNamespace.SomeClass..cctor() 中的 c:\ExceptionHandlingDemo.cs 的第43行。
   --- 内部异常堆栈跟踪的结尾 ---
   在 SomeNamespace.SomeClass..ctor()
   在 c:\ExceptionHandlingDemo.cs 的 SomeNamespace.Callback() 中的第34行。
现在,您可以很容易地确定问题的根本原因是字典中存在重复的键,并将其定位到源文件的第43行。

1
此外,某些异常类型的 ToString() 包括额外的细节,而 Message 不包括(我想一些 FileLoadException 等异常类型包含更多关于失败的详细信息,但我现在不确定)。 - Christian.K

0

我不知道这是否有帮助或者是否太高级了,但您是否知道微软的企业库?(http://msdn.microsoft.com/en-us/library/ff648951.aspx

其中有一个日志“应用程序块”,它是一种非常强大/灵活的工具。经过设置(全部由配置驱动),我已经将其作为我构建的所有内容的标准。

特别是在异常处理方面,我认为我几乎不需要做任何事情就可以获得有意义的信息。


0
我不确定这是否有帮助。我编写了这个实用程序类来记录异常的所有信息,我使用Exception.DataException.Message来记录信息。
这里分享一些东西:https://stackoverflow.com/a/15005319/1060656
public class ExceptionInfoUtil
{
    public static string GetAllExceptionInfo(Exception ex)
    {
        StringBuilder sbexception = new StringBuilder();

        int i = 1;
        sbexception.Append(GetExceptionInfo(ex, i));

        while (ex.InnerException != null)
        {
            i++;
            ex = ex.InnerException;
            sbexception.Append(GetExceptionInfo(ex, i));
        }

        return sbexception.ToString();
    }

    private static string GetExceptionInfo(Exception ex, int count)
    {
        StringBuilder sbexception = new StringBuilder();
        sbexception.AppendLine(string.Format(""));
        sbexception.AppendLine(string.Format(""));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format(" Inner Exception : No.{0} ", count));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format("=================================================="));
        sbexception.AppendLine(string.Format(" Error Message : {0} ", ex.Message));
        sbexception.AppendLine(string.Format("=================================================="));
        #region Mine Thru data dictionary

        try
        {
            sbexception.AppendLine(string.Format("=================================================="));
            sbexception.AppendLine(string.Format(" Data parameters Count at Source :{0}", ex.Data.Count));
            sbexception.AppendLine(string.Format("=================================================="));

            string skey = string.Empty;
            foreach (object key in ex.Data.Keys)
            {
                try
                {
                    if (key != null)
                    {
                        skey = Convert.ToString(key);
                        sbexception.AppendLine(string.Format(" Key :{0} , Value:{1}", skey, Convert.ToString(ex.Data[key])));
                    }
                    else
                    {
                        sbexception.AppendLine(string.Format(" Key is null"));
                    }
                }
                catch (Exception e1)
                {
                    sbexception.AppendLine(string.Format("**  Exception occurred when writting log *** [{0}] ", e1.Message));
                }
            }
        }
        catch (Exception ex1)
        {
            sbexception.AppendLine(string.Format("**  Exception occurred when writting log *** [{0}] ", ex1.Message));
        }

        #endregion
        sbexception.AppendLine(string.Format("=================================================="));
        sbexception.AppendLine(string.Format(" Source : {0} ", ex.Source));
        sbexception.AppendLine(string.Format("=================================================="));
        sbexception.AppendLine(string.Format(" StackTrace : {0} ", ex.StackTrace));
        sbexception.AppendLine(string.Format("=================================================="));
        sbexception.AppendLine(string.Format(" TargetSite : {0} ", ex.TargetSite));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format(" Finished Writting Exception info :{0} ", count));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format("************************************************"));
        sbexception.AppendLine(string.Format(""));
        sbexception.AppendLine(string.Format(""));

        return sbexception.ToString();

    }
}

以下是使用此实用程序类的示例类

[Serializable]
    public class FlatFileItem
    {
        ArrayList errorlist = new ArrayList();

        public FlatFileItem()
        {
            if (errorlist == null) { errorlist = new ArrayList(); }
        }

        //Name of the file
        public string FileName { get; set; }
        public override string ToString()
        {
            return string.Format(@"FlatFileItem (Unzip FTPLineItem) => FileName:{0}",  this.FileName);
        }
    }


public class someclass {
    public void somemethod(){
        try{

              //Throw exception code

        } catch (Exception ex)
                    {
                        ex.Data["flatfile"] = Convert.ToString(flatfile);  //Using data property
                        flatfile.HasErrors = true;  //not there in above example
                        flatfile.Parent.AddErrorInfo(ex); //not there in above example
                        logger.Error(String.Format(ex.Message)); //not there in above example

                        throw ( new Exception ("yourmsg",ex)); //if you want to do this
                    }
    }
}

我的观点是,你不应该尝试自己格式化异常。相反,要依赖于Exception.ToString()方法,它将显示有关内部异常、跨远程边界重新引发的异常等所有必要细节。 - Martin Liversage
很抱歉我没有完全理解这个问题。但在我的情况下,我不得不进行格式化,因为我正在使用Exception.Data属性来添加对象,以便我可以从深层的函数堆栈中查看所有业务对象。我不确定Exception.ToString()是否处理异常类的Data对象。 - aked

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接