在C#.NET中捕获所有未处理异常的简便方法

时间:2008-11-17 14:49:40

标签: c# asp.net

我有一个用C#.NET构建的网站,它倾向于从各种用户控件产生相当稳定的SQL超时流,我想轻松地弹出一些代码来捕获所有未处理的异常并将它们发送到可以记录它们的东西并向用户显示友好的信息。

如何通过最小的努力捕获所有未处理的异常?

this question似乎说这是不可能的,但这对我来说没有意义(而且它是关于Windows应用程序中的.NET 1.1):

13 个答案:

答案 0 :(得分:26)

所有未处理的异常最终都通过global.asax中的Application_Error传递。因此,要提供常规异常消息或执行日志记录操作,请参阅Application_Error

答案 1 :(得分:16)

如果您需要捕获所有线程中的异常,最好的方法是实现Unhandled Exception Module并将其添加到应用程序外观here中以获取示例

答案 2 :(得分:14)

使用Global.asax文件中的Application_Error方法。在Application_Error方法实现中调用Server.GetLastError(),记录Server.GetLastError()返回的异常的详细信息,无论你希望如何。

e.g。

void Application_Error(object sender, EventArgs e) 
{ 
    // Code that runs when an unhandled error occurs
    log4net.ILog log = log4net.LogManager.GetLogger(typeof(object));
    using (log4net.NDC.Push(this.User.Identity.Name))
    {
        log.Fatal("Unhandled Exception", Server.GetLastError());
    }
}

不要过多关注log4net的东西,Server.GetLastError()是最有用的一点,记录你喜欢的细节。

答案 3 :(得分:8)

ELMAH project听起来值得一试,其功能列表包括:

  

ELMAH(错误记录模块和   处理程序)是一个应用程序范围的错误   完全记录设施   可插拔。它可以动态添加   到正在运行的ASP.NET Web应用程序,   甚至所有ASP.NET Web应用程序   在一台机器上,没有任何需要   重新编译或重新部署。

     
      
  • 记录几乎所有未处理的例外情况。
  •   
  • 远程查看已重新编码的例外日志的网页。
  •   
  • 用于远程查看任何已记录内容的完整详细信息的网页   异常。
  •   
  • 在很多情况下,你可以查看死亡的原始黄色屏幕   为给定的ASP.NET生成的   异常,即使使用customErrors模式   关掉了。
  •   
  • 发生时每个错误的电子邮件通知。
  •   
  • 日志中最近15个错误的RSS提要。
  •   
  • 日志的许多后备存储实现
  •   

有关使用dotnetslackers

中的ELMAH的更多信息

答案 4 :(得分:7)

您可以订阅AppDomain.CurrentDomain.UnhandledException活动。

答案 5 :(得分:3)

重要的是要注意你不应该来捕获未处理的异常。如果您遇到SQL超时问题,则应该特别注意这些问题。

答案 6 :(得分:2)

你的意思是在所有线程中处理它,包括由第三方代码创建的线程吗?在“已知”线程中,只需捕获堆栈顶部的Exception

答案 7 :(得分:1)

我建议查看log4net,看看它是否适合问题的记录部分。

答案 8 :(得分:1)

如果使用.net 2.0框架,我使用内置的Health Monitoring服务。这里有一篇很好的文章描述了这种方法: http://aspnet.4guysfromrolla.com/articles/031407-1.aspx

如果您坚持使用1.0框架,我会使用ELMAH: http://msdn.microsoft.com/en-us/library/aa479332.aspx

希望这会有所帮助

答案 9 :(得分:1)

此问题处理有两个部分&识别。

识别

这是在最终捕获异常时执行的操作,而不一定是抛出异常的位置。因此,该阶段的异常必须有足够的上下文信息,以便您确定问题是什么

处理

为了处理,你可以 a)添加一个HttpModeule。看到 http://www.eggheadcafe.com/articles/20060305.asp 我建议这种方法只有在没有可用的上下文信息且可能存在IIS / aspnet时才会发布,简而言之就是灾难性的情况

b)创建一个名为AbstractBasePage的抽象类,它派生自Page类,并使所有代码隐藏类派生自AbstractBasePage

