HOW 2 LEARN ASP.NET
نویسنده : delshad
تاریخ  : ۱۳۸۸/۷/۱۷
امتیاز :
بازدید : 2755
موضوع : لاگ کردن رویداد ها و مدیریت خطاها در Asp.NET
توضیحات : در این مقاله روشی جهت Log نمودنError ها و Event ها ارائه نموده ام
     

لاگ کردن رویداد ها و مدیریت خطاها در Asp.NET

اگر شما هم یک برنامه نویس وب هستید چه تازه کار و چه حرفه ای با خطاهای مختلفی روبرو خواهید شد ، شما ناگزیر هستید این خطا ها را مدیریت کنید در غیر این صورت حیات  پروژه وب شما در خطر خواهد افتاد .

روش های متعددی برای مدیریت خطا وجود دارد و برنامه نویسان به اقتضای سلیقه و پروژه روش های مختلفی را پیاده سازی می کنند ، بنده در این مقاله یک روش مدیریت خطا را که در چند پروژه مهم از آن استفاده کردم ارائه خواهم داد ، این روش برای پروژه های بزرگ و حجم زیاد داده به خوبی پاسخگو خواهد بود .

علاوه بر لاگ کردن خطا ها شما می توانید تمامی اتفاقاتی که در وب سایت می افتد را لاگ کنید ، از ورود کاربر گرفته تا هر عملی که توسط کاربر انجام می شود ، این اطلاعات به مدیر سایت کمک زیادی خواهد کرد .

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

بنابراین پس از بارگزاری پروژه و نهایی سازی آن ابتدا خاصیت mode تگ CustomeErrors را برابر Off یا RemoteOnly قرار دهید :

<customErrors mode="Off"></customErrors>

و خاصیت debug تگ Compilation را نیز false نمایید :

<compilation debug="false"></compilation>

سناریو کار پس از بروز یک خطا به این صورت است که ابتدا متن خطا را لاگ (Log) کرده و سپس کاربر را به یک صفحه Html هدایت می کنیم ، در این صفحه شما می توانید از کاربر عذر خواهی کنید و اطمینان دهید که متن خطا برای مدیران سایت ارسال شده و در اولین فرصت خطا بر طرف خواهد شد .

لاگ نمودن خطا در دو حالت قابل انجام است :

  1. در فایل Global.asax
  2. در بلاک Try , Catch

در این دو حالت متن خطا ذخیره شده و بسته به سیاست شما پروژه به کار خود ادامه داده یا به یک صفحه پیغام هدایت می شود .

نکته اساسی نحوه ذخیره لاگ می باشد ، بسیاری از برنامه نویسان لاگ ها را در فایل های متنی ذخیره می کنند ولی تجربه به بنده نشان داده که بهترین محل برای ذخیره لاگ دیتابیس می باشد .

پیشنهاد می کنم یک دیتابیس جداگانه برای لاگ ایجاد کنید ، دلیل این امر این است که پس از مرور زمان این دیتابیس بزرگ شده و جابجایی و بکاپ گرفتن از آن مشکل خواهد شد بنابراین در صورتی که جدول لاگ جزئی از دیتابیس اصلی پروژه شما باشد به مشکل بزرگی بر می خورید .

یکی از مهمترین مضایای استفاده از دیتابیس قابلیت Query گرفتن از آن می باشد ، شما براحتی با یک QUery ساده می توانید خطا ها را با یک شرط خاص بدست آورید ، یا تمام کارهایی که یک کاربر انجام داده است را لیست نمایید .

یک دیتابیس به نام Log ساخته و جدول TBLLogs را در آن ایجاد کنید :

CREATE TABLE [dbo].[TBLLogs](
	[LogID] [bigint] IDENTITY(1,1) NOT NULL,
	[UserID] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[LogType] [tinyint] NOT NULL,
	[LogSource] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[LogName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[LogText] [nvarchar](max) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[URL] [nvarchar](500) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[IPAddress] [nvarchar](15) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[Browser] [nvarchar](15) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[AddDate] [datetime] NOT NULL CONSTRAINT [DF_TBLLogs_AddDate]  DEFAULT (getdate()),
 CONSTRAINT [PK_TBLLogs] PRIMARY KEY CLUSTERED 
(
	[LogID] DESC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

توضیح فیلد ها :

LogID : کد واحد خطا که به صورت اتوماتیک ایجاد شده و فیلد کلید جدول می باشد

UserID : کد کاربری می باشد ، در صورت پروژه شما سیستم ورود و تشخیص هویت داشته باشد کد کاربری را در آن ذخیره می کنید در غیر این صورت آن را 0 وارد کنید

LogType : نوع لاگ را مشخص می کند : 1- لاگ رویداد ها 2- لاگ خطاها

LogSource : منبع رخداد خطا را در این فیلد ذخیره می کنیم ، منبع رخداد می تواند ساب روتین مربوط یا حتی نام صفحه یا ماژول باشد

LogName : در صورتی که لاگ رویداد ها را ذخیره می کنید نام لاگ برای شما بسیار مفید خواهد بود مثلا "AddNewUser" یک نام برای لاگ درج یک کاربر جدید است

LogText : متن کامل لاگ یا متن کامل خطا در این فیل ذخیره می شود

URL : آدرس صفحه در این فیلد ذخیره می شود تا بعدا متوجه شوید که در کدام صفحه این اتفاق افتاده است

IPAddress: آیپی کاربر در این فیل ذخیره می شود

Browser : نوع مرورگر کاربر در این فیلد ذخیره می شود

AddDate : تاریخ و زمان درج رکورد هم در این فیلد ذخیره خواهد شد

 

و همچنین یک Stored Procedure برای درج رکورد به صورت زیر ایجاد نمایید :

CREATE PROCEDURE [dbo].[AddNewLog]
(
	@UserID nvarchar(50),
	@LogType tinyint,
	@LogSource nvarchar(50),
	@LogName nvarchar(50),
	@URL nvarchar(500),
	@LogText nvarchar(MAX),
	@IPAddress nvarchar(15),
	@Browser nvarchar(15)
)
AS
	SET NOCOUNT OFF;
INSERT INTO TBLLogs
                      (UserID, LogType, LogSource, LogName, URL, LogText,IPAddress,Browser)
VALUES     (@UserID,@LogType,@LogSource,@LogName,@URL,@LogText,@IPAddress,@Browser)

کار ما با بانک اطلاعاتی تقریبا به پایان رسیده ، به سراغ پروژه رفته و یک کلاس به نام Logger در آن به صورت زیر ایجاد می کنیم :

using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;
using System.Net.Mail;
using System.Net;

public class Logger
{
    public enum LogType
    {
        EventLog = 1,
        ErrorLog = 2
    }

    public static void AddNewLog(LogType logType, string logSource, string logName, string logText)
    {
        HttpContext currentContext = HttpContext.Current;
        string userId = "0";
        string url = currentContext.Request.Url.PathAndQuery.ToString();
        string ipAddress = currentContext.Request.UserHostAddress;
        string browser = currentContext.Request.Browser.Browser + " " + HttpContext.Current.Request.Browser.Version;

        bool maiErrors = Convert.ToBoolean(ConfigurationManager.AppSettings["MailErrors"]);


        if (currentContext.User.Identity.IsAuthenticated)
            userId = currentContext.User.Identity.Name;

        using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["LogConStr"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand("AddNewLog", con);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            cmd.Parameters.AddWithValue("@UserID", userId);
            cmd.Parameters.AddWithValue("@LogType", logType);
            cmd.Parameters.AddWithValue("@LogSource", logSource);
            cmd.Parameters.AddWithValue("@LogName", logName);
            cmd.Parameters.AddWithValue("@URL", url);
            cmd.Parameters.AddWithValue("@LogText", logText);
            cmd.Parameters.AddWithValue("@IPAddress", ipAddress);
            cmd.Parameters.AddWithValue("@Browser", browser);

            try
            {
                con.Open();
                cmd.ExecuteNonQuery();
            }
            finally
            {

                con.Close();

            }
        }

        if (logType == LogType.ErrorLog && maiErrors)
        {
            SendMail(logName, string.Format("UserID:{0} <br> LogSource:{1} <br> LogName:{2} <br> Url:{3} <br> LogText:{4} <br> IpAddress:{5} <br> Browser:{6}",
                userId, logSource, logName, url, logText, ipAddress, browser));
        }


    }
    private static void SendMail(string subject, string body)
    {
        SmtpClient MyMail = new SmtpClient();
        MailMessage MyMsg = new MailMessage();

        MyMail.Host = "mail.yourdomain.com";

        MyMsg.To.Add(new MailAddress("ReceiverEmailAddres"));
        MyMsg.Subject = subject;
        MyMsg.SubjectEncoding = Encoding.UTF8;

        MyMsg.IsBodyHtml = true;
        MyMsg.From = new MailAddress("SenderEmailAddresss");
        MyMsg.BodyEncoding = Encoding.UTF8;
        MyMsg.Body = body;

        MyMail.UseDefaultCredentials = false;
        NetworkCredential MyCredentials = new NetworkCredential("SenderEmailAddress", "SenderEmailPassword");
        MyMail.Credentials = MyCredentials;
        try
        {
            MyMail.Send(MyMsg);
        }
        catch
        {

        }

    }
}

کلاس فوق از دو متد ساده تشکیل شده است

متد AddNewLog وظیفه ایجاد اطلاعات و ذخیره آن در بانک را انجام می دهد ، در صورتی که مقدار MailErrors در وبکانفیگ true باشد متن خطا به صورت ایمیل هم ارسال خواهد شد .

دقت نمایید که رشته اتصال (ConnectionString) در وبکانفیگ ذخیره شده است :

	<connectionStrings>
		<add connectionString="Data Source=(local);Initial Catalog=Log;Integrated Security=True" name="LogConStr" providerName="System.Data.SqlClient" />
	</connectionStrings>

و مقدار MailErrors برای فعال و غیر فعال کردن ارسال ایمیل :

	<appSettings>
		<add key="MailErrors" value="false" />
	</appSettings>

 

اکنون شما در هر جایی می توانید از این متد استفاده نمایید ، اولین نمونه استفاده آن ذخیره لاگ خطا ها در Global.asax  می باشد ،اگر در پروژه شما فایل این فایل وجود ندارد آن را از طریق Add new Item و سپس  Global Application class انتخاب و به پروژه اضافه نمایید ، سپس در رویداد Application_Error از متد فوق به صورت زیر استفاده می کنیم :

    void Application_Error(object sender, EventArgs e)
    {
        Logger.AddNewLog(Logger.LogType.ErrorLog, sender.ToString(), "Error", Server.GetLastError().Message);
        Response.Redirect("Error.htm");
    }

 همانطور که ملاحظه می کنید پس از لاگ کردن خطا کاربر به صفحه Error.html هدایت خواهد شد .

برای بیشتر روشن شدن مطلب به مثال زیر توجه کنید :

یک صفحه تستی به پروژه اضافه کردیم .

4 باتن بر روی صفحه قرار دادیم :

    <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button1" />
    &nbsp;<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Button2" />
    &nbsp;<asp:Button ID="Button3" runat="server" OnClick="Button3_Click" Text="Error Button" />
    &nbsp;<asp:Button ID="Button4" runat="server" OnClick="Button4_Click" Text="Catch Button" />

و رویداد هر کدام را هندل نمودیم :

  protected void Button1_Click(object sender, EventArgs e)
    {
        // Log event
        Logger.AddNewLog(Logger.LogType.EventLog, sender.ToString(), "Button1", "Button 1 Was Clicked!");
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        // Log event
        Logger.AddNewLog(Logger.LogType.EventLog, sender.ToString(), "Button2", "Button 2 Was Clicked!");
    }
    protected void Button3_Click(object sender, EventArgs e)
    {
        // It Makes Error 
       string test = Request["test"].ToString();
    }
    protected void Button4_Click(object sender, EventArgs e)
    {
        try
        {
            object test = null;
            Response.Write(test.ToString());

            // Log event
            Logger.AddNewLog(Logger.LogType.ErrorLog, sender.ToString(), "Button4", "Button 4 Was Clicked!");
        }
        catch (Exception exp)
        {
            // Log error
            Logger.AddNewLog(Logger.LogType.ErrorLog, sender.ToString(), "Button4", exp.Message);
        }
    }

در رویداد مربوط به کلیک شدن Button1 و Button2 فقط کلیک شدن آن لاگ شده است ، در رویداد مربوط به کلیک شدن Button3 یک کد اشتباه که باعث بروز Null Exception می شود نوشتیم ، اگر بر روی این باتن کلیک کنید سیستم شما را به صفحه Error.html هدایت کرده و لاگ خطا ذخیره خواهد شد

به همین ترتیب در مورد Button4 نیز یک کد که باعث بروز خطا می شود نوشتیم ولی در این بار با استفاده از بلاک try و catch خودمان خطا را هندل کرده و به صورت دلخواه لاگ کردیم ، با این روش کاربر به صفحه Error.html هدایت نشد .

فایل کامل مقاله را میتوانید از این لینک دانلود نمایید

 


 

 

 
امتیاز بدهید :

نویسنده : پ‍ژمان p.roudkhaneei@gmail.com ۱۳۸۹ چهاردهم خرداد
دست گلت درد نكنه دوست عزيز ، بوس ...
نویسنده : Error Ddd.Error@Gmail.com ۱۳۸۹ سي فروردين
سلام ممنون و سپاسگذارم
نویسنده : زکیه madadrangy@yahoo.com ۱۳۸۸ بيست اسفند
سلام. خیلی خوب و روان توضیح دادید. امیدوارم استفاده از این کدها هم به همین سادگی باشه. چون خیلی کاربردیه. ممنون واقعا
نویسنده : مصطفي LoyalPhoenix@yahoo.com ۱۳۸۸ هجدهم دي
كاشكي زودتر اين مقاله رو ديده بودم . يكي هفته اي وقت گذاشته بودم تا چيزي شبيه به اين رو درست كرده بودم ولي نه به كاملي اين . مقاله خيلي خوبي بود . ادامه بده
نویسنده : محمد رحمانی mamadrahmani@yahoo.com ۱۳۸۸ دوم آذر
سلام به نظر میرسه در اولین کدی که نوشتید کد زیر درست باشد:
نویسنده : بهروز راد 1@1.com ۱۳۸۸ نوزدهم مهر
فراخوانی Server.ClearError رو بعد از Server.GetLastError فراموش کردی.

 
نظر بدهید :
لطفا سوالات فنی خود را در تالار گفتگو www.forum.how2learnasp.net مطرح نمایید
نام :  
ایمیل :    
نظرات :
 
Xml rss feed

 

Valid CSS! Atom rss feed
Ali Delshad Official Site