ELMAH'ın ASP.NET MVC [HandleError] özniteliğiyle çalışması nasıl sağlanır?


564

ASP.NET MVC uygulamamdaki hataları günlüğe kaydetmek için ELMAH kullanmaya çalışıyorum, ancak denetleyicilerimde [HandleError] özniteliğini kullandığımda ELMAH meydana geldiklerinde herhangi bir hata kaydetmiyor.

Tahmin ediyorum gibi ELMAH sadece işlenmemiş hataları günlüğe kaydeder ve [HandleError] özniteliği hatayı işliyor, böylece günlüğe gerek yok.

ELMAH'ın bir hata olduğunu bilmesi ve günlüğe kaydetmesi için özelliği nasıl değiştirebilirim veya özniteliği nasıl değiştirebilirim?

Düzenleme: Herkesin anladığından emin olmama izin verin, sorduğum soru değil özelliği değiştirebileceğimi biliyorum ... ELMAH, ele alındığı için bir hata olduğunu görmeyeceği anlamına gelen handleerror niteliğini atladı zaten özniteliğe göre ... Sorduğum şey, ELMAH'ın hatayı görmesine ve öznitelik tarafından ele alınmasına rağmen günlüğe kaydetmenin bir yolu var ... Etrafı araştırdım ve günlüğe zorlamak için çağırmak için herhangi bir yöntem görmüyorum hata....


12
Vay canına, umarım Jeff ya da Jared bu soruya cevap verir. Stackoverflow için ELMAH kullanıyorlar;)
Jon Limjap

11
Hmm, garip - HandleErrorAttribute kullanmıyoruz - Elmah web.config dosyamızın <modules> bölümünde ayarlanmıştır. HandleErrorAttribute kullanmanın faydaları var mı?
Jarrod Dixon

9
@Jarrod - ELMAH çatalı hakkında "özel" olanı görmek güzel olurdu.
Scott Hanselman

3
@ dswatik web.config dosyasında redirectMode öğesini ResponseRewrite olarak ayarlayarak da yönlendirmeleri önleyebilirsiniz. Bkz. Blog.turlov.com/2009/01/…
Pavel Chuchuva

6
Web belge ve [HandleError] özniteliği ve Elmah hakkında konuşurken yayınları çalışmaya devam etti, ama ben kukla durumda ayarladığınızda bu çözülen davranışı (örneğin Elmah "işlenmiş" hata günlüğü değil) görmedim. Bunun nedeni Elmah.MVC 2.0.x'ten itibaren bu özel HandleErrorAttribute'a artık gerek yoktur; nuget paketine dahildir.
14'te plyawn

Yanıtlar:


503

İstisnayı ELMAH ile ve yalnızca temel uygulama tarafından işleniyorsa kaydedecek HandleErrorAttributeşekilde OnExceptionüyesini alt sınıflandırabilir ve geçersiz kılabilirsiniz (kopyalamaya gerek yoktur). İhtiyacınız olan minimum kod miktarı aşağıdaki gibidir:

using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled) 
            return;
        var httpContext = context.HttpContext.ApplicationInstance.Context;
        var signal = ErrorSignal.FromContext(httpContext);
        signal.Raise(context.Exception, httpContext);
    }
}

Temel uygulama önce çağrılır ve istisna işlendi olarak işaretleme şansı verilir. Ancak bundan sonra istisna bildirilir. Yukarıdaki kod basittir ve HttpContexttest gibi kullanılabilir olmayan bir ortamda kullanılırsa sorunlara neden olabilir. Sonuç olarak, daha savunmacı olan (biraz daha uzun olma pahasına) kodu isteyeceksiniz:

using System.Web;
using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled       // if unhandled, will be logged anyhow
            || TryRaiseErrorSignal(context) // prefer signaling, if possible
            || IsFiltered(context))         // filtered?
            return;

        LogException(context);
    }

    private static bool TryRaiseErrorSignal(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        if (httpContext == null)
            return false;
        var signal = ErrorSignal.FromContext(httpContext);
        if (signal == null)
            return false;
        signal.Raise(context.Exception, httpContext);
        return true;
    }

    private static bool IsFiltered(ExceptionContext context)
    {
        var config = context.HttpContext.GetSection("elmah/errorFilter")
                        as ErrorFilterConfiguration;

        if (config == null)
            return false;

        var testContext = new ErrorFilterModule.AssertionHelperContext(
                              context.Exception, 
                              GetHttpContextImpl(context.HttpContext));
        return config.Assertion.Test(testContext);
    }

    private static void LogException(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        var error = new Error(context.Exception, httpContext);
        ErrorLog.GetDefault(httpContext).Log(error);
    }

    private static HttpContext GetHttpContextImpl(HttpContextBase context)
    {
        return context.ApplicationInstance.Context;
    }
}

