سفارشی کردن ELMAH برای جلوگیری از گزارش گیری خطاهای غیرضروری در MVC

جمعه 18 دی 1394

ELMAH یک ماژول رایگان و سورس باز برای لاگ کردن خطاهای مدیریت نشده برنامه‌های ASP.Net‌ است. ELMAH در واقع برای ثبت خطا هایی است که برنامه نویس در کد هایش از Exception Handling مناسبی برای متد ها ( مثل متد های بازگشتی داده از دیتابیس) استفاده نکرده است. در این مقاله می خواهیم ELMAH را برای جلوگیری از گزارش گیری خطاهای غیر ضروری سفارشی کنیم.

سفارشی کردن ELMAH برای جلوگیری از گزارش گیری خطاهای غیرضروری در MVC

ممکن است در استفاده از ELMAH به مشکلاتی برخورد کنیم که استفاده از آن را برای ما سخت می کند، مشکلاتی که رخ می دهد را در این مقاله مورد بررسی قرار می دهید. در استفاده مکرر از ELMAH  مشاهده کردیم که برای کامپوننت های  خارجی با مشکلاتی مواجه می شویم به عنوان مثال زمانی که خطایی اتفاق میافتد، ELMAH راه اندازی شده و خطای رخ داده شده را گزارش گیری می کند اما اگر شما صفحه را برای مثال 20 بار refresh کنید، ELMAH آن خطا را در چندین بار گزارش می گیرد و ثبت می کند، همه ما می دانیم که این زمانی در یک اپلیکیشن اتفاق میافتد که کاربر فکر می کند که اگر صفحه را refresh کند صفحه ممکن است کار کند، بنابراین چنین کاری می تواند تعداد زیادی رکورد غیر ضروری را در پایگاه داده شما ایجاد کند( فرض کنید که ما به یک پایگاه داده متصل هستیم) یا یکی دیگر مشکلات بالقوه زمانی می تواند اتفاق بیفتد که شما ELMAH را طوری تنظیم کرده اید که در صورت رخداد خطا یک ایمیل به خودتان ارسال کند اما زمانی که یک خطایی رخ می دهد، ممکن است با refresh کردن صفحه تعداد بالایی ایمیل اسپم شده به شما ارسال شود که این معظلی است که به خود تحمیل کرده اید و inbox شما با ایمیل های مرتبط با همان خطا پر می شود. این عمل می تواند به دلایل مختلفی رخ دهد، برای مثال یک روبات تلاش کند تا سایت شما را برای یک URL مشخص جستجو کند (مانند صفحه login ورد پرس)، و این روبات صدها صفحه را برای مشاهده اینکه شما چه چیزی دارید بشمرد و هر زمان تنها یک ELMAH راه اندازی می کند و خطای 404 می دهد، بنابراین شما 100 خطا را دریافت خواهید کرد.

در این مقاله می خواهیم نشان دهیم که چگونه می توانید ELMAH را سفارشی کرد تا از این نوع مشکلات جلوگیری کنیم، روش های متعددی وجود دارد اما در این مقاله از تعدادی nuget packages و ابزار استفاده می کنیم که  استفاده از ELMAH را آسان تر می سازد و توسط آنها ELMAH را به صورتی که می خواهیم محدود می کنیم.

نصب و پیکربندی ELMAH برای گزارش گیری خطاها در پایگاه داده و ارسال ایمیل

قبل از nugget package هایی مانند Elmah.MVC موجود، باید ELMAH dll را اضافه کرده و تنظیمات خود را انجام دهید. همچنین اگر بخواهید خطاهای خود را در پایگاه داده ثبت کنید  باید بسته Elmah.SqlServer.EFInitializer را به پروژه خود اضافه کنید، این بسته کلاس ElmahInitializer را به پروژه شما اضافه می کند و در این کلاس یک initializer برای ایجاد پایگاه داده لازم راه اندازی می شود، همه باید این کار را برای داشتن یک رشته اتصال انجام دهیم، شما رشته اتصال خود را در بخش ELMAH مربوط بهXMLمشخص می کنید:

<elmah>
      <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="myDb" />
</elmah>

اگر می خواهید یک ایمیل نیز ارسال شود، باید بخش تنظیمات mail را در web.Config  راه اندازی کنید،  Elmah به صورت اتوماتیک آن را انتخاب کرده و از آن برای ارسال ایمیل استفاده می کند:

<system.net>
      <mailSettings>
        <smtp from="Elmah self-spammer<info@site.com>">
          <network host="mail.site.com" port="25" userName="info@site.com"  
                   password="password" />
        </smtp>
      </mailSettings>
</system.net>

و در بخش Elmah از web.Config از گره زیر استفاده می شود تا نشان دهد که می خواهید از Elmah استفاده کنید تا خطاهای ثبت شده را به ایمیل شما ارسال کند:

<elmah>
      <errorMail from=" Elmah self-spammer<info@site.com>" to="admin@site.com" 
                 async="true" />
