c# - ELMAH 异常生成通用 "The service is unavailable"消息

标签 c# asp.net asp.net-mvc elmah elmah.mvc

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

Controller :

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 重定向,甚至 system.webServer 中的 httpErrors。显示的内容不是正常的 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 将自身插入模块管道的方式存在某种问题,这阻止了我处理该问题。

编辑:

澄清一下,这是一个简化的示例。我不打算向最终用户公开此信息。此可用性页面仅适用于对 future 问题进行故障排除的内部用户。

ELMAH 只是相关应用程序使用的服务/数据库之一,我想为管理员提供一个类似于仪表板的快速 View ,显示上行和下行的情况。如果 ELMAH 错误导致此 insta-503,我将无法执行此操作。

最佳答案

好吧,基本上没有任何代码这是不可能的。 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");
        }
    }
}

将配置文件中的 ErrorLogModuleCustomErrorLogModule 交换,当发生错误时,Elmah 会提示;在测试页面中调用 Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("test")); 会导致 InvalidOperationException("An error was notlogging")被排除在通话之外。


如果您想获取在尝试记录异常时发生的确切异常,您可以利用 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);
    }
}

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

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


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

关于c# - ELMAH 异常生成通用 "The service is unavailable"消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25164866/

相关文章:

c# - 重定向到页面和按下后退按钮后进入同一页面之间的区别?

asp.net - 限制 Azure 中的 Web 服务调用

javascript - 关于向 Javascript 添加数据的简单问题

c# - 我的代码如何确定它是否在 IIS 中运行?

.net - Entity Framework v4 : Preventing Storage Model Overwrites by Update Model Wizard

javascript - 如何在表单提交后使用 Ajax POST

c# - 如何在 int 中存储转换日期时间?

c# - 转换函数以确定从 C# 到 C++ 的运算符优先级

c# - 如何检索与可配置产品关联的简单产品

c# - 使用 C# 开发一个充当 Web 程序的程序