在后台服务中调试堆栈溢出错误

10

这不是一个真正的问题,而是一篇希望能帮助其他人的答案。

那些以前编写过Windows服务的人都知道,在其中查找错误可能是一项艰巨的任务,特别是如果它只在实际环境中发生。在我的情况下,我有一个服务,在几个小时内平稳运行,然后由于堆栈溢出错误而停止。没有堆栈跟踪。祝你好运在海量日志中寻找错误原因。

该服务确实生成了一个日志文件,并且代码中有很多日志条目,但尽管如此详细,它仍会产生大小为500 MB的日志文件!您几乎无法打开该文件,更不用说分析它了。但是,如何解决这个问题?您可以尝试生成较少信息的日志文件,或者一个可以在编写新条目时自动删除旧日志条目的日志文件,但是这样做会使错误的上下文丢失。

解决方案是一个日志文件,将跟踪代码中的循环,并自动删除每个成功迭代的日志条目。这样,您可以维护一个高度详细的日志文件,同时保持相对较小的大小。当您的服务出现故障时,您的日志文件将告诉您发生了什么,以及它发生的原因和背景。

您可以从http://sourceforge.net/projects/smartl/files/?source=navbar下载这个日志文件生成器。它是一个独立的类,所有方法都是静态的。提供了一个示例类,展示了如何正确使用日志记录方法:

    public void ExampleMethod()
    {           
        SmartLog.EnterMethod("ExampleMethod()"); 
        try
        {
            SmartLog.Write("Some code happening before the loop");

            Guid exampleLoopID = SmartLog.RegisterLoop("exampleLoopID");
            for (int i = 0; i < 10; i++)
            {
                SmartLog.IncrementLoop(exampleLoopID);

                SmartLog.Write("Some code happening inside the loop.");

            }
            SmartLog.CompleteLoop(exampleLoopID);

            SmartLog.Write("Some code happening after the loop.");

            SmartLog.LeaveMethod("ExampleMethod()");
        }
        catch (Exception ex)
        {
            SmartLog.WriteException(ex);
            SmartLog.LeaveMethod("ExampleMethod()");
            throw;
        }
    }

确保您的应用程序在其根文件夹上具有读写权限。

如果您执行代码,并且在循环内部中断它,则日志文件将看起来像这样:

. ENTER METHOD: FirstMethod()
some code happening here.
Calling a different method:

. . ENTER METHOD: ExampleMethod()
some code happening before the loop.

LOOP: doWorkLoopID [4135a8ed-05b7-45de-b887-b2ab3c638faa] - CURRENT ITERATION: 20
some code happening inside the loop.

一旦循环完成,它的内容就会被移除,您的日志文件将如下所示:

. ENTER METHOD: FirstMethod()
some code happening here.
Calling a different method:

. . ENTER METHOD: ExampleMethod()
some code happening before the loop.

LOOP: doWorkLoopID [4135a8ed-05b7-45de-b887-b2ab3c638faa] - TOTAL ITERATIONS: 22

some code happening after the loop.
. . LEAVING METHOD: ExampleMethod()

some code happening here.
some code happening here.
. LEAVING METHOD: FirstMethod()

我希望这篇文章可以帮助别人解决那个问题,否则可能需要几周时间才能解决。对我来说,它确实很有效。


8
请将您的解决方案作为答案发布。再仔细阅读一遍 http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ - Soner Gönül
6
在Stack Overflow上,这样做是可以的,而且实际上也是受到鼓励的。但是你仍然应该用一个问题的形式来呈现它,然后再自己回答这个问题。 - Jonathon Reinhart
1
感谢您的贡献。我鼓励您像其他人建议的那样:编辑您的问题,使其只包含问题,然后将答案作为答案提交。一旦您有足够的声望来通过新用户限制,您实际上可以同时提交您的问题和答案。 - User
好的,下次会这样做。这是我在Stack Overflow的第一次贡献。 - hannodb
1
@hannodb,我不想重复别人已经指出的内容。除非你在SO上保持这种状态,否则它被认为是一个没有答案的问题 - 因此,许多人会来检查他们是否可以帮助回答。因此,保持这种状态有点误导性。 - G.Y
1
现在,请注意:如果您不将您的答案发布为答案,那么我们继续检查如何帮助您“未回答的问题”就是浪费我们的时间。 - Doug_Ivison
3个回答

1
这是我的静态记录器解决方案。不仅适用于服务,对于所有项目都很有用:
应用程序启动时:
MyLog.Reset();

每个静态或非静态方法都以以下方式开始:
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); MyLog.Log("", stackTrace.GetFrame(0).GetMethod().DeclaringType.ToString(), stackTrace.GetFrame(0).GetMethod().Name, 0);

结果是一个graphviz图形源码,看起来像这样: 请注意,在从log.text复制文本以生成GraphViz图形时,需要手动添加最后一个闭合花括号。
digraph G{arrowsize=2.0; ratio=fill; node[fontsize=24];graph [fontsize=24] edge [fontsize=24] node [fontsize=24] ranksep = 1.5 nodesep = .25 edge [style="setlinewidth(3)"]; 

subgraph cluster_Riogrande_UI { node [style=filled]; label = "Riogrande_UI"; color=red  
subgraph cluster_UsersForm { node [style=filled]; _ctor_UF; label = "UsersForm"; color=blue }}
subgraph cluster_Riogrande_DL { node [style=filled]; label = "Riogrande_DL"; color=red  
subgraph cluster_DataAccessUsers { node [style=filled]; _ctor_DAU; label = "DataAccessUsers"; color=blue    }}
_ctor_UF -> _ctor_DAU;
}

