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

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

علاوه بر لاگ کردن خطا ها شما می توانید تمامی اتفاقاتی که در وب سایت می افتد را لاگ کنید ، از ورود کاربر گرفته تا هر عملی که توسط کاربر انجام می شود ، این اطلاعات به مدیر سایت کمک زیادی خواهد کرد . قبل از هرچیز بد نیست بدانید که به هیچ وجه نباید متن خطا را به کاربران سایت نمایش دهید ، این یکی از مهمترین اصول امنیت در وب سایت شماست ، دلیل آن هم این است که ممکن است در متن خطا اطلاعاتی باشد که به وسیله این اطلاعات سایت شما به راحتی هک شود . بنابراین پس از بارگزاری پروژه و نهایی سازی آن ابتدا خاصیت mode تگ CustomeErrors را برابر Off یا RemoteOnly قرار دهید : view source print? 1.<customErrors mode=”Off”></customErrors> و خاصیت debug تگ Compilation را نیز false نمایید : view source print? 1.<compilation debug=”false”></compilation> سناریو کار پس از بروز یک خطا به این صورت است که ابتدا متن خطا را لاگ (Log) کرده و سپس کاربر را به یک صفحه Html هدایت می کنیم ، در این صفحه شما می توانید از کاربر عذر خواهی کنید و اطمینان دهید که متن خطا برای مدیران سایت ارسال شده و در اولین فرصت خطا بر طرف خواهد شد . لاگ نمودن خطا در دو حالت قابل انجام است : در فایل Global.asax در بلاک Try , Catch در این دو حالت متن خطا ذخیره شده و بسته به سیاست شما پروژه به کار خود ادامه داده یا به یک صفحه پیغام هدایت می شود . نکته اساسی نحوه ذخیره لاگ می باشد ، بسیاری از برنامه نویسان لاگ ها را در فایل های متنی ذخیره می کنند ولی تجربه به بنده نشان داده که بهترین محل برای ذخیره لاگ دیتابیس می باشد . پیشنهاد می کنم یک دیتابیس جداگانه برای لاگ ایجاد کنید ، دلیل این امر این است که پس از مرور زمان این دیتابیس بزرگ شده و جابجایی و بکاپ گرفتن از آن مشکل خواهد شد بنابراین در صورتی که جدول لاگ جزئی از دیتابیس اصلی پروژه شما باشد به مشکل بزرگی بر می خورید یکی از مهمترین مضایای استفاده از دیتابیس قابلیت Query گرفتن از آن می باشد ، شما براحتی با یک QUery ساده می توانید خطا ها را با یک شرط خاص بدست آورید ، یا تمام کارهایی که یک کاربر انجام داده است را لیست نمایید . یک دیتابیس به نام Log ساخته و جدول TBLLogs را در آن ایجاد کنید : view source print? 01.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 : کد کاربری می باشد ، در صورت پروژه شما سیستم ورود و تشخیص هویت داشته باشد کد کاربری را در آن ذخیره می کنید در غیر این صورت آن را ۰ وارد کنید

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

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

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

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

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

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

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

AddDate : تاریخ و زمان درج رکورد هم در این فیلد ذخیره خواهد شد و همچنین یک Stored Procedure برای درج رکورد به صورت زیر ایجاد نمایید : view source

print? 01.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 در آن به صورت زیر ایجاد می کنیم :

view source

print?

 

۰۱٫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) در وبکانفیگ ذخیره شده است :

view source

print? 1.<connectionStrings>

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

۳٫</connectionStrings> و مقدار MailErrors برای فعال و غیر فعال کردن ارسال ایمیل :

view source

print? 1.<appSettings>

۲٫ <add key=”MailErrors” value=”false” />

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

view source

print? 1.void Application_Error(object sender, EventArgs e)

۲٫{

۳٫ Logger.AddNewLog(Logger.LogType.ErrorLog, sender.ToString(), “Error”, Server.GetLastError().Message);

۴٫ Response.Redirect(“Error.htm”);

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

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

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

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

view source

print? 1.<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” /> و رویداد هر کدام را هندل نمودیم :

view source

print?

 

۰۱٫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 هدایت نشد .