AbstractBasePage可以实现该Page.Error委托,以便可以在此处捕获通过n层体系结构渗透的所有异常(并且可能已记录)

我建议您使用这种异常的原因(SQlException)有足够的上下文信息,以便您确定它是超时并采取可能的操作。此操作可能包括将用户重定向到自定义错误页面,并为每种不同类型的异常(Sql,Web服务,异步调用超时等)提供相应的消息。

由于 RVZ

答案 10 :(得分:1)

如果您没有强行关闭sqlconnections,通常会发生超时错误。

所以,如果你有一个

try {
    conn.Open();
    cmd.ExecuteReader();
    conn.Close();
} catch (SqlException ex) {
    //do whatever
}

如果该ExecuteReader出现任何问题,您的连接将无法关闭。总是添加一个finally块。

try {
    conn.Open();
    cmd.ExecuteReader();
    conn.Close();
} catch (SqlException ex) {
    //do whatever
} finally {
    if(conn.State != ConnectionState.Closed)
       conn.Close();
}

答案 11 :(得分:1)

一个简短的答案是在调用委托时使用(匿名)委托方法和公共处理代码。

背景:如果您已针对弱点,或者有一些样板错误处理代码,则需要普遍应用于特定类别的问题,并且您不希望编写相同的尝试每个调用位置的..catch,(例如更新每个页面上的特定控件等)。

案例研究:一个痛点是Web表单并将数据保存到数据库。我们有一个控件,可以向用户显示保存的状态,我们希望在每个页面中都有常见的错误处理代码以及常见的显示,而不需要复制粘贴重用。此外,每个页面都以自己的方式完成它自己的事情,因此代码中唯一真正常见的部分是错误处理和显示。

现在,在被抨击之前,这不是数据访问层和数据访问代码的替代品。这仍然是假设存在,良好的n层分离等。这段代码是特定于UI层的,允许我们编写干净的UI代码而不重复自己。我们非常相信不会抛弃异常,但某些异常不应该让用户获得一般错误页面并丢失他们的工作。将有sql超时,服务器出现故障,死锁等等。

解决方案:我们这样做的方法是将匿名委托传递给自定义控件上的方法,并基本上使用匿名委托注入try块。

// normal form code.
private void Save()
{
    // you can do stuff before and after. normal scoping rules apply
    saveControl.InvokeSave(
        delegate
        {
            // everywhere the save control is used, this code is different
            // but the class of errors and the stage we are catching them at
            // is the same
            DataContext.SomeStoredProcedure();
            DataContext.SomeOtherStoredProcedure();
            DataContext.SubmitChanges();
        });
}

SaveControl本身的方法如下:

public delegate void SaveControlDelegate();

public void InvokeSave(SaveControlDelegate saveControlDelegate)
{        
    // I've changed the code from our code. 
    // You'll have to make up your own logic.
    // this just gives an idea of common handling.
    retryButton.Visible = false;
    try
    {
        saveControlDelegate.Invoke();
    }
    catch (SqlTimeoutException ex)
    {
        // perform other logic here.
        statusLabel.Text = "The server took too long to respond.";
        retryButton.Visible = true;
        LogSqlTimeoutOnSave(ex);
    }
    // catch other exceptions as necessary. i.e.
    // detect deadlocks
    catch (Exception ex)
    {
        statusLabel.Text = "An unknown Error occurred";
        LogGenericExceptionOnSave(ex);
    }
    SetSavedStatus();
}

  • 还有其他方法可以实现这一点(例如,通用基类,接口),但在我们的例子中,这是最合适的。
  • 这不是取代Elmah这样的好工具来记录所有未处理的异常。这是以标准方式处理某些异常的有针对性的方法。

答案 12 :(得分:0)

这是一个老问题,但这里没有列出最好的方法(对我而言)。所以我们在这里:

ExceptionFilterAttribute对我来说是一个很好的解决方案。来源:http://weblogs.asp.net/fredriknormen/asp-net-web-api-exception-handling

public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        var exception = context.Exception;
        if(exception is SqlTimeoutException)
        {
            //do some handling for this type of exception
        }
    }
}

并将其附加到f.e. HomeController中:

[ExceptionHandling]
public class HomeController: Controller
{

}