Bu ikinci sürüm, ilk olarak ELMAH'dan günlük kaydı, postalama, filtreleme ve neye sahip olduğunuz gibi tamamen yapılandırılmış boru hattını içeren hata sinyalini kullanmaya çalışacaktır . Başarısız olursa, hatanın filtrelenip filtrelenmeyeceğini görmeye çalışır. Değilse, hata basitçe günlüğe kaydedilir. Bu uygulama posta bildirimlerini işlemez. İstisna sinyali verilebilirse, yapılandırılmışsa bir posta gönderilir.

Ayrıca, birden fazla HandleErrorAttributeörnek etkinse, yinelenen günlük kaydının gerçekleşmemesine dikkat etmeniz gerekebilir , ancak yukarıdaki iki örneğin başlamanız gerekir.


1
Mükemmel. Elmah'ı hiç uygulamaya çalışmıyordum. MVC ile iyi çalışan bir şekilde yıllardır kullandığım kendi hata raporlarımı bağlamaya çalışıyordum. Kodun bana bir başlangıç ​​noktası verdi. +1
Steve Wortham

18
HandleErrorAttribute öğesini alt sınıflamanız gerekmez. Sadece bir IExceptionFilter uygulamasına sahip olabilir ve HandleErrorAttribute ile birlikte kaydettirebilirsiniz. Ayrıca ErrorSignal.Raise (..) başarısız olursa neden bir yedek olması gerekiyor anlamıyorum. Boru hattı kötü yapılandırılmışsa düzeltilmelidir. 5 astarlı IExceptionFilter kontrol noktası 4 için. Burada - ivanz.com/2011/05/08/…
Ivan

5
Lütfen aşağıdaki cevap hakkında @IvanZlatev'in uygulanabilirlik, eksiklikler vb. İle ilgili yorumunu yapabilirsiniz. İnsanlar daha kolay / kısa / basit olduğunu ve cevabınızla aynı sonuca ulaştığını ve doğru cevap olarak işaretlenmesi gerektiğini söylüyorlar. Bu konuya bakış açınız olması ve bu cevaplarla netlik kazanmanız iyi olur.
Andrew

7
Bu hala alakalı mi yoksa ELMAH.MVC bunu ele alıyor mu?
Romias

2
Hatta bugünün sürümünde hala alakalı olup olmadığını bilmek istiyorum
refactor

299

Üzgünüm, ama kabul edilen cevabın aşırıya kaçması olduğunu düşünüyorum. Tek yapmanız gereken şudur:

public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
    public void OnException (ExceptionContext context)
    {
        // Log only handled exceptions, because all other will be caught by ELMAH anyway.
        if (context.ExceptionHandled)
            ErrorSignal.FromCurrentContext().Raise(context.Exception);
    }
}

ve Global.asax.cs dosyasına kaydedin (sipariş önemlidir):

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
    filters.Add(new ElmahHandledErrorLoggerFilter());
    filters.Add(new HandleErrorAttribute());
}

3
1 Çok güzel, genişletmek gerek HandleErrorAttribute, gerek geçersiz kılmak için OnExceptionüzerinde BaseController. Bu kabul edilen cevap için varsayalım.
CallMeLaNN

1
@bigb Sana mesela (istisna mesajında, vb şeyler eklemek için kendi istisna türü istisna kaydırmak zorunda düşünüyorum new UnhandledLoggedException(Exception thrown)şey ekler hangi Messageiade öncesinde.
Ivan Zlatev

23
Atif Aziz
ELMAH'ı yarattı

48
@jamiebarrow Bunu fark etmedim, ancak cevabı ~ 2 yaşında ve muhtemelen API, sorunun kullanım durumlarını daha kısa ve kendi kendine yeten bir şekilde desteklemek için basitleştirildi.
Ivan Zlatev

6
@Ivan Zlatev ElmahHandledErrorLoggerFilter()elmah sadece işlenmemiş hataları günlüğe kaydedemiyor, ama işlenemiyor. Söylediğiniz gibi filtreleri doğru sırada kaydettim, herhangi bir düşünce?
kuncevic.dev

14

NuGet'te Atif tarafından geliştirilmiş bir çözüm içeren bir ELMAH.MVC paketi ve ayrıca MVC yönlendirme içindeki elmah arayüzünü işleyen bir kontrolör (artık bu ekseni kullanmaya gerek yok)
Bu çözümle ilgili sorun ), bir veya başka bir elmah hata işleyicisinin, hatayı customError etiketi olarak veya ErrorHandler veya kendi hata işleyiciniz aracılığıyla ayarlamak isteyebileceğiniz şeyleri yoksayarak hatayı işlemesi
En iyi çözüm IMHO, diğer tüm filtrelerin sonunda hareket edecek ve zaten işlenmiş olayları günlüğe kaydedecek bir filtre oluşturmaktır. Elmah modülü, uygulama tarafından işlenmeyen diğer hataları kaybetmeye özen göstermelidir. Bu ayrıca sağlık olayını ve hata olaylarına bakmak için asp.net'e eklenebilecek diğer tüm modülleri kullanmanıza izin verir.

