web-dev-qa-db-ja.com

エラー処理と組み合わせたASP.NetMVC例外ログ

ASP.Net MVC1.0アプリケーションでエラー処理と組み合わせた例外ログを実行するための簡単なソリューションを探しています。

StackOverflowに投稿された質問など、さまざまな状況に応じてさまざまなソリューションを提供する記事をたくさん読みました。私はまだ自分のニーズに合った解決策を思い付くことができません。

これが私の要件です:

  1. コントローラで[HandleError]属性(または同等のもの)を使用して、アクションまたはビューのいずれかからスローされる可能性のあるすべての例外を処理できるようにするため。これは、どのアクションでも特に処理されなかったすべての例外を処理する必要があります(ポイント2で説明)。コントローラ内のすべてのアクションについて、エラーの場合にユーザーをリダイレクトする必要があるビューを指定できるようにしたいと思います。

  2. 特定のアクションの上部に[HandleError]属性(または同等のもの)を指定して、特定の例外をキャッチし、ユーザーを例外に適したビューにリダイレクトできるようにしたい。他のすべての例外は、引き続きコントローラーの[HandleError]属性で処理する必要があります。

  3. 上記のどちらの場合も、log4net(またはその他のログライブラリ)を使用して例外をログに記録する必要があります。

上記を達成するにはどうすればよいですか?すべてのコントローラーを、OnExceptionメソッドをオーバーライドするベースコントローラーから継承すること、およびロギングを行うことについて読みました。ただし、これはユーザーを適切なビューにリダイレクトすることを混乱させたり、混乱させたりします。

これを処理するためにIExceptionFilterを実装する独自のフィルターアクションの作成について読みましたが、これは[HandleError]属性と競合します。

これまでのところ、最善の解決策は、HandleErrorAttributeから継承する独自の属性を作成することであると考えています。そうすれば、[HandleError]のすべての機能を取得し、独自のlog4netログを追加できます。解決策は次のとおりです。

    public class HandleErrorsAttribute: HandleErrorAttribute {

      private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

      public override void OnException(ExceptionContext filterContext)
      {
          if (filterContext.Exception != null)
          {
            log.Error("Error in Controller", filterContext.Exception);
          }

          base.OnException(filterContext);
      }
   }

上記のコードは私の要件で機能しますか?そうでない場合、どのソリューションが私の要件を満たしていますか?

22
Saajid Ismail

私はまだそこにあるすべての異なるソリューションと、属性が互いにどのように干渉する可能性があるかについて少し混乱していますが、私はこのソリューションを使用しました:

public class LogErrorsAttribute: FilterAttribute, IExceptionFilter
{
    #region IExceptionFilter Members

    void IExceptionFilter.OnException(ExceptionContext filterContext)
    {
        if (filterContext != null && filterContext.Exception != null)
        {
            string controller = filterContext.RouteData.Values["controller"].ToString();
            string action = filterContext.RouteData.Values["action"].ToString();
            string loggerName = string.Format("{0}Controller.{1}", controller, action);

            log4net.LogManager.GetLogger(loggerName).Error(string.Empty, filterContext.Exception);
        }

    }

    #endregion
}

元の質問で説明したように、引き続き[HandleError]属性を使用し、各コントローラーを[LogErrors]属性で装飾するだけです。

これは、エラーログを1つの場所に保持し、重複する例外が複数回ログに記録されることを引き起こさないため、私にとってはうまくいきます([HandleError]を拡張し、属性を複数の場所で使用すると発生します)。

非常に面倒で複雑になったり、[HandleError]の使用に影響を与えたりすることなく、例外ロギングとエラー処理の両方を1つの属性またはクラスに組み合わせることができるとは思いません。

しかし、これは私にとってはうまくいきます。なぜなら、[LogErrors]属性を使用して各コントローラーを1回だけ装飾し、コントローラーとアクションを互いに干渉することなく、希望どおりに[HandleError]を装飾するからです。

更新:

これが私がそれを使う方法の例です:

[LogErrors(Order = 0)]
[HandleError(Order = 99)]
public class ContactController : Controller
{
    public ActionResult Index()
    {
        return View(Views.Index);
    }

    public ActionResult Directions()
    {
        return View(Views.Directions);
    }


    public ActionResult ContactForm()
    {
        FormContactMessage formContactMessage = new FormContactMessage();

        return View(Views.ContactForm,formContactMessage);
    }

    [HandleError(ExceptionType = typeof(SmtpException), View = "MessageFailed", Order = 1)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult ContactForm(FormContactMessage formContactMessage)
    {
        if (ModelState.IsValid)
        {
            if (formContactMessage.IsValid)
            {
                SmtpClient client = new SmtpClient();

                MailAddress recipientAddress = new MailAddress(Properties.Settings.Default.ContactFormRecipientEmailAddress);
                MailAddress senderAddress = new MailAddress(Properties.Settings.Default.ContactFormSenderEmailAddress);
                MailMessage mailMessage = formContactMessage.ToMailMessage(recipientAddress, senderAddress);

                client.Send(mailMessage);

                return View("MessageSent");
            }
            else
            {
                ModelState.AddRuleViolations(formContactMessage.GetRuleViolations());
            }
        }
        return View(Views.ContactForm, formContactMessage);
    }

    private static class Views
    {
        public static string Index { get { return "Index"; } }
        public static string Directions { get { return "Directions"; } }
        public static string ContactForm { get { return "ContactForm"; } }

    }
}

上記のコードでは、ContactFormアクションオーバーロードのSmtpExceptionsは、非常に特殊な方法で処理されます。失敗した送信メッセージに固有のViewPageがユーザーに表示されます。この場合、「MessageFailed」と呼ばれます。他のすべての例外は、[HandleError]のデフォルトの動作によって処理されます。また、エラーのログ記録が最初に行われ、次にエラーの処理が行われることに注意してください。これは次のように示されます。

[LogErrors(Order = 0)]
[HandleError(Order = 99)]

更新:

これに対する代替の解決策があり、非常に優れた説明があります。関係する問題をよりよく理解するために、それを読むことをお勧めします。

ASP.NET MVC HandleError属性、カスタムエラーページ、およびログ例外 (以下の回答でリンクを提供してくれた以下のScott Shepherdに感謝します)。

25
Saajid Ismail