Регистрация ошибок в ASP.NET MVC

109

В настоящее время я использую log4net в своем приложении ASP.NET MVC для регистрации исключений. Я делаю это, наследуя все мои контроллеры от класса BaseController. В событии OnActionExecuting BaseController я регистрирую все исключения, которые могли произойти:

protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
    // Log any exceptions
    ILog log = LogManager.GetLogger(filterContext.Controller.GetType());

    if (filterContext.Exception != null)
    {
        log.Error("Unhandled exception: " + filterContext.Exception.Message +
            ". Stack trace: " + filterContext.Exception.StackTrace, 
            filterContext.Exception);
    }
}

Это отлично работает, если во время действия контроллера возникло необработанное исключение.

Что касается ошибок 404, у меня есть настраиваемая ошибка в моем web.config, например:

<customErrors mode="On">
    <error statusCode="404" redirect="~/page-not-found"/>
</customErrors>

И в действии контроллера, которое обрабатывает URL-адрес «страница не найдена», я регистрирую запрашиваемый исходный URL-адрес:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult PageNotFound()
{
    log.Warn("404 page not found - " + Utils.SafeString(Request.QueryString["aspxerrorpath"]));

    return View();
}

И это тоже работает.

Проблема, с которой я столкнулся, заключается в том, как регистрировать ошибки, которые находятся на самих страницах .aspx. Допустим, у меня есть ошибка компиляции на одной из страниц или какой-то встроенный код, который вызовет исключение:

<% ThisIsNotAValidFunction(); %>
<% throw new Exception("help!"); %>

Похоже, что атрибут HandleError правильно перенаправляет это на мою страницу Error.aspx в общей папке, но он определенно не перехватывается моим методом OnActionExecuted моего BaseController. Я думал, что могу поместить код ведения журнала на саму страницу Error.aspx, но я не уверен, как получить информацию об ошибке на этом уровне.

Кевин Панг
источник
+1 для ELMAH. Вот учебное пособие по ELMAH, которое я написал, чтобы помочь вам начать работу. Также не забудьте использовать пакет Elmah.MVC при использовании ASP.NET MVC, чтобы избежать проблем с настраиваемыми страницами ошибок и т. Д.
ThomasArdal
Есть несколько продуктов, которые будут регистрировать все ошибки, возникающие в приложениях .NET. Они не такие низкоуровневые, как ELMAH или log4net, но сэкономят вам массу времени, если вы просто пытаетесь отслеживать и диагностировать ошибки: Bugsnag и AirBrake - два из них, которые, как я знаю, делают в .NET
Дон П.

Ответы:

103

Я бы подумал об упрощении вашего веб-приложения, подключив Elmah .

Вы добавляете сборку Elmah в свой проект, а затем настраиваете свой web.config. Затем он будет регистрировать исключения, созданные на уровне контроллера или страницы. Его можно настроить для входа в различные места (например, SQL Server, электронную почту и т. Д.). Он также предоставляет веб-интерфейс, так что вы можете просматривать журнал исключений.

Это первое, что я добавляю в любое создаваемое мной приложение asp.net mvc.

Я все еще использую log4net, но стараюсь использовать его для регистрации отладки / информации и оставляю все исключения на усмотрение Elmah.

Вы также можете найти дополнительную информацию в вопросе Как вы регистрируете ошибки (исключения) в своих приложениях ASP.NET? .

Эндрю Риммер
источник
3
Я начал использовать Elmah недавно, и это один из самых удобных и простых средств ведения журнала исключений, который я когда-либо использовал. Я прочитал сообщение, в котором говорилось, что MS должна включить его в ASP.net, и я согласен.
dtc
14
Почему мне нужны и ELMAH, и log4net для приложения. протоколирование? Почему нет единого решения?
VJAI
Будет ли это работать, даже если у меня многоуровневая архитектура? Контроллеры - сервисы - репозитории?
a.farkas2508
2
ELMAH переоценен.
Ронни Оверби
ELMAH бесплатно?
Даллас
38

Вы можете подключиться к событию OnError в Global.asax.

Что-то вроде этого:

/// <summary>
/// Handles the Error event of the Application control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected void Application_Error(object sender, EventArgs e)
{
    if (Server != null)
    {
        Exception ex = Server.GetLastError();

        if (Response.StatusCode != 404 )
        {
            Logging.Error("Caught in Global.asax", ex);
        }

    }


}
Чак Конвей
источник
3
Это должно уловить все исключения. Считаю это лучшей практикой.
Андрей Рыня
4
Согласно анализу значений ReSharper, Serverвсегда будет отличным от нуля.
Дрю Ноукс,
6
Игнорирование 404 не помогло мне так, как вы его написали. Я написалif (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) return;
pauloya
21

MVC3
Create Attribute, который наследуется от HandleErrorInfoAttribute и включает ваш выбор ведения журнала

public class ErrorLoggerAttribute : HandleErrorAttribute 
{
    public override void OnException(ExceptionContext filterContext)
    {
        LogError(filterContext);
        base.OnException(filterContext);
    }

    public void LogError(ExceptionContext filterContext)
    {
       // You could use any logging approach here

        StringBuilder builder = new StringBuilder();
        builder
            .AppendLine("----------")
            .AppendLine(DateTime.Now.ToString())
            .AppendFormat("Source:\t{0}", filterContext.Exception.Source)
            .AppendLine()
            .AppendFormat("Target:\t{0}", filterContext.Exception.TargetSite)
            .AppendLine()
            .AppendFormat("Type:\t{0}", filterContext.Exception.GetType().Name)
            .AppendLine()
            .AppendFormat("Message:\t{0}", filterContext.Exception.Message)
            .AppendLine()
            .AppendFormat("Stack:\t{0}", filterContext.Exception.StackTrace)
            .AppendLine();

        string filePath = filterContext.HttpContext.Server.MapPath("~/App_Data/Error.log");

        using(StreamWriter writer = File.AppendText(filePath))
        {
            writer.Write(builder.ToString());
            writer.Flush();
        }
    }

Поместите атрибут в Global.asax RegisterGlobalFilters

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
       // filters.Add(new HandleErrorAttribute());
        filters.Add(new ErrorLoggerAttribute());
    }
отметка
источник
1

Вы думали о расширении атрибута HandleError? Кроме того , Скотт имеет хороший блог о фильтровальных перехватчиков на контроллеры / действия здесь .

Кирон
источник
1

Представление Error.aspx определяется следующим образом:

namespace MvcApplication1.Views.Shared
{
    public partial class Error : ViewPage<HandleErrorInfo>
    {
    }
}

HandleErrorInfo имеет три свойства: строка ActionName string ControllerName Exception Exception

У вас должен быть доступ к HandleErrorInfo и, следовательно, к исключению в представлении.

Правин Ангян
источник
0

Вы можете попробовать изучить HttpContext.Error, но я не уверен в этом.

Майк Чалий
источник