2

我正在尝试创建一个可用性页面来检查站点使用的所有服务,将每个检查包装在 try/catch 中,然后向用户显示任何故障。其中一项服务是 ELMAH,所以我调用它是为了仔细检查我们是否可以成功记录错误。

控制器:

var a = new AvailabilityModel();

try {
    a.ElmahConnectionString = ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString;
    Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Elmah availability test"));
    a.ElmahSuccess = true;

} catch (Exception ex) {
    a.ElmahSuccess = false;
    a.ElmahException = ex;
    Response.StatusCode = 503;
}

return View(a);

当 ELMAH 成功时,一切都很好。当它抛出任何类型的错误(数据库权限等)时,我会收到一个错误,该错误未被 try/catch 或任何正常的错误捕获部分捕获:ASP.NET MVC HandleErrorcustomErrors重定向,甚至httpErrorssystem.webServer. 显示不是正常的 IIS 通用消息,而是我看到一行显示“服务不可用”。

回复:

LTSB-W34511 C:\s\d\build % curl -i http://server/test/availability
HTTP/1.1 503 Service Unavailable
Cache-Control: public, max-age=14400, s-maxage=0
Content-Type: text/html
Server: Microsoft-IIS/7.5 X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 06 Aug 2014 15:46:55 GMT
Content-Length: 27

The service is unavailable.

就是这样。至少我知道我的可用性不起作用,但我想至少向用户显示它是导致问题的 ELMAH,并显示它试图使用的连接字符串。所以,我需要以某种方式捕获这个异常。

我已经尝试以多种不同的方式调整我的 web.config,但我怀疑 ELMAH 将自身插入模块管道的方式存在一些问题,这阻止了我处理这个问题。

编辑:

为了澄清,这是一个简化的例子。我不打算向最终用户公开这些信息。此可用性页面仅对解决未来问题的内部用户可用。

ELMAH 只是相关应用程序使用的服务/数据库之一,我想为管理员提供一个快速的仪表板式视图,了解上下运行的情况。如果 ELMAH 错误导致这个 insta-503,我不能这样做。

4

2 回答 2

3

好的,基本上没有任何代码这是不可能的。Elmah 中的Raise方法不会让您看到任何错误,除非您跟踪它:

// ErrorLogModule.LogException
try
{
    Error error = new Error(e, context);
    ErrorLog errorLog = this.GetErrorLog(context);
    error.ApplicationName = errorLog.ApplicationName;
    string id = errorLog.Log(error);
    errorLogEntry = new ErrorLogEntry(errorLog, id, error);
}
catch (Exception value)
{
    Trace.WriteLine(value);
}

但是,当事件成功记录时,ErrorLogModule 将调用该logged事件,以便让潜在的侦听器知道记录成功。因此,让我们快速编写一个自定义类,该类将覆盖 ErrorLogModule 中的一些方法,并允许我们注意到记录事件:

public class CustomErrorLogModule: Elmah.ErrorLogModule
{
    public Boolean SomethingWasLogged { get; set; }
    protected override void OnLogged(Elmah.ErrorLoggedEventArgs args)
    {
        SomethingWasLogged = true;
        base.OnLogged(args);
    }

    protected override void LogException(Exception e, HttpContext context)
    {
        SomethingWasLogged = false;
        base.LogException(e, context);
        if (!SomethingWasLogged)
        {
            throw new InvalidOperationException("An error was not logged");
        }
    }
}

ErrorLogModule与配置文件中的交换,CustomErrorLogModule当发生错误时 Elmah 会抱怨;Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("test"));在测试页面中调用可以让调用InvalidOperationException("An error was not logged")被抛出。


如果您想返回在尝试记录异常时发生的确切异常,您可以使用 ErrorLogModule 在异常发生时跟踪异常的事实。创建一个监听器类:

public class ExceptionInterceptor : DefaultTraceListener
{
    public Exception TracedException { get; set; }
    public override void WriteLine(object o)
    {
        var exception = o as Exception;
        if (exception != null)
        {
            TracedException = exception;
        }
    }
}

然后你的LogException方法变成

protected override void LogException(Exception e, HttpContext context)
{
    var exceptionListener = new ExceptionInterceptor();
    Trace.Listeners.Add(exceptionListener);
    try
    {
        SomethingWasLogged = false;
        base.LogException(e, context);
        if (!SomethingWasLogged)
        {
            throw exceptionListener.TracedException;
        }
    }
    finally
    {
        Trace.Listeners.Remove(exceptionListener);
    }
}

编辑:或者即使你想尽可能简洁

public class ExceptionInterceptor : DefaultTraceListener
{
    public override void WriteLine(object o)
    {
        var exception = o as Exception;
        if (exception != null)
        {
            throw exception;
        }
    }
}

// snip... LogException in your CustomErrorLogModule
protected override void LogException(Exception e, HttpContext context)
{
    var exceptionListener = new ExceptionInterceptor();
    Trace.Listeners.Add(exceptionListener);
    try
    {
        base.LogException(e, context);
    }
    finally
    {
        Trace.Listeners.Remove(exceptionListener);
    }
}

最后一句话:这种检查服务可用性的方式有一种味道,你将在你的错误数据库中添加测试异常,这可能不是你想要的行为。我了解您的目标是检查整个日志记录链,但也许还有其他方法可以做到这一点;我真的不知道你的背景,所以我不会进一步评论,但请不要犹豫考虑一下。

无论如何,这些更改应该让您收到您需要的异常。


重要编辑:非常重要的一点:您可能希望向您的触发器添加一个触发器,CustomErrorLogModule以便在您不测试时它不会抛出。您在 Elmah 中观察到的弹性通常是一件好事,因为您不希望诊断平台导致可能需要其他诊断的问题。这就是 Elmah 或日志框架不抛出的原因,这就是为什么你应该使异常重新抛出机制可触发,这样你的程序在 Elmah 中引发异常时就不必注意它的步骤。

于 2014-08-14T15:41:48.617 回答
-1

不不不!永远不要向用户显示连接字符串,也永远不要告诉他们问题出在哪里。这样做是一个严重的安全漏洞。简单地说,不要这样做。用 Elmah 解决你的潜在问题。

错误处理管道中的问题非常糟糕,因为它会导致它尝试处理生成的新错误,这基本上会导致循环。ASP.NET 引擎识别出严重的错误,因此它给出一个通用的“服务不可用”消息。检查服务器上的事件日志以找出潜在的 Elmah 错误并更正它。

于 2014-08-06T16:00:05.637 回答