这是由GraphViz生成的图表:

The result

这是我使用的类:

namespace Riogrande
{
    public class MyLog
    {
        private static int MaximAcceptedLevel = 5;
        private static string lastMethodName = string.Empty;
        private static string filePath = "log.txt";

        public static void Log(string namespaceName, string className, string methodName, int logLevel)
        {
            if (logLevel < MaximAcceptedLevel)
            using (StreamWriter w = File.AppendText(filePath))
            {
                string namespceName = className.Substring(0, className.LastIndexOf('.')).Replace('.', '_');

                if (className.Contains('.'))
                {
                    className = className.Substring(className.LastIndexOf('.') + 1);
                }
                if (className.Contains('+'))
                {
                    className = className.Substring(0, className.LastIndexOf('+'));
                }
                className = className.Replace('.', '_');
                string cls = "";
                for (int i = className.Length-1; i > -1; i--)
                {
                    if (Char.IsUpper(className[i]))
                    {
                        if (cls.Length < 3)
                        {
                            cls = className[i] + cls;
                        }
                    }
                }
                string currentMethodName = methodName.Replace('.', '_') + "_" + cls;
                w.WriteLine("subgraph cluster_" + namespceName + " { node [style=filled]; label = \"" + namespceName + "\"; color=red   ");
                w.WriteLine("subgraph cluster_" + className + " { node [style=filled]; " + currentMethodName + "; label = \"" + className + "\"; color=blue }}");
                if (!string.IsNullOrEmpty(lastMethodName))
                {
                    w.WriteLine(lastMethodName + " -> " + currentMethodName + ";");
                }
                lastMethodName = currentMethodName;
            }
        }

        public static void Reset()
        {
            File.Delete(filePath);
            using (StreamWriter w = File.AppendText(filePath))
            {
                w.WriteLine("digraph G{arrowsize=2.0; ratio=fill; node[fontsize=24];graph [fontsize=24] edge [fontsize=24] node [fontsize=24] ranksep = 1.5 nodesep = .25 edge [style=\"setlinewidth(3)\"]; ");
                w.WriteLine();
            }
        }    
    }
}

解决方案不提供小尺寸文件,但您可以在同一类中实现此选项。

0
解决方案是一个日志文件,它将跟踪代码中的循环,并自动删除该循环的每个成功迭代的日志条目。这样,您可以维护一个高度详细的日志文件,同时保持相对较小的大小。当您的服务出现故障时,您的日志文件将告诉您发生了什么,以及发生原因的所有必要上下文。
您可以从http://sourceforge.net/projects/smartl/files/?source=navbar下载此日志文件生成器。它是一个独立的类,所有方法都是静态的。提供了一个示例类,以向您展示如何正确使用日志记录方法:
public void ExampleMethod()
{           
    SmartLog.EnterMethod("ExampleMethod()"); 
    try
    {
        SmartLog.Write("Some code happening before the loop");

        Guid exampleLoopID = SmartLog.RegisterLoop("exampleLoopID");
        for (int i = 0; i < 10; i++)
        {
            SmartLog.IncrementLoop(exampleLoopID);

            SmartLog.Write("Some code happening inside the loop.");

        }
        SmartLog.CompleteLoop(exampleLoopID);

        SmartLog.Write("Some code happening after the loop.");

        SmartLog.LeaveMethod("ExampleMethod()");
    }
    catch (Exception ex)
    {
        SmartLog.WriteException(ex);
        SmartLog.LeaveMethod("ExampleMethod()");
        throw;
    }
}

请确保您的应用程序对其根文件夹具有读写访问权限。

如果您执行代码并在循环内部中断它,则日志文件将类似于以下内容:

. ENTER METHOD: FirstMethod()
some code happening here.
Calling a different method:

. . ENTER METHOD: ExampleMethod()
some code happening before the loop.

LOOP: doWorkLoopID [4135a8ed-05b7-45de-b887-b2ab3c638faa] - CURRENT ITERATION: 20
some code happening inside the loop.

一旦循环完成,它的内容将被删除,您的日志文件将如下所示:

. ENTER METHOD: FirstMethod()
some code happening here.
Calling a different method:

. . ENTER METHOD: ExampleMethod()
some code happening before the loop.

LOOP: doWorkLoopID [4135a8ed-05b7-45de-b887-b2ab3c638faa] - TOTAL ITERATIONS: 22

some code happening after the loop.
. . LEAVING METHOD: ExampleMethod()

some code happening here.
some code happening here.
. LEAVING METHOD: FirstMethod()

0
不错的工作,但如果真正的问题是处理大型日志甚至是巨大的日志,您应该看一下微软LogParser。只要您正确格式化日志文件,就可以像在SQL中使用的方式查询日志。您甚至可以从结果创建csv文件并分析它们或使用库在.net应用程序中执行复杂操作。
例如,假设您想选择给定页面的所有IIS条目并将结果保存为CSV,您可以执行以下操作
logparser "select * into LANDINGPAGEHITS.CSV from [yourlogfile] where cs-uri-stem like '/home.aspx"

我在这里的观点并不是要打破你的幻想,而是向你展示如何处理大文件,如果你之前格式化了应用程序日志输出,并且不必担心将条目从中删除。


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