Nine MVP's Blog

21/04/2012

Application Design : Logging 2

Filed under: Core System — Tags: , — Nine MVP @ 6:58 pm

ตอนที่แล้ว
https://nine69.wordpress.com/2012/03/15/application-design-logging-1/

ในตอนนี้ขอลงรายละเอียดเรื่องราวทั้งหมดนี้ครับ

  1. การใช้งาน NLog เบื้องต้นกับ .NET Application
  2. การ config Alternate Source
  3. แนวคิดการเก็บ Audit Data ของข้อมูลในระบบ

ต่อไปสำหรับการเก็บ Log ของการทำงานใน Application นั้น จะขอแนะนำการใช้งาน

NLog Framework

มาทำความรู้จักเจ้า NLog กัน  โดย NLog เป็น Logging Framework ที่มีความสามารถและประสิทธิภาพในการทำงานที่อยู่ในระดับที่ดี ซึ่งสามารถทำการบันทึก log ได้หลากหลายชนิด บันทึก log พร้อมกันได้หลายชนิดในรูปแบบข้อมูลที่สามารถกำหนดให้แตกต่างกันได้ เพียงแค่ทำการ config เท่านั้นหรือจะเขียนโปรแกรมกำหนดก็ได้  สามารถกำหนด buffer ของการเขียน  ทำการบันทึกแบบ async ทำ load balancing และรองรับ Failover  รวมไปถึงสามารถนำไปใช้กับ .NET/ SliverLight / Windows Phone/ Mono ได้อีกด้วย 

Target Source ที่รองรับ

  • Files – บันทึกลงไฟล์เดียวหรือจะหลาย file, กำหนดให้ rename หรือจะ zip ด้วยก็ได้ แถมยังกำหนด size ของ file เพื่อให้ split file ออกตามขนาดที่กำหนดก็ได้
  • Event Log – สามารถบันทึกแบบ local และ remote ก็ได้
  • Database – เก็บ log ลง database
  • Network – ยิง log ออกไปด้วย protocol ชนิดต่าง ๆ ไม่ว่าจะ TCP, UDP, SOAP, MSMQ เป็นต้น
  • Command-line- สามารถ show log ออก console app ได้ด้วยสีสันต่าง ๆ
  • E-Mail – กำหนดให้ส่ง email ออกไปตาม config
  • ASP.NET trace – บันทึกเข้าไว้ใน trace ของ asp.net

เริ่มต้นใช้งาน NLog เบื้องต้น

    สำหรับหัวข้อต่อไปต้องใช้ Software ต่อไปนี้

  1. Visual Studio 2005 ขึ้นไป (บทความใช้ VS2010)
  2. NuGet  (download here)
  3. MS SQL
  4. Internet

เปิด Visual Studio ขึ้นมาแล้วทำการสร้าง Console Project ตามภาพ

image

เสร็จแล้วจะได้โปรเจ็คตามภาพด้านล่างนี้

image

ต่อไปให้ทำการเรียก Package Manager Console ขึ้นมาเพื่อทำการติดตั้งตัว NLog ในโปรแกรมของเรา

image

หลังจากได้ Package Manager Console Window มาแล้วก็ทำการรันคำสั่งด้านล่างนี้

PM> Install-Package NLog

ก็จะได้ output ต่อท้ายว่า

Successfully installed ‘NLog 2.0.0.2000’.

Successfully added ‘NLog 2.0.0.2000’ to NLog01.

จากนั้นให้ติดตั้งตั้ว Schema ของ NLog เพิ่มดังนี้

PM> Install-Package NLog.Schema

ก็จะได้ output ต่อท้ายว่า

Attempting to resolve dependency ‘NLog.Config (≥ 2.0.0.0)’.

Attempting to resolve dependency ‘NLog (≥ 2.0.0.2000)’.

Successfully installed ‘NLog.Config 2.0.0.2000’.

Successfully installed ‘NLog.Schema 2.0.0.2000’.

Successfully added ‘NLog.Config 2.0.0.2000’ to NLog01.

    เข้าไปดูใน Project เพื่อตรวจสอบจะต้องได้ภาพตามนี้

    image

    จากนั้นเปิด file NLog.config มาแก้ไขตามนี้

image

อธิบาย config

1: เปลี่ยนไปตาม .NET Framwork version ที่ใช้งานในโปรเจ็ค

2: ทำการใช้งาน target source แบบ File โดยกำหนด option

– fileName :  ให้เก็บ log ไว้ที่เดียวกับที่โปรแกรมอยู่ และสร้าง log folder ขึ้นถ้่าหากไม่มี /  ตามด้วยชื่อไฟล์ โดยเอา date format ตามด้วย .log

– layout :  เมื่อจะเขียน log ทุก level จะต้องเขียนด้วย format ของ วันที่ LEVEL และตามด้วย ข้อความของ Log ที่จะบันทึก

3: บอกตัว log ให้ทำการบันทึกลง Target Source ใดๆบ้าง

– minlevel :  กำหนดไว้ต่ำสุดที่ Trace Level