Bu görünümlü elmah.mvc içindeki ErrorHandler reflektör ile yazdım

public class ElmahMVCErrorFilter : IExceptionFilter
{
   private static ErrorFilterConfiguration _config;

   public void OnException(ExceptionContext context)
   {
       if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
       {
           var e = context.Exception;
           var context2 = context.HttpContext.ApplicationInstance.Context;
           //TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
           if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
           {
            _LogException(e, context2);
           }
       }
   }

   private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
   {
       if (_config == null)
       {
           _config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
       }
       var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
       return _config.Assertion.Test(context2);
   }

   private static void _LogException(System.Exception e, System.Web.HttpContext context)
   {
       ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
   }


   private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
   {
       var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
       if (signal == null)
       {
           return false;
       }
       signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
       return true;
   }
}

Şimdi, filtre yapılandırmanızda şöyle bir şey yapmak istiyorsunuz:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //These filters should go at the end of the pipeline, add all error handlers before
        filters.Add(new ElmahMVCErrorFilter());
    }

İnsanlara gerçekten istisnayı işleyecek küresel bir filtre eklemek istediklerinde, bu son filtreden ÖNCE gitmeleri gerektiğini hatırlatmak için bir yorum bıraktığımıza dikkat edin, aksi takdirde işlenmeyen istisnanın ElmahMVCErrorFilter tarafından yok sayılacağı duruma girersiniz. İşlenmedi ve Elmah modülü tarafından kilitlenmeli, ancak bir sonraki filtre istisnayı işlendi olarak işaretler ve modül onu yok sayar, istisna asla elmah haline gelmez.

Şimdi, webconfig'nizdeki elmah uygulamalarının aşağıdaki gibi olduğundan emin olun:

<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->

Burada önemli olan "elmah.mvc.disableHandleErrorFilter" dir, bu yanlışsa, elmaE.mvc içindeki customError ayarlarınızı yok sayacak olan varsayılan HandleErrorHandler'ı kullanarak istisnayı işleyecek işleyiciyi kullanır.

Bu kurulum, sınıflar ve görünümlerde kendi ErrorHandler etiketlerinizi ayarlamanıza izin verirken, bu hataları ElmahMVCErrorFilter aracılığıyla kaybederken, elmah modülü aracılığıyla web.config dosyasına özel bir Hata yapılandırması ekleyerek kendi Hata İşleyicilerinizi bile yazmanıza olanak tanır. Yapmanız gereken tek şey, yazdığımız elmah filtresinden önce hatayı işleyecek herhangi bir filtre eklememeyi unutmayın. Ve bahsetmeyi unuttum: elmah'da kopya yok.


7

Yukarıdaki kodu alabilir ve her denetleyiciye HandleErrorWithElmah özniteliğini enjekte eden özel bir denetleyici fabrikası tanıtarak bir adım daha ileri gidebilirsiniz.

Daha fazla bilgi için MVC'de oturum açma hakkındaki blog serime göz atın. İlk makale Elmah'ın MVC için kurulmasını ve çalıştırılmasını içermektedir.

Makalenin sonunda indirilebilir kodla ilgili bir bağlantı vardır. Umarım yardımcı olur.

http://dotnetdarren.wordpress.com/


6
Bana sadece bir baz denetleyici sınıfına yapışmak çok daha kolay olurdu gibi görünüyor!
Nathan Taylor

2
Günlük ve istisna işleme yukarıdaki Darren serisi iyi okumaya değer !!! Çok titiz!
Ryan Anderson

6

ASP.NET MVC'de yeniyim. Aynı sorunla karşı karşıya kaldım, aşağıdaki benim Erorr.vbhtml'de çalışılabilir (sadece Elmah günlüğünü kullanarak hatayı günlüğe kaydetmeniz gerekirse çalışır)

@ModelType System.Web.Mvc.HandleErrorInfo

    @Code
        ViewData("Title") = "Error"
        Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
        //To log error with Elmah
        Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
    End Code

<h2>
    Sorry, an error occurred while processing your request.<br />

    @item.ActionName<br />
    @item.ControllerName<br />
    @item.Exception.Message
</h2> 

Sadece!


Bu açık ara en basit çözüm. Özel işleyiciler ve şeyler yazmaya veya kaydetmeye gerek yoktur. Benim için iyi çalışıyor
ThiagoAlves

3
JSON / HTML olmayan yanıtlar için yok sayılır.
Craig Stuntz

6
ayrıca bu bir görünümde hizmet seviyesi işlevselliği yapıyor. Buraya ait değil.
Trevor de Koekkoek

6

Tamamen alternatif bir çözüm, HandleErrorAttributeMVC'yi kullanmamak ve bunun yerine Elmah'ın çalışmak üzere tasarlandığı ASP.Net hata işlemesine güvenmektir.

Varsayılan HandleErrorAttributeglobal'i App_Start \ FilterConfig'den (veya Global.asax) kaldırmanız ve ardından Web.config dosyasında bir hata sayfası ayarlamanız gerekir:

<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />

Bu bir MVC yönlendirilmiş URL olabilir, bu nedenle yukarıdaki ErrorController.Indexbir hata oluştuğunda eyleme yönlendirilir .


Bu şimdiye kadarki en basit çözüm ve varsayılan yönlendirme bir MVC eylemi olabilir :)
Jeremy Cook

3
Bu, JSON vb. Gibi diğer istek türleri için yeniden yönlendirir - iyi değil.
zvolkov

5

Benim için e-posta günlüğünün çalışmasını sağlamak çok önemliydi. Bir süre sonra bunun Atif örneğinde sadece 2 satır daha kod gerektirdiğini keşfettim.

public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
    static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();

    public override void OnException(ExceptionContext context)
    {
        error_mail_log.Init(HttpContext.Current.ApplicationInstance);
        [...]
    }
    [...]
}

Umarım bu birine yardımcı olur :)


2

MVC site yapılandırmam için tam da ihtiyacım olan şey bu!

Atif Aziz'in önerdiği gibi, OnExceptionbirden çok HandleErrorAttributeörneği ele alma yöntemine küçük bir değişiklik ekledim :

birden fazla HandleErrorAttributeörnek geçerliyse , yinelenen günlüğe kaydetmenin gerçekleşmemesine dikkat etmeniz gerekebileceğini unutmayın .

Sadece context.ExceptionHandledtemel sınıf çağırmadan önce kontrol , sadece başka birinin geçerli işleyiciden önce istisnayı ele alıp almadığını bilmek.
Benim için çalışıyor ve başka birinin ihtiyacı olması durumunda kod gönderirim ve kimse bir şey göz ardı edip etmediğini bilmek ister.

Umarım faydalıdır:

public override void OnException(ExceptionContext context)
{
    bool exceptionHandledByPreviousHandler = context.ExceptionHandled;

    base.OnException(context);

    Exception e = context.Exception;
    if (exceptionHandledByPreviousHandler
        || !context.ExceptionHandled  // if unhandled, will be logged anyhow
        || RaiseErrorSignal(e)        // prefer signaling, if possible
        || IsFiltered(context))       // filtered?
        return;

    LogException(e);
}

Base.OnException () çağrılırken "if" ifadesi görünmüyor. Ve (exceptionHandledByPreviousHandler ||! Context.ExceptionHandled || ...) birbirinizi iptal eder ve her zaman doğru olur. Bir şey mi kaçırıyorum?
joelvh

İlk önce akımdan önce çağrılan başka bir işleyicinin istisnayı yönetip yönetmediğini kontrol ederim ve sonucu değişkende saklıyorum: exceptionHandlerdByPreviousHandler. Sonra geçerli işleyiciye istisnanın kendisini yönetme şansı veriyorum: base.OnException (context).
ilmatte

İlk önce akımdan önce çağrılan başka bir işleyicinin istisnayı yönetip yönetmediğini kontrol ederim ve sonucu değişkende saklıyorum: exceptionHandlerdByPreviousHandler. Sonra geçerli işleyiciye istisnanın kendisini yönetme şansı veriyorum: base.OnException (context). Özel durum önceden yönetilmediyse şu olabilir: 1 - Geçerli işleyici tarafından yönetilir, o zaman: exceptionHandledByPreviousHandler = false ve! ​​Context.ExceptionHandled = false 2 - Geçerli işleyici tarafından yönetilmez ve: exceptionHandledByPreviousHandler = false ve! ​​Context. ExceptionHandled true. Yalnızca vaka 1 günlüğe kaydedilir.
ilmatte
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.