如何使用 ELMAH 手动记录错误

Posted

技术标签:

【中文标题】如何使用 ELMAH 手动记录错误【英文标题】:How to use ELMAH to manually log errors 【发布时间】:2011-11-18 10:57:04 【问题描述】:

是否可以使用 ELMAH 执行以下操作?

logger.Log(" something");

我正在做这样的事情:

try 

    // Code that might throw an exception 

catch(Exception ex)

    // I need to log error here...

ELMAH 不会自动记录此异常,因为它已被处理。

【问题讨论】:

为了将来参考,我写了一篇关于这个的帖子:Logging errors programmatically。我的ELMAH Tutorial 也有这方面的一些信息。 【参考方案1】:

直接日志写入方法,从 ELMAH 1.0 开始工作:

try 

    some code 

catch(Exception ex)

    Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(new Elmah.Error(ex));

ELMAH 1.2 引入了更灵活的 API:

try 

    some code 

catch(Exception ex)

    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);

这两种解决方案是有区别的:

Raise 方法将 ELMAH 过滤规则应用于异常。 Log 方法没有。 Raise 是基于订阅的,能够将一个异常记录到多个记录器中。

【讨论】:

你的方法和其他方法有什么区别? 这些在 Elmah 中记录错误,而不会导致应用程序停止工作。它允许您捕获常见异常,正确处理它们,但仍然能够记录它们。 我发现当 POST 返回包含 Mvc4 .Net 4.5 的不安全 html 时,Elmah.ErrorSignal 没有记录,在我的示例中,是从 Windows 访问控制服务返回的带有 SignInResponseMessage 的 POST。 Elmah.ErrorLog.GetDefault 在那种情况下确实有效 我对不安全的 Html 有同样的问题。 ErrorLog.GetDefault 成功了 使用Elmah.ErrorLog.Log() 时的一个重要警告:它会在日志调用本身失败的情况下抛出,可能会导致整个网络应用程序崩溃。 Raise() 静默失败。例如:如果服务器端存在错误配置问题(例如 Elmah 配置为将错误保存到磁盘,但没有正确访问日志文件夹),.Log() 方法将抛出。 (不过这对调试很有用,例如为什么 .Raise() 不记录任何内容?)【参考方案2】:

我建议将对 Elmah 的调用包装在您自己的简单包装类中。

using Elmah;

public static class ErrorLog

    /// <summary>
    /// Log error to Elmah
    /// </summary>
    public static void LogError(Exception ex, string contextualMessage=null)
    
        try
        
            // log error to Elmah
            if (contextualMessage != null) 
            
                // log exception with contextual information that's visible when 
                // clicking on the error in the Elmah log
                var annotatedException = new Exception(contextualMessage, ex); 
                ErrorSignal.FromCurrentContext().Raise(annotatedException, HttpContext.Current);
            
            else 
            
                ErrorSignal.FromCurrentContext().Raise(ex, HttpContext.Current);
            

            // send errors to ErrorWS (my own legacy service)
            // using (ErrorWSSoapClient client = new ErrorWSSoapClient())
            // 
            //    client.LogErrors(...);
            // 
        
        catch (Exception)
        
            // uh oh! just keep going
        
    

然后在需要记录错误时调用它。

try 
   ...
 
catch (Exception ex) 

    // log this and continue
    ErrorLog.LogError(ex, "Error sending email for order " + orderID);

这有以下好处:

您无需记住 Elmah 调用的这种略显陈旧的语法 如果您有许多 DLL,则无需从每个 DLL 中引用 Elmah Core - 只需将其放入您自己的“系统”DLL 中即可。 如果您需要进行任何特殊处理,或者只是想设置断点来调试错误,那么您可以在一处完成。 如果您离开 Elmah,您只需换一个地方即可。 如果您有想要保留的旧式错误日志记录(我只是碰巧有一个简单的错误日志记录机制,它与一些我没有时间立即删除的 UI 相关联)。

注意:我为上下文信息添加了一个“contextualMessage”属性。如果您愿意,可以省略它,但我发现它非常有用。 Elmah 会自动解包异常,因此底层异常仍会在日志中报告,但当您单击它时,上下文消息将可见。

【讨论】:

很好的答案。也许 ELMAH 应该开箱即用地实现类似的东西。有时在没有上下文的情况下很难调试错误。 我喜欢// uh oh! just keep going 的任何次要错误,但几乎没有。如果我的错误处理失败,我想知道。我想让它发出一些声音。 @JeremyCook 我同意,但需要注意的是,如果你不小心失败的错误处理例程往往最终会调用自己然后爆炸(哦,我实际上也在调用第三方 API在这里记录错误)。我可能不应该把这个留在这个答案中,但我之前曾有过这样的事情发生的不好的经历 作为扩展方法会更好。 您可能会想:不,我可以尝试记录多少手动错误?我是这么想的,大约一年前。长话短说:使用这个包装器!【参考方案3】:

您可以使用 Elmah.ErrorSignal() 方法记录问题而不会引发异常。

try

    // Some code

catch(Exception ex)

    // Log error
    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);

    // Continue

【讨论】:

【参考方案4】:
catch(Exception ex)

    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);

【讨论】:

【参考方案5】:

是的,这是可能的。 ELMAH 旨在拦截未处理的异常。但是,您可以通过 ErrorSignal 类向 ELMAH 发出异常信号。这些异常不会被抛出(不要冒泡),而只会发送给 ELMAH(以及 ErrorSignal 类的 Raise 事件的订阅者)。

一个小例子:

protected void ThrowExceptionAndSignalElmah()

    ErrorSignal.FromCurrentContext().Raise(new NotSupportedException());

【讨论】:

【参考方案6】:

我希望在我已经开始从我的 MVC4 应用程序中排队邮件的线程中做同样的事情,因此当引发异常时我没有可用的 HttpContext。为此,我根据这个问题和在此处找到的另一个答案得出了以下结论:elmah: exceptions without HttpContext?

在配置文件中我指定了一个应用程序名称:

<elmah>
    <security allowRemoteAccess="false" />
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ELMAH" applicationName="myApplication"/>   
</elmah>

然后在代码中(如上面提供的答案,但没有 HttpContext)您可以传递 null 而不是 HttpContext:

ThreadPool.QueueUserWorkItem(t => 
     try 
         ...
         mySmtpClient.Send(message);
      catch (SomeException e) 
         Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(e));
     
 );

【讨论】:

我喜欢你的解决方案;但是,我无法解决“Elmah”。在我的项目中。我尝试添加“使用 Elmah;”在我的代码中,但在我当前的上下文中不存在。 @Taersious 你的packages.config 是什么样的?你看到类似的东西:&lt;package id="elmah" version="1.2.2" targetFramework="net45" /&gt;&lt;package id="elmah.corelibrary" version="1.2.2" targetFramework="net45" /&gt;&lt;package id="elmah.sqlserver" version="1.2" targetFramework="net45" /&gt;'?你是用 NuGET 安装的吗? 我想说是的,但是我的项目目前被锁定在源代码管理中。我从项目中的示例配置文件手动实现了 elmah。 @Taersious 如果手动执行,您是否在调用 using 之前将 Elmah 引用添加到项目中...我希望它能够以任何一种方式工作,但我知道当 nuget 添加它时以上添加到packages.config【参考方案7】:

我在 ASP.NET core 上使用 ElmahCore

要手动记录错误使用 HttpContext(在控制器中)只需编写:

using ElmahCore;
...
HttpContext.RiseError(new Exception("Your Exception"));

在您的应用程序的另一部分没有 HttpContext

using ElmahCore;
...
ElmahExtensions.RiseError(new Exception("Your Exception"));

【讨论】:

【参考方案8】:

有时CurrentHttpContext 可能不可用。

定义

public class ElmahLogger : ILogger

    public void LogError(Exception ex, string contextualMessage = null, bool withinHttpContext = true)
    
        try
        
            var exc = contextualMessage == null 
                      ? ex 
                      : new ContextualElmahException(contextualMessage, ex);
            if (withinHttpContext)
                ErrorSignal.FromCurrentContext().Raise(exc);
            else
                ErrorLog.GetDefault(null).Log(new Error(exc));
        
        catch  
    

使用

public class MyClass

    readonly ILogger _logger;

    public MyClass(ILogger logger)
    
        _logger = logger;
    

    public void MethodOne()
    
        try
        

        
        catch (Exception ex)
        
            _logger.LogError(ex, withinHttpContext: false);
        
    

【讨论】:

【参考方案9】:

我试图使用 Signal.FromCurrentContext().Raise(ex); 将自定义消息写入 elmah 日志;发现这些异常都是冒泡的,eg:

try

    ...

catch (Exception ex)

    Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
    // this will write to the log AND throw the exception

此外,我看不到 elmah 如何支持不同级别的日志记录 - 是否可以通过 web.config 设置关闭详细日志记录?

【讨论】:

如果你捕捉到一个异常并且不再抛出它,异常就不会冒泡。也许我误解了? ELMAH 不支持不同级别的日志记录。仅用于错误。 谢谢,托马斯。这正是我想要确认的【参考方案10】:

用过这条线,效果很好。

 try
            //Code which may throw an error
    
    catch(Exception ex)
            ErrorLog.GetDefault(HttpContext.Current).Log(new Elmah.Error(ex));
    

【讨论】:

以上是关于如何使用 ELMAH 手动记录错误的主要内容,如果未能解决你的问题,请参考以下文章

如何保护 Elmah.axd?

如何保护 Elmah.axd?

如何从 ELMAH 发送电子邮件?

未记录 ELMAH 和 ASP.NET MVC 自定义错误

如何在 SQL Server 数据库中将 Azure 存储用于 elmah 日志而不是 elmah 日志

我可以将 Elmah 错误详细信息发送到 ApplicationInsights 吗?