</elmah>

محدود کردن فرم Elmah برای جلوگیری از ارسال ایمیل های غیر ضروری و گزارش گیری خطاهای مشابه در پایگاه داده

جلوگیری از Elmah برای ارسال ایمیل در زمان رخ دادن خطاهای 404 

اولین چیزی که ممکن است بخواهید انجام دهید این است که Elmah را طوری تغییر دهید که خطاهای 404 را ثبت نکند، شما روزانه صدها هزار خطای 404 را دریافت خواهید کرد و اگر یک ایمیل برای هر یکی از آنها دریافت کنید، صندوق پیام شما منفجر خواهد شد، می توانید با اضافه کردن یک فیلتر به بخش Elmah در XML از ارسال این نوع پیام ها جلوگیری کنید:

<elmah>
      <security allowRemoteAccess="yes" />
 
      <errorFilter>
        <test>
          <and>
            <equal binding="HttpStatusCode" value="404" type="Int32" />
            <regex binding="FilterSourceType.Name" pattern="mail" />
          </and>
        </test>
      </errorFilter>
</elmah>

همچنین ممکن است بخواهید دسترسی به Elmah را برای کاربران احراز هویت شده با نقش ادمین محدود کنید. شما می توانید این کار را توسط  اضافه کردن کد زیر به بخش appSettings از web.Config خود انجام دهید:

<appSettings>
      <add key="elmah.mvc.disableHandler" value="false" />
      <add key="elmah.mvc.disableHandleErrorFilter" value="false" />
      <add key="elmah.mvc.requiresAuthentication" value="true" />
      <add key="elmah.mvc.IgnoreDefaultRoute" value="false" />
      <add key="elmah.mvc.allowedRoles" value="admin" />
      <add key="elmah.mvc.allowedUsers" value="*" />
      <add key="elmah.mvc.route" value="elmah" />
      <add key="elmah.mvc.UserAuthCaseSensitive" value="true" />
</appSettings>

 

جلوگیری ازگزارش گیری ELMAH برای چندین خطای مشابه در زمان های یکسان یا با referesh کردن صفحه

ELMAH به صورت پیش فرض برخی رویدادهایی را که توسط آنها می توانیم نحوه گزارش گیری خطا را  تغییر دهیم،ارائه می دهد، برخی از این رویدادها شامل ErrorLog_Filtering و ErrorMail_Filtering می باشد. ما می توانیم از این رویدادها در فایل global.asax استفاده کنیم، در اینجا می خواهیم با محدود کردن ELMAH فقط در صورتی خطا را ثبت کنیم که قبل از آن این خطای مشابهی رخ نداده باشد و یا این خطا بیش از 10 دقیقه پیش رخ داده باشد، همچنین ما خطاهایی را که از نوع HttpRequestValidationException یا FileNotFoundException باشد را  رد کرده و از ذخیره شده آنها در بانک اطلاعاتی یا ارسال ایمیل مربوط به آنها جلوگیری می کنیم:

private bool _sendEmail;
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
{
    using (var context = new ElmahContext())
    {
        //if the exception had the same message and was from
        //the last 10 min it means it's the same we dismiss it
        _sendEmail = true;

        var lastErr = context.ELMAH_Errors
                      .OrderByDescending(m => m.TimeUtc).Take(1)
                      .SingleOrDefault();

        if (lastErr != null &&
            (e.Exception.Message == lastErr.Message &&
            lastErr.TimeUtc > DateTime.UtcNow.AddMinutes(-10)))
        {
            e.Dismiss();
            _sendEmail = false;
        }
    }

    if (e.Exception.GetBaseException() is HttpRequestValidationException) e.Dismiss();
}

void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
{
    if (_sendEmail == false)
    {
        e.Dismiss();
    }

    if (e.Exception.GetBaseException() is FileNotFoundException) e.Dismiss();
}

حذف خطاهای قدیمی و غیر ضروری ELMAH از پایگاه داده برای صرفه جویی در فضای پایگاه داده

اگر بخواهید برخی کارها را هر بار برای هر خطای ثبت شده انجام دهید می توانید آن را در ErrorLog_Logged انجام دهید، در اینجا ما تعدادی کد ارائه کردیم که حذف هرگونه خطایی قدیمی تر از 60 روز در پایگاه داده ما را کنترل می کند:

void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
        {
            //keep the log form the last 60 days and delete the rest
            using (var context = new ElmahContext())
            {
                var baseLineDate = DateTime.UtcNow.AddDays(-60);
                var model = context.ELMAH_Errors.Where(p => p.TimeUtc < baseLineDate);
                foreach (var item in model)
                {
                    context.ELMAH_Errors.Remove(item);
                }
                context.SaveChanges();
            }
        }

روشی که در این مقاله برای سفارشی کردن ELMAH ارائه کردیم مطمئنا تنها روش نیست و راه حل های دیگری نیز برای انجام این کار وجود دارد که می توانید انجام دهید.

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید