为日志记录定义自定义异常格式

时间:2014-06-12 11:59:31

标签: c# exception logging

我经常有一个要求:

如何使用尽可能多的信息记录异常但是尽可能少的文本?


当然,exception.Message的信息太少了。
但是exception.ToString()有点太多而且不可读:

System.NotImplementedException: Die Methode oder der Vorgang ist nicht implementiert.
   bei ConsoleApplication1.Program.Second() in C:\..\Program.cs:Line 48.
   bei ConsoleApplication1.Program.First() in C:\..\Program.cs:Line 43.
   bei ConsoleApplication1.Program.Main(String[] args) in C:\..\Program.cs:Line 21.

我的想法是一个包含最相关信息的单一行,如下所示:

System.NotImplementedException: Die Methode oder der Vorgang ist nicht implementiert {ConsoleApplication1.Program.Main(String[] args)[21] -> ConsoleApplication1.Program.First()[43] -> ConsoleApplication1.Program.Second()[48]}

最好的方法是什么?
我意识到我可以从TargetSite属性获得最深的异常导致方法名称。 但是如何获取方法呢?最重要的是:如何获得行号?

3 个答案:

答案 0 :(得分:1)

您可以将来自exception.Message的信息与Environment.StackTrace相结合(例如,拆分成行并仅使用最后几行)或使用Diagnostics.StackTrace类来获取您需要的信息。祝你好运!

答案 1 :(得分:1)

就例外情况而言,TMI是YMMV的事情。 我会选择Exception.Message + StackTrace。 (几乎是ToString()似乎打印)

如果您真的认为您只需要用户代码中的最后几个函数/仅函数,则可以拆分堆栈跟踪并仅获取与您的类/程序集名称匹配的行。

IMO更多信息总是很好,在阅读过程中没有节省空间/花时间跳过几行是值得开发人员挫折/花费的时间

  哇,这段代码发生了什么,我希望我从堆栈跟踪中再添加一行。

要获取行号,您需要pdb文件信息: C# Exceptions Not Giving Line Numbers

答案 2 :(得分:0)

根据@data的链接,我制作了自己的方法,符合我的要求。

public static class ExceptionExtensions
{
    /// <summary>
    /// Return a compact exception format for logging
    /// </summary>
    /// <remarks>
    /// In a single line the exception will contains the most relevant information like
    /// Excepition type and message, the call stack methods and lines
    /// Note: call stack is only available in Debug, Release will not contain these information
    /// </remarks>
    /// <param name="ex"></param>
    /// <returns></returns>
    /// <example>System.NotImplementedException: NotImplementedException (OwnLib.WoohooClass.ThisIsTheBestMethodEver()[13] => ConsoleApplication1.OwnClass.CallMeAClass()[13] => ConsoleApplication1.Program.Second(System.TimeSpan)[68] => ConsoleApplication1.Program.First()[57] => ConsoleApplication1.Program.Main(System.String[])[25])</example>
    public static string ToCompactString(this Exception ex)
    {
        var stack = new List<string>();
        var stackTrace = new StackTrace(ex, true);
        for (int i = 0; i < stackTrace.FrameCount; i++)
        {
            StackFrame sf = stackTrace.GetFrame(i);
            var method = sf.GetMethod();
            stack.Add(string.Format("{0}[{1}]", 
                method.ToString().SubstringFromMatch(method.Name, true).Replace(method.Name, string.Format("{0}.{1}", method.ReflectedType.FullName, method.Name)), 
                sf.GetFileLineNumber()));
        }

        return string.Format("{0}: {1} ({2})", ex.GetType(), ex.Message, stack.GetDelimiterSeperatedList(" => "));
    }
}

//Helper methods
public static class StringExtensions
{
    /// <summary>
    /// Substring for a searched string. takes the whole string from the match and cuts before.
    /// </summary>
    /// <param name="searchString">string to search within</param>
    /// <param name="searchedString">string to search for</param>
    /// <param name="includeMatch"><c>true</c> = with searchString, <c>false</c> = without</param>
    /// <param name="count">which time to found (0 = first)</param>
    /// <returns>a substring for a match, the whole string when no match</returns>
    public static string SubstringFromMatch(this string searchString, string searchedString, bool includeMatch, int count = 0)
    {
        if (searchString.Contains(searchedString))
        {
            var index = searchString.IndexOf(searchedString, count, StringComparison.Ordinal) + (includeMatch ? 0 : searchedString.Length);
            return searchString.Substring(index, searchString.Length - index);
        }
        return searchString;
    }

    /// <summary>
    /// Returns a list auf values, concatted by a delimiter like ",", e.g. "1,2,abc,cde"
    /// </summary>
    /// <remarks>a maximum of 999 items could be seperated</remarks>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="delimiter">a delimiter used to seperate the items</param>
    /// <example>"," => "item1,item2,item3"; " - " "item1 - item2 - item3"</example>
    public static string GetDelimiterSeperatedList<T>(this ICollection<T> list, string delimiter)
    {
        if (list != null && list.Count > 0)
        {
            var seperatedList = String.Empty;
            var listCounter = 0;
            foreach (var id in list)
            {
                seperatedList += String.Format("{0}{1}", id, delimiter);
                if (listCounter++ >= 999) break;
            }
            return seperatedList.Remove(seperatedList.LastIndexOf(delimiter, StringComparison.Ordinal));
        }
        return null;
    }
}

示例输出如下所示:

  

System.NotImplementedException:你有forgotton做某事(AnotherLib.WoohooClass.ThisIsTheMostUselessMethodEver()[13] =&gt; ConsoleApplication1.AnotherClass.CallMeAClass()[13] =&gt; ConsoleApplication1.Program.Third(System.TimeSpan) [68] =&gt; ConsoleApplication1.Program.Second()[63] =&gt; ConsoleApplication1.Program.First()[57] =&gt; ConsoleApplication1.Program.Main(System.String [])[25])

顺便说一下:我还验证了上面的解决方案的时间测量和正常的exception.ToString()方法。有趣的是,没有显着差异。

谢谢你的帮助。