【问题标题】:How to use ELMAH to manually log errors如何使用 ELMAH 手动记录错误
【发布时间】: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 不会自动记录此异常,因为它已被处理。

【问题讨论】:

标签: c# exception-handling elmah error-logging


【解决方案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));
                }
            

            【讨论】:

              猜你喜欢
              • 2012-11-24
              • 2013-04-03
              • 1970-01-01
              • 2020-11-24
              • 2018-03-20
              • 1970-01-01
              • 2017-05-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多