– writeTo :  อ้างไปยัง target source ที่ชื่อว่า file (ในข้อ2) 

    จากนั้นให้เปิด Program.cs ขึ้นมาเพื่อแก้ไข Code ตามตารางด้านล่างนี้

    Code Snippet
    1. using System;
    2. using NLog;
    3.  
    4. namespace NLog01
    5. {
    6.     public class Program
    7.     {
    8.         //  NLog instance
    9.         private static Logger logger = LogManager.GetCurrentClassLogger();
    10.  
    11.         private static void Main(string[] args)
    12.         {
    13.             TestLog(); //  method  test
    14.             Console.ReadLine();
    15.         }
    16.  
    17.         public static void TestLog()
    18.         {
    19.             logger.Trace("Test trace message");
    20.             logger.Debug("Test debug message");
    21.             logger.Info("Test informational message");
    22.             logger.Warn("Test warning message");
    23.             logger.Error("Test error message");
    24.             logger.Fatal("Test fatal error message");
    25.             //  Log() method  Level
    26.             logger.Log(LogLevel.Info, "Test fatal error message");
    27.         }
    28.     }
    29. }

    อธิบาย code

    1. line2: เรียกใช้ namespace ของ NLog

    2. line17-27:  เป็นการทดสอบเขียน Log ใน Level ต่าง ๆ  รวมไปถึงการกำหนด level ผ่าน Log() method ได้เอง

    3. line13:  เรียกใช้ TestLog() ที่ได้เขียนไว้

      จากนั้นก็ทดสอบ run โดยกด F5 ก็จะได้หน้าต่างดำๆ ค้างนิ่งอยู่ตามภาพ และกด Enter หรือ Key อื่น ๆ ลงไปเพื่อจบโปรแกรม

      image

      กลับมาที่ Visual Studio แล้วไปที่ Solution Explorer แล้วกดตามลำดับหมายเลขในรูปด้านล่าง

      image

    4. เลือกโปรเจ็ค
    5. กดเพื่อดู file/folder ทั้งหมดในโปรเจ็ค
    6. กดเข้าไปใน bin –> Debug –> logs –> จะพบ file log เกิดขึ้นใน folder ดังกล่าว
    7. เปิด file log ขึ้นมาเพื่อดูผลลัพธ์ ก็จะพบ log message ที่เราได้สั่งบันทึกไปตามรูปด้านล่าง image
    8. จากนั้นหากเราจะกำหนดให้ File Target จำกัดขนาดของ log file และ split ออกตามขนาดเพื่อความง่ายในการเปิดมาอ่าน โดยเปิดให้เพิ่ม config เข้าไปตามนี้
      Code Snippet
      1. <?xml version="1.0" encoding="utf-8" ?>
      2. <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
      3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
      4.      <!–See http://nlog-project.org/wiki/Configuration_file   
      5.   for information on customizing logging rules and outputs.   –>
      6.   <targets>
      7.     <!– add your targets here –>
      8.     <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
      9.             layout="${longdate} ${uppercase:${level}} ${message}"
      10.             archiveEvery="Day"
      11.             archiveAboveSize="10000"
      12.             archiveNumbering="Sequence" />
      13.   </targets>
      14.   <rules>    
      15.     <!– add your logging rules here –>
      16.     <logger name="*" minlevel="Trace" writeTo="file" />
      17.   </rules>
      18. </nlog>

    9.  เสร็จแล้วไปปรับ Code ใน Program.cs มาแก้ไข TestLog() ใหม่ตามนี้ เพื่อจำลองสร้าง log file ขนาดใหญ่

    Code Snippet
    1. public static void TestLog()
    2. {
    3.     for (int i = 0; i < 500; i++)
    4.     {
    5.         logger.Trace("Test trace message");
    6.         logger.Debug("Test debug message");
    7.         logger.Info("Test informational message");
    8.         logger.Warn("Test warning message");
    9.         logger.Error("Test error message");
    10.         logger.Fatal("Test fatal error message");
    11.         //  Log() method  Level
    12.         logger.Log(LogLevel.Info, "Test fatal error message");
    13.     }
    14. }

    -10.  ทดลองรันโปรแกรมดูด้วยการกด F5 แล้วกลับไปดูใน Log Folder จะพบ log file หลากตัวเกิดขึ้นเนื่องจากเรากำหนด size ของไฟล์ไว้นั้นเอง

    image

    และเราก็ยังสามารถ config เพิ่มเติมได้อีกมากมายสามารถเข้าไปอ่านรายละเอียดได้ที่นี่ http://nlog-project.org/wiki/File_target

    การเก็บ log ใน Event Log ของ Windows

    ต่อไปเราจะเก็บ log ลงไปใน Event Log มีขั้นตอนดังนี้    

    1.  ไปที่ Solution Explorer และเปิด NLog.config ขึ้นมาเพื่อแก้ไข

    2. เพิ่ม config เข้าไปตามตารางด้านล่าง

    Code Snippet
    1. <?xml version="1.0" encoding="utf-8" ?>
    2. <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
    3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
    4.      <!–See http://nlog-project.org/wiki/Configuration_file   
    5.   for information on customizing logging rules and outputs.   –>
    6.   <targets>
    7.     <!– add your targets here –>
    8.     <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
    9.             layout="${longdate} ${uppercase:${level}} ${message}"
    10.             archiveEvery="Day"
    11.             archiveAboveSize="10000"
    12.             archiveNumbering="Sequence" />
    13.     <target xsi:type="EventLog" name="evl" layout=" ${longdate}|${level:uppercase=true}|${logger}|${message}"
    14.             machineName="NINE-WNB"
    15.             source="NLog01" eventId="9001" log="Application" />
    16.   </targets>
    17.   <rules>    
    18.     <!– add your logging rules here –>
    19.     <logger name="*" minlevel="Trace" writeTo="file" />
    20.     <logger name="*" minlevel="Trace" writeTo="evl" />
    21.   </rules>
    22. </nlog>

      line13-15:  เป็นการเพิ่ม event log target เข้าไป

      line14: อย่าลิมแก้เป็นชื่อ computer name ของตัวเองนะครับ

      3.  จากนั้นกลับไปแก้ file Program.cs ใน method TestLog() โดยลบ For Loop ออกตามด้านล่างนี้

      Code Snippet
      1. public static void TestLog()
      2. {
      3.     logger.Trace("Test trace message");
      4.     logger.Debug("Test debug message");
      5.     logger.Info("Test informational message");
      6.     logger.Warn("Test warning message");
      7.     logger.Error("Test error message");
      8.     logger.Fatal("Test fatal error message");
      9.     // custom log level
      10.     logger.Log(LogLevel.Info, "Test fatal error message");
      11. }

      4.  และทำการ กด F5 เพื่อรันโปรแกรมทดสอบ กด Enter เพื่อจบโปรแกรม

      5.. เสร็จแล้วไปเปิด Start –> Administrative Tools –> Event Viewer เพื่อดูผลก็จะพบ log ที่เราสั่งบันทึกไปตามรูปด้านล่าง

      image

    การเก็บ log ลงใน Database

    เริ่มต้นด้วยการสร้าง Database ใน MS SQL ชื่อว่า Logging

    จากนั้นนำ script ไปสร้าง Table ใน Database ด้วย script ด้านล่างนี้

    Code Snippet
    1. CREATE TABLE [dbo].[MyLog](
    2.     [Id] [bigint] IDENTITY(1,1) NOT NULL,
    3.     [CreateDate] [varchar](50) NULL,
    4.     [Origin] [nvarchar](50) NULL,
    5.     [Level] [varchar](50) NULL,
    6.     [Message] [nvarchar](1000) NULL,
    7.     [Exception] [nvarchar](max) NULL,
    8.     [StackTrace] [nvarchar](4000) NULL,
    9.     [Module] [varchar](100) NULL,
    10.     [User] [nvarchar](200) NULL,
    11. CONSTRAINT [PK_MyLog] PRIMARY KEY CLUSTERED
    12. (
    13.     [Id] ASC
    14.   )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS= ON) ON [PRIMARY]
    15. ) ON [PRIMARY]
    16.  
    17. GO

    เมื่อสร้าง Database และ Table ตาม script ตามข้างต้นเสร็จแล้ว

    กลับมาที่ Visual Studio ไปเปิดไฟล์ NLog.config ขึ้นมาเพื่มค่าตามตารางด้านล่างนี้

    Code Snippet
    1. <?xml version="1.0" encoding="utf-8" ?>
    2. <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
    3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
    4.      <!–See http://nlog-project.org/wiki/Configuration_file   
    5.   for information on customizing logging rules and outputs.   –>
    6.   <targets>
    7.     <!– add your targets here –>
    8.     <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
    9.             layout="${longdate} ${uppercase:${level}} ${message}"
    10.               archiveEvery="Day"archiveAboveSize="10000" archiveNumbering="Sequence" />
    11.     <target xsi:type="EventLog" name="evl" layout=" ${longdate}|${level:uppercase=true}|${logger}|${message}"
    12.             machineName="NINE-WNB" source="NLog01" eventId="9001" log="Application" />
    13.     <target xsi:type="Database" name="db" keepConnection="false"
    14.             useTransactions="true"
    15.             connectionString="Data Source=NINE-WNB\MSSQL2K8R2;Initial Catalog=LoggingDB;Integrated Security=True">
    16.       <commandText>
    17.         insert into MyLog ([CreateDate], [Origin], [Level], [Message], [Exception], [StackTrace], [Module], [User]) values (@createDate, @origin, @logLevel, @message, @exception, @stackTrace, @module, @user);
    18.       </commandText>
    19.       <parameter name="@createDate" layout="${longdate}"/>
    20.       <parameter name="@origin" layout="${callsite}"/>
    21.       <parameter name="@logLevel" layout="${level}"/>
    22.       <parameter name="@message" layout="${message}"/>
    23.       <parameter name="@exception" layout="${exception:format=Message,StackTrace}"/>
    24.       <parameter name="@stackTrace" layout="${stacktrace}"/>
    25.       <parameter name="@module" layout="${mdc:item=module}"/>
    26.       <parameter name="@user" layout="${mdc:item=user}"/>
    27.     </target>
    28.     
    29.   </targets>
    30.   <rules>    
    31.     <!– add your logging rules here –>
    32.     <logger name="*" minlevel="Trace" writeTo="file" />
    33.     <logger name="*" minlevel="Trace" writeTo="evl" />
    34.     <logger name="*" minlevel="Trace" writeTo="db" />
    35.   </rules>
    36. </nlog>

    อธิบาย config

    1: line3: ตรง option internalLogFile จะเป็นการบันทึกหากเกิด error จากการทำงานของ NLog

    2: line13-29:  จะเป็นการเพิ่ม database target เข้ามาใน NLog โดยค่า default จะเป็น driver ของ MSSQL

    3: line15: ให้แก้เป็น connection string ของเครื่องตัวเองครับ

    4: line25,26: เป็นการอ้างถึงค่าเพิ่มเติมที่ต้องการจะส่งมาจากโปรแกรมเพื่อบันทึกลง log table

    5: line 34: เป็นการสั่งให้ NLog ทำการบันทึกลง Target ที่ชื่อ db

     

    ต่อไปเปิดไฟล์ Program.cs เพื่อแก้ไขโค้ดตามตารางด้านล่างนี้

    Code Snippet
    1. public static void TestLog()
    2. {
    3.     //Assign variable value
    4.     NLog.MappedDiagnosticsContext.Set("module", "Main Program");
    5.     NLog.MappedDiagnosticsContext.Set("user", "Nine");
    6.  
    7.     //write log
    8.     logger.Trace("Test trace message");
    9.     logger.Debug("Test debug message");
    10.     logger.Info("Test informational message");
    11.     logger.Warn("Test warning message");
    12.     logger.Error("Test error message");
    13.     logger.Fatal("Test fatal error message");
    14.  
    15.     // custom log level
    16.     logger.Log(LogLevel.Info, "Test fatal error message");
    17. }

    จากโค้ดด้านบนเป็นการส่งค่าที่ต้องการจะบันทึก จากโปรแกรมลง Log Table เพื่อเก็บ User ที่ทำรายการ และ Module ที่ทำการบันทึก Log ในครั้งนี้

    ทำการกด F5 เพื่อทดสอบ

    กด Enter เพื่อจบโปรแกรม

    และลองไปเปิด Table ใน Database ดูจะได้ผลลัพธ์ตามภาพด้านล่าง

    image

    ตัว NLog ยังรองรับ Database อีกหลายชนิดไม่แต่เพียง MS SQL Server เพียงอย่างเดียว

    จึงจบการสอนใช้งานตัว NLog ไว้แค่นี้หวังว่าพอจะได้แนวทางการนำไปใช้งานกัน ซึ่งจริงๆแล้วตัว NLog นั้นยังมีความสามารถมากกว่านี้ครับ ลองไปอ่านเพิ่มเติมจากเว็บไซต์หลักของเขาที่นี่ http://nlog-project.org/

     

     

    แชร์แนวคิดการเก็บ Audit Data ในระบบ

      ก่อนที่เราจะไปเข้ารายละเอียดของแต่ละ Application แต่ละชนิดกัน จะพูดถึงว่าแต่ละระบบเองก็จะมีส่วนของ Security ที่มีใช้งานกันเช่น ใช้ user account ในการเข้าใช้งานในระบบ ก็ต้องใส่ username/password เข้ามาเพื่อตรวจสอบตัวตนว่ามีสทธิ์เข้าใช้งานระบบหรือไม่ ส่วนกรณีที่เป็น public access application เราอาจจะเก็บข้อมุลจาก client ที่เข้ามาใช้งานเท่าที่จำเป็น เช่น IP Address, การเข้าใช้งานในแต่ละหน้า เป็นต้น
      การบันทึกข้อมูลที่เป็น information data ในระบบนั้น ความเป็นเจ้าของข้อมูล ทั้งการ Create/Update เนื่องจากเมื่อมีการแก้ไขข้อมูลไปแล้ว แน่นอนว่าข้อมูลนั้นจะมีการเปลี่ยนแปลงไปเป็นข้อมูลใหม่ ซึ่งในหลายโอกาสจะเกิดปัญหา ไม่ว่าต้องการรู้ว่าใครสร้างข้อมูลนี้ ใครเป็นคนแก้ไขข้อมูลนี้ เมื่อไหร่เวลาไหน เป็นต้น ซึ่งความต้องการเหล่านี้ทำให้เราจำเป็นที่จะต้องเก็บข้อมูลเหล่านั้นเอาไว้เพื่อตอบโจทย์เหล่านี้

    เก็บข้อมูลเฉพาะ username / datetime

    เช่นตัวอย่าง Customer table

    ID FirstName Age Salary Status CreatedBy CreatedDate ModifiedBy ModifiedDate
    1 Nine xx 10000 Active Nine 10/12/2011 13:00:23 Nine 01/02/2012 07:45:45

    จากตางรางด้านบน จะเหมาะสำหรับข้อมูลที่เราให้ความสำคัญของการตรวจสอบครับ

    ข้อดีคือ

    • เก็บข้อมูลแค่ผู้สร้างและแก้ไขล่าสุด
    • ใช้พื้นที่ในการเก็บไม่เยอะมาก ข้อเสีย
    • ไม่ได้เก็บข้อมูลที่ถูกแก้ไขไป และไม่สามารถเรียกดูย้อนหลังได้
    • อาจจะต้องมีการบันทึกข้อมูลกระจายไปยังที่อื่นเพื่อทำ log เพิ่มเติม

    เก็บข้อมูลที่ถูกแก้ไขทั้ง row

    ซึ่งบางระบบก็อาจจะทำเป็น log table นั้น ๆ เพิ่มมาตามจำนวน table ที่มีในระบบ เมื่อมีการ Update Table ก็จะมาทำการ insert ลงใน table นี่เพิ่ม เช่น แก้ไข Customer Table 1 row ก็จะไป insert ลงใน Customer_Log Table 1 row เป็นต้น

    ตัวอย่าง Customer Table

    ID FirstName Age Salary Status LastModifiedBy LastModifiedDate
    1 Peter 30 50000 Deleted User3 10/02/2012 08:30:49

    Customer_Log Table

    LogNo ID FirstName Age Salary Status ModifiedBy ModifiedDate
    1 1 Peter X 20 500000 Active User1 01/02/2012 07:45:45
    2 1 Peter 30 50000 Active User2 01/02/2012 19:55:50
    3 1 Peter 30 50000 Deleted User3 10/02/2012 08:30:49

    ข้อดีคือ

    • สามารถเก็บข้อมูลที่ถูกแก้ไขไว้ได้ทั้งหมด ใช้ในกรณีที่ต้องการ audit ข้อมูลอย่างละเอียด
      ข้อเสืยคือ

    • หากมี 400 table ที่ต้องการเก็บ log ก็ต้อง x2 ตามจำนวน และข้อมูล history จะเยอะมากกินพื้นที่ใน disk
    • อาจจะต้องมีการย้ายข้อมูล log ออกจาก table เป็นประจำ หรืออาจจะแยกเป็น 2 Database เก็บกันคนละที่
    • อาจจะต้องใช้ trigger เข้าช่วยในการบันทึก log เพื่อลดการเขียนโปรแกรม

    ยังมีวิธีการเก็บ log ข้อมูลอีกหลายวิธีแต่ละวิธีก็จะมีข้อดีข้อเสียแตกต่างกันไป ดังนั้นอาจจะต้องเลือกวิธีที่เหมาะสมกับควาต้องการของระบบ ซึ่งการเก็บ log ของการเปลี่ยนแแปลงข้อมูลนั้นอาจจะจำเป็นต้องใช้งานในวันข้างหน้า แต่บาง table ก็อาจจะไม่ต้องทำการเก็บบันทึกก็ได้เนื่องจากอาจจะไม่ใช่ข้อมูลสำคัญของระบบเป็นต้น

    สรุปความ

    ก็ขอจบเรื่องของ Logging เอาไว้เพียงแค่ 2 ตอนนี้หวังว่าพอจะเป็นประโยชน์กับนักพัฒนาระบบที่มีความสนใจและกำลังค้นคว้าศึกษาครับ

    Download Source Code

     http://dl.dropbox.com/u/34809954/LoggingSrc.zip


    image

    About Me:

    Chalermpon Areepong : Nine (นาย)

    Microsoft MVP Thailand ASP.NET

    ASP.NET & MVC Developers Thailand Group : http://www.facebook.com/groups/MVCTHAIDEV/

    Greatfriends.biz Community Leader : http://greatfriends.biz

    Email : nine_biz-talk.net at hotmail dot com

    Blog : https://nine69.wordpress.com

    Advertisements

    15/03/2012

    Application Design : Logging 1

    Filed under: C#, Core System — Tags: , — Nine MVP @ 2:58 am

    ตอนที่2
    https://nine69.wordpress.com/2012/04/21/application-design-logging-2/

    หายไปนาน กลับมากับหัวข้อที่ว่าด้วยเรื่องของการซับพอทโปรแกรมและระบบที่เรากำลังจะสร้างกัน เพราะคงน้อยมากที่นักพัฒนาหลังจากเขียนโปรแกรมเสร็จแล้ว จะหายไปไม่ต้องสนใจโปรแกรมของตนอีกเลย หลังจากมีผู้ใช้งานเริ่มใช้งานระบบนั้น ๆ  เราเรียกว่าการซัพพอทหลังการพัฒนาระบบ ไม่ว่าจะเป็น bug, human error, system failed, system down, system maintenance เมื่อเกิดขึ้นแล้ว ปัญหาจะวิ่งกลับมายังผู้พัฒนาหรือบุคคลที่ต้องเป็นแพะ (admin support) หากไม่ได้ออกแบบส่วนนี้ไว้หรือทำได้ไม่ดีพอ  เราจะได้เห็นฝ่าย IT เจ้าของระบบนั้นๆ นั่งทำงานคิ้วขมวดทั้งวันทั้งคืนเป็นแน่

     

    ปัญหาหากโปรแกรมขาดการเก็บ Log 

    ในระบบที่มีการใช้งานจากผู้ใช้ในระบบจำนวนมาก หลากหลายฝ่าย หลายตำแหน่ง ระดับการทำงานแตกต่างกัน และมีการเข้าใช้งานโปรแกรมที่เราได้พัฒนาไว้  บ่อยครั้งที่เกิดการทำงานที่ผิดพลาดไม่ว่าจะเกิดจากตัวระบบเอง หรือเกิดจากผู้ใช้งาน  ซึ่งหากเราไม่ได้ทำการเก็บบันทึกข้อมูลของปัญหาที่เกิดขึ้นไว้แล้ว  ยากที่จะทำการตรวจสอบและแก้ไขให้สะดวกและรวดเร็วได้

    เนื่องจากต้องไปหาสาเหตุจากต้นตอของปัญหา ไม่ว่าจะเกิดจากผู้ใช้หรือระบบ, หน้าจอไหน, ข้อมูลใด, กลุ่มคลาสหรือเมดธอดอะไร ข้อมูลเหล่านี้มีความจำเป็นในการตรวจสอบ ซึ่งจะช่วยให้ผู้พัฒนาทำการทดสอบระบบด้วยกรณีเดียวกันได้เพื่อให้ทราบถึงปัญหา   ซึ่งโปรแกรมที่เราๆใช้กันอยู่ไม่ว่าจะเป็น Windows 7, Office, หรืออื่น ๆ ต่างก็มีการบันทึก log การทำงานทุกอย่างเพื่อให้ผู้ใช้งานสามารถเข้าไปดูเพื่อวิเคราะห์และแก้ไขปัญหานั่นเอง

    สำหรับนักพัฒนาหน้าใหม่ อาจจะไม่ได้ดักจับ error ไว้ในโปรแกรมเลย หรือไม่ก็อาจจะใช้ MessageBox.Show(); พวกข้อความที่ error บอกผู้ใช้ระบบไปตรงๆ       ซึ่งผู้ใช้งานระบบคงไม่เข้าใจว่าเกิดอะไรขึ้น  ก็ต้อง print screen ส่งมาบอกผู้พัฒนาด้วยตัวเอง และบอกว่าทำอะไรไป ทำหน้าไหน ใส่ข้อมูลอะไร กดอะไรไป  

    ถ้าเราเป็นผู้ใช้งานเองจะรุ้สึกยังไงกับโปรแกรมนี้? ยุ่งยาก มีปัญหา ไม่มีความน่าเชื่อถือ แถมยังต้องมานั่งอธิบายโปรแกรมเมอร์ โดนซักถามเหมือนทำความผิด   เจอผุ้ใช้แบบไม่สนใจไม่แคร์บอกไม่ขอใช้งานระบบ ตีกลับมาก็คงมีเคืองกันอีก ดูไม่เป็น professional เอาเสียเลย

    ดังนั้นผู้พัฒนาอย่างเราๆ จึงจำเป็นที่จะต้องออกแบบระบบการเก็บ log การทำงานของโปรแกรมให้รองรับปัญหาที่จะเกิดขึ้นด้วย  

     

    เราควรเก็บ Log อะไรบ้าง?

    การบันทึก log นั้น อยู่ที่วัตถุประสงค์ของแต่ระบบที่ได้ออกแบบและตกลงกันไว้ แต่วัตถุประสงค์หลัก ๆ ของการเก็บ log นั้นมีอยู่ 2 ส่วนคือ

    1. System Support เช่น การบันทึก Error, Information, System Status เป็นต้น

    2. Audit Support กรณีที่่ต้องคอยให้ข้อมูลเพื่อการตรวจสอบการทำงานของผุ้ใช้ หรือตัวระบบเอง เช่นไม่ว่าจะ login, logout, access page, access service, approved, commit task เป็นต้น

    โดยข้อมุลที่ควรจะถูกจัดเก็บอาจจะมีข้อมูลคร่าว ๆ ดังนี้

    1. Id (auto gen id) อาจจะเป็น bigint, long, guid เป็นต้น
    2. DateTime : วันที่และเวลาที่ทำการบันทึก log (ใช้เวลาที่ error จริง ๆ ใน Application)
    3. LogLevel : อ่านด้านล่าง
    4. Priority : อ่านด้านล่าง
    5. Title : หัวข้อที่จะบันทึก
    6. Application : กรณีมีใช้งานสถานที่เก็บ log รวมกันเช่น windows event log, database table เป็นต้น
    7. Module : ใช้บ่งบอกว่าเป็นโมดูลการทำงานส่วนไหนภายในโปรแกรม เช่น Member Register เป็นต้น
    8. Action : ใช้บ่งบอกว่าเป็นการทำงานอะไร เช่น Create New Register เป็นต้น
    9. Data : อาจจะเป็นข้อมูลที่ต้องเอาไว้ใช้ตรวจสอบ หรืออ้างอิงถึงเช่น Id ของ register user หรืออาจจะเป็น OrderNumber
    10. Message : เป็น message ของ error ที่เกิดขึ้น
    11. Trace : เป็น StackTrace ของ error ที่เกิดขึ้น
    12. SupportNo : โปรแกรมอาจจะมี troubleshooting document link แนบไว้ให้กรณีที่มีคำแนะนำให้ผู้ support ระบบ
    13. Host : อาจจะเป็น computer name, ip address ของ server นั้น ๆ ที่ Application ติดตั้งอยู่
    14. ClientIPAddress : เป็น ip address จริงของ user ที่เข้าใช้งาน application
    15. UserId : อาจจะเป็น username, user id, domain account ของผู้ใช้ที่ทำการ login ในตอนนั้น

    Log Level: ซึ่งนิยมใช้งานกันดังนี้

    1. Fatal\Critical : เป็นความผิดพลาดของระบบที่เกิดขึ้นเอง เช่น OS เกิดปัญหา, executable file corrupt, Hardware ระบบเสียหาย
    2. Error : การบันทึกเมื่อระบบเกิดปัญหาขึ้นเช่น Exception ในโปรแกรม
    3. Warning : การบันทึกเพื่อเตือนว่าอาจจะมีการทำงานผิดพลาดเกิดขึ้นหลังจากนี้
    4. Audit : การบันทึกเพื่อตรวจสอบการทำงานของผุ้ใช้ต่าง ๆ
    5. Information : การบันทึกเก็บข้อมูลบางอย่างเพื่อใช้เป็นการอ้างอิง
    6. Debug : การบันทึกเพื่อใช้ในการตรวจสอบเขียนโปรแกรม
    7. Trace : บันทึกการทำงาน ณ จุดต่าง ๆ ของโปรแกรมตั้งแต่ต้นจนจบ

    Priority: ระดับความสำคัญในการแก้ปัญหา

    1. High: เป็นระดับความสำคัญสูงสุด ซึ่งต้องต้องรีบตรวจสอบหรือแก้ไขปัญหาในทันที เช่น ไม่สามารถบันทึกข้อมูลลงในระบบได้ ทำให้ user ไม่สามารถทำงานต่อไปได้
    2. Medium: เป็นปัญหาที่มีความสำคัญ แต่ระบบหลักยังคงทำงานได้ เพียงแต่อาจจะไม่สมบูรณ์ในบางโมดูล เช่น feed ข่าวจากเว็บมาแสดงไม่ได้ เป็นต้น
    3. Low: เป็นปัญหาที่กระทบต่อการทำงานของผู้ใช้ และควรจะแก้ไขใน released ต่อไปเช่น เกิด exception ใน loop แต่เรา try ข้ามไปและทำงานต่อได้ถูกต้อง เป็นต้น

    เราสามารถบันทึก Log ต่าง ๆ เก็บไว้ที่ไหนได้บ้าง

    • File การบันทุึกเก็บลงไฟล์นั้น

    มีข้อดี

    • สามารถสร้าง method ในการเขียนไฟล์ได้เลย
    • บันทึกข้อมูลได้รวดเร็ว

    มีข้อเสีย

    • เรื่องการกำหนดขนาดไฟล์ไม่ให้ใหญ่มาก และการจัดเก็บไฟล์ให้เป็นระบบ
    • มีโอกาส file corrupt และ read/write locked
    • รวมไปถึงการเข้ามาอ่าน log file ทั้งเรื่องรูปแบบของไฟล์ สถานที่เก็บไฟล์
    • Database การบันทึกลง table ใน database

    มีข้อดี

    • ในด้านการจัดการ โดยสามารถลดการกระจัดกระจายของ log โดยรวมไว้ที่เดียว
    • สามารถเข้าถึงและอ่าน log ได้ง่าย

    ข้อเสีย

    • จะกินเนื้อในการเก็บข้อมูลจำนวนมาก
    • มี cost ที่เพิ่มขึ้นเช่น CPU time, Network bandwidth ของ DB Server
    • Windows Event Log

    ข้อดี

    • เก็บไว้ในที่เป็นมาตรฐานของ OS
    • สามารถเรียกดูได้โดย remote event viewer หรือผ่านหน้าเครื่องตรงๆ

    ข้อเสีย

    • มีผลต่อประสิทธิภาพของ OS
    • มีพื้นที่จำกัดในการเก็บบันทึก

    และยังมีที่เก็บอีกมากมายแล้วแต่ความต้องการไม่ว่าจะเป็น Email, ยิง TCP, ส่งไปเข้า MSMQ เป็นต้น ซึ่งก็แล้วแต่ว่าเราจะเลือกสถานที่จัดเก็บเพื่อความสะดวกและเหมาะสม

     

    รู้จักกับ Error ที่เกิดขึ้นในการเขียนโปรแกรม (Exception)

    โค้ดจำนวนมากผู้พัฒนาได้เขียนออกไป กรณีหากเกิดการทำงานที่ผิดพลาดขึ้น ในตอน Run time ระบบจะทำการโยน (Throw) ชุดคำสั่งมีการทำงานที่ผิดพลาดกลับมายังโปรแกรมในรูปของ Exception ซึ่งอาจจะทำให้ระบบแฮ้งค้าง เกิดไดอะล็อกเตือน ไปจนหยุดการทำงานของโปรแกรม 

    กรณีที่เราไม่ต้องการให้โปรแกรมหยุดการทำงาน และตรวจจับปัญหาของชุดคำสั่งได้นั้น แต่ละภาษาก็จะมีชุดคำสั่งในการดักจับ Error ที่เรียกว่า Try…Catch Block  ซึ่งเขียนได้ในรูปนี้

    Code 1:
    1. Person person1 = null;
    2. try
    3. {
    4.     //person1 = new Person();
    5.     person1.FullName = "call null object";
    6.     
    7. }
    8. catch (Exception ex)
    9. {
    10.     MessageBox.Show(ex.ToString());
    11. }
    12. finally
    13. {
    14.     person1 = null;
    15. }

     

    จาก code ตัวอย่างด้านบนจะเห็นได้ว่า

    (line8-11) มีการ catch error ลง Exception Class เมื่อเกิด error ขึ้นใน try block ด้านบน (line 2-7)

    ทำให้โปรแกรมเข้าไปทำงานต่อบล็อกของ catch พร้อมได้รายละเอียดของ error เพื่อนำไปจัดการปัญหาที่เกิดขึ้นต่อไป ในตัวอย่างแสดงแค่แสดง messagebox เท่านั้น

    เมื่อทำการ  Watch ex object ดูก็จะเห็นข้อมูลดังนี้

    Image 1: image 

    Exception Class เป็นเบสคลาสของ exception class อื่นๆ ซึ่งพบบ่อยว่ามักใช้ในการดักจับ error ทุกประเภทไม่ว่าจะ IO , Network , Permission , COM ซึ่งค่อนข้างใช้งานได้กว้างขวาง โดยโครงสร้างของคลาสจะมี property ของข้อมูลพื้นฐาน ให้ใช้งานดังนี้

    Property Name

    Description

    Data จะบอกข้อมูลของ Data ที่เกี่ยวข้องกับ Error นั้น
    HelpLink เป็น link ไปยัง document เพื่อช่วยอธิบายปัญหา หาก exception นั้นมีใส่ไว้
    HResult เป็น number ที่กำหนดให้ error นั้น
    InnerException เป็น exception ที่เป็นสาเหตุให้เกิด exception นี้
    Message ข้อความของ error
    Source ชื่อของโปรแกรมที่ error
    StackTrace เป็นข้อมูลที่เรียงลำดับการ call จาก Main Method ไปจนถึง line number ที่เกิด error
    TargetSite ชื่อ Main Method ที่โยน Exception

    ซึ่งข้อที่ควรสนใจคือ Exception Class ไม่ควรใช้งานเป็นคลาสหลัก เพราะเป็นเบสคลาสซึ่งจะทำให้เสียข้อมูลในส่วนของ exception ที่ควรจะได้ไม่ครบถ้วนของปัญหาที่เกิดขึ้น เช่น SqlException ที่จะมี Number property ของ Database แจ้งออกมาด้วยเป็นต้น แต่ถ้าคิดไม่ออกจริง ๆ ว่า try..catch block นี้จะเกิด exception ตัวไหนบ้างก็รอง catch สุดท้ายไว้ด้วย Exception Class ก็ได้ครับ

    Exception Class ได้ถูกกำหนดให้เป็น Based class ซึ่งถูกนำไปใช้โดยแยกออกตามส่วนของระบบดังนี้

    1. SystemException Class  เป็นคลาสได้ที่กำหนดไว้แล้วใน Common Language Runtime Class ซึ่งจะใช้เป็น Base Class อีกครั้งสำหรับสร้าง exception class ที่แยกย่อยออกไปตามการทำงานเช่น NullReferenceException, ArithmaticException, IOException, SqlException เป็นต้น
    2. ApplicationException Class  เป็นคลาสที่อนุญาตให้ผู้พัฒนานำไปใช้เป็น base class สำหรับใช้สร้าง Exception class ที่จะใช้โปรแกรมนั้น เช่น InvalidBusinessRuleException เป็นต้น

      ลองดูโค้ดชุดนี้ที่มีการสร้าง Exception ต่างๆ ในแต่ละกรณีตัวอย่าง

      Code 2:
      1. try
      2. {
      3.     //Case 1: NullReferenceException
      4.     Person person1 = null;
      5.     person1.FullName = "call null object";
      6.  
      7.     //Case 2: ArithmeticException
      8.     int i = 5;
      9.     int x = 0;
      10.     var res = i / x;
      11.  
      12.     //Case 3: ApplicationException
      13.     throw new ApplicationException("My Error");
      14.  
      15. }
      16. catch (NullReferenceException ex) //Case1
      17. {
      18.     MessageBox.Show(ex.ToString());
      19. }
      20. catch (ArithmeticException ex) //Case2
      21. {
      22.     MessageBox.Show(ex.ToString());
      23. }
      24. catch (ApplicationException ex)  //Case3
      25. {
      26.     MessageBox.Show(ex.ToString());
      27. }
      28. catch (SystemException ex) //Case1, Case2
      29. {
      30.     MessageBox.Show(ex.ToString());
      31. }
      32. catch (Exception ex) //Case1, Case2, Case3
      33. {
      34.     MessageBox.Show(ex.ToString());
      35. }

    Line 5:  จะเกิด error เนื่องจาก person1 ยังไม่ได้ทำการกำหนดค่าเริ่มต้น new Person(); ซึ่งจะวิ่งไปที่ Line 16: ซึ่งดักด้วย NullReferenceException  กรณีที่อ้างอิง object ที่เป็น null

    Line 10: จะเกิด error เนื่องจากเป็นการหารด้วย 0  ซึ่งจะวิ่งไปทำงานที่ Line 20: ซึ่งจับด้วย ArithmeticException กลุ่มคณิต

    Line 13: จะเกิด error เนื่องจากเป็นการสร้าง ApplicationException แล้ว throw ให้โปรแกรมเกิด error  จะวิ่งไปทำงานที่ Line 24: คือตรงตัว class

    ส่วน Line 28:  หากลบโค้ดตั้งแต่ Line 16 – 27 ออก  Line 5 และ 10  จะวิ่งมาทำงานที่นี่เพราะเป็น base class ที่สืบสอดมาจาก Exception Class ใช้งานในกลุ่มของ CLR Exception ทั้งหมด

     

    สำหรับ .NET App เราสามารถบันทึก Log ด้วยอะไรได้บ้าง?

    Custom Log

    โดยเป็นการเขียนโค้ดขึ้นมาเอง โดยอาจจะมี static method เอาไว้ใช้งานโดย ให้บันทึกลง File, ยิงเข้า StoreProcedure ลง Table, ส่ง Email เป็นต้น ซึ่งต้องเขียนจัดการเองทั้งหมด ซึ่งหากเขียนไว้ใช้เองก็จะพอดีกับงาน  แต่ค่อนข้างไม่ยืดหยุ่นแล้วมีข้อจำกัด รวมไปถึงมีความเสี่ยงในการจัดการของตัว Log ที่เขียนขึ้นเอง

    3rd Party Library

    การใช้กลุ่ม logging open source ถือว่าน่าสนใจมากกว่าเมื่อเปรียบเทียบข้อดีข้อเสียแล้ว  เพราะ library เหล่านี้ถูกพัฒนาขึ้นมาและใช้งานจากกลุ่มนั้น ๆ มีการพัฒนาและแก้ไขปรับปรุงโค้ดต่อเนื่อง รวมไปถึงความสามารถที่ช่วยลดการเกิดปัญหาเกี่ยวกับตัว Log เองเช่น การทำ file rolling, file size limit, various target source, multiple target source, Alternate Source,  write buffer, etc.  ผมขอแนะนำที่น่าสนใจ

    1. Enterprise Library 5.0: Logging Block จาก Microsoft เอง ก็น่าสนใจตัวนึง มีการพัฒนาต่อเนื่อง และมี Document/Lab ให้เอาไปทดลองทำกันได้

    2. Log4Net  เป็นการพัฒนาต่อยอดมาจาก Log4J มีการใช้งานกันอย่างกว้างขวาง แต่ก็ไม่เห็นมี update version มาซักระยะแล้ว

    3. NLog  เป็นตัวที่จะนำมาแนะนำในบทความตอนต่อไปครับ

       

    ส่งท้าย

    ในตอนนี้พยายามชี้ให้เห็นถึงปัญหาของการซัพพอทระบบหรือโปรแกรม  ที่เรากำลังจะส่งต่อไปให้ผุ้ใช้งานและผุ้ดูแลระบบ  ให้เข้าใจถึงความสำคัญว่าทำไมเราต้องเก็บ log ข้อมูลเอาไว้   ทั้งได้แนะนำข้อมูล log เบื้องต้นและการจัดการ exception ที่เกิดขึ้นใน .NET Application

    ในตอนต่อไปจะแนะนำการใช้งาน NLog และแนะนำการเก็บบันทึกข้อมูลจาก Application ที่แตกต่างกันครับ

     


     

    Chalermpon Areepong Nine (นาย)

    Microsoft MVP Thailand ASP.NET

    email : nine_biz-talk.net at hotmail dot com

    Create a free website or blog at WordPress.com.