调试后台服务上的堆栈溢出错误

时间:2013-09-17 14:30:18

标签: c# debugging windows-services logfile

这不是一个真正的问题,而是一个可以帮助其他人的答案。

那些之前编写过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()

我希望这可以帮助某人解决这个问题,否则可能需要数周的时间。这对我来说确实有用。

3 个答案:

答案 0 :(得分:1)

这是我的静态记录器解决方案。对所有项目都有用,不仅仅是服务:

申请开始于:

MyLog.Reset();

Yhen每个静态或非静态方法都以:

开头
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图时,手动添加最后一个结束大括号shuld。

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();
            }
        }    
    }
}

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

答案 1 :(得分:0)

不错的工作,但如果真正的问题是使用大日志甚至是大日志,那么你应该看一下microsoft LogParser。只要正确格式化日志文件,就可以像在SQL中一样查询日志。您甚至可以从结果中创建csv文件并对其进行分析,或使用这些库在.net应用程序中执行复杂的操作。

例如,假设您要为给定页面选择所有IIS条目并将结果保存为CSV,您可以执行以下操作

logparser "select * into LANDINGPAGEHITS.CSV from [yourlogfile] where cs-uri-stem like '/home.aspx"

我的观点不是破坏你的泡泡,而是告诉你如果你以前格式化你的应用程序日志输出并且不用担心从中删除条目,如何处理大文件。

答案 2 :(得分: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()