Application Design : Logging 2
21/04/2012 2 Comments
ตอนที่แล้ว
http://nine69.wordpress.com/2012/03/15/application-design-logging-1/
ในตอนนี้ขอลงรายละเอียดเรื่องราวทั้งหมดนี้ครับ
- การใช้งาน NLog เบื้องต้นกับ .NET Application
- การ config Alternate Source
- แนวคิดการเก็บ 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 เบื้องต้น
- Visual Studio 2005 ขึ้นไป (บทความใช้ VS2010)
- NuGet (download here)
- MS SQL
- Internet
สำหรับหัวข้อต่อไปต้องใช้ Software ต่อไปนี้
เปิด Visual Studio ขึ้นมาแล้วทำการสร้าง Console Project ตามภาพ
เสร็จแล้วจะได้โปรเจ็คตามภาพด้านล่างนี้
ต่อไปให้ทำการเรียก Package Manager Console ขึ้นมาเพื่อทำการติดตั้งตัว NLog ในโปรแกรมของเรา
หลังจากได้ 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.
อธิบาย 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 ตามตารางด้านล่างนี้
- using System;
- using NLog;
- namespace NLog01
- {
- public class Program
- {
- // NLog instance
- private static Logger logger = LogManager.GetCurrentClassLogger();
- private static void Main(string[] args)
- {
- TestLog(); // method test
- Console.ReadLine();
- }
- public static void TestLog()
- {
- logger.Trace("Test trace message");
- logger.Debug("Test debug message");
- logger.Info("Test informational message");
- logger.Warn("Test warning message");
- logger.Error("Test error message");
- logger.Fatal("Test fatal error message");
- // Log() method Level
- logger.Log(LogLevel.Info, "Test fatal error message");
- }
- }
- }
อธิบาย code
-
line2: เรียกใช้ namespace ของ NLog
-
line17-27: เป็นการทดสอบเขียน Log ใน Level ต่าง ๆ รวมไปถึงการกำหนด level ผ่าน Log() method ได้เอง
-
line13: เรียกใช้ TestLog() ที่ได้เขียนไว้
จากนั้นก็ทดสอบ run โดยกด F5 ก็จะได้หน้าต่างดำๆ ค้างนิ่งอยู่ตามภาพ และกด Enter หรือ Key อื่น ๆ ลงไปเพื่อจบโปรแกรม
กลับมาที่ Visual Studio แล้วไปที่ Solution Explorer แล้วกดตามลำดับหมายเลขในรูปด้านล่าง
- เลือกโปรเจ็ค
- กดเพื่อดู file/folder ทั้งหมดในโปรเจ็ค
- กดเข้าไปใน bin –> Debug –> logs –> จะพบ file log เกิดขึ้นใน folder ดังกล่าว
- เปิด file log ขึ้นมาเพื่อดูผลลัพธ์ ก็จะพบ log message ที่เราได้สั่งบันทึกไปตามรูปด้านล่าง
- จากนั้นหากเราจะกำหนดให้ File Target จำกัดขนาดของ log file และ split ออกตามขนาดเพื่อความง่ายในการเปิดมาอ่าน โดยเปิดให้เพิ่ม config เข้าไปตามนี้
Code Snippet
- <?xml version="1.0" encoding="utf-8" ?>
- <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
- <!–See http://nlog-project.org/wiki/Configuration_file
- for information on customizing logging rules and outputs. –>
- <targets>
- <!– add your targets here –>
- <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
- layout="${longdate} ${uppercase:${level}} ${message}"
- archiveEvery="Day"
- archiveAboveSize="10000"
- archiveNumbering="Sequence" />
- </targets>
- <rules>
- <!– add your logging rules here –>
- <logger name="*" minlevel="Trace" writeTo="file" />
- </rules>
- </nlog>
9. เสร็จแล้วไปปรับ Code ใน Program.cs มาแก้ไข TestLog() ใหม่ตามนี้ เพื่อจำลองสร้าง log file ขนาดใหญ่
- public static void TestLog()
- {
- for (int i = 0; i < 500; i++)
- {
- logger.Trace("Test trace message");
- logger.Debug("Test debug message");
- logger.Info("Test informational message");
- logger.Warn("Test warning message");
- logger.Error("Test error message");
- logger.Fatal("Test fatal error message");
- // Log() method Level
- logger.Log(LogLevel.Info, "Test fatal error message");
- }
- }
-10. ทดลองรันโปรแกรมดูด้วยการกด F5 แล้วกลับไปดูใน Log Folder จะพบ log file หลากตัวเกิดขึ้นเนื่องจากเรากำหนด size ของไฟล์ไว้นั้นเอง
และเราก็ยังสามารถ config เพิ่มเติมได้อีกมากมายสามารถเข้าไปอ่านรายละเอียดได้ที่นี่ http://nlog-project.org/wiki/File_target
การเก็บ log ใน Event Log ของ Windows
ต่อไปเราจะเก็บ log ลงไปใน Event Log มีขั้นตอนดังนี้
1. ไปที่ Solution Explorer และเปิด NLog.config ขึ้นมาเพื่อแก้ไข
2. เพิ่ม config เข้าไปตามตารางด้านล่าง
- <?xml version="1.0" encoding="utf-8" ?>
- <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
- <!–See http://nlog-project.org/wiki/Configuration_file
- for information on customizing logging rules and outputs. –>
- <targets>
- <!– add your targets here –>
- <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
- layout="${longdate} ${uppercase:${level}} ${message}"
- archiveEvery="Day"
- archiveAboveSize="10000"
- archiveNumbering="Sequence" />
- <target xsi:type="EventLog" name="evl" layout=" ${longdate}|${level:uppercase=true}|${logger}|${message}"
- machineName="NINE-WNB"
- source="NLog01" eventId="9001" log="Application" />
- </targets>
- <rules>
- <!– add your logging rules here –>
- <logger name="*" minlevel="Trace" writeTo="file" />
- <logger name="*" minlevel="Trace" writeTo="evl" />
- </rules>
- </nlog>
- public static void TestLog()
- {
- logger.Trace("Test trace message");
- logger.Debug("Test debug message");
- logger.Info("Test informational message");
- logger.Warn("Test warning message");
- logger.Error("Test error message");
- logger.Fatal("Test fatal error message");
- // custom log level
- logger.Log(LogLevel.Info, "Test fatal error message");
- }
line13-15: เป็นการเพิ่ม event log target เข้าไป
line14: อย่าลิมแก้เป็นชื่อ computer name ของตัวเองนะครับ
3. จากนั้นกลับไปแก้ file Program.cs ใน method TestLog() โดยลบ For Loop ออกตามด้านล่างนี้
4. และทำการ กด F5 เพื่อรันโปรแกรมทดสอบ กด Enter เพื่อจบโปรแกรม
5.. เสร็จแล้วไปเปิด Start –> Administrative Tools –> Event Viewer เพื่อดูผลก็จะพบ log ที่เราสั่งบันทึกไปตามรูปด้านล่าง
การเก็บ log ลงใน Database
เริ่มต้นด้วยการสร้าง Database ใน MS SQL ชื่อว่า Logging
จากนั้นนำ script ไปสร้าง Table ใน Database ด้วย script ด้านล่างนี้
- CREATE TABLE [dbo].[MyLog](
- [Id] [bigint] IDENTITY(1,1) NOT NULL,
- [CreateDate] [varchar](50) NULL,
- [Origin] [nvarchar](50) NULL,
- [Level] [varchar](50) NULL,
- [Message] [nvarchar](1000) NULL,
- [Exception] [nvarchar](max) NULL,
- [StackTrace] [nvarchar](4000) NULL,
- [Module] [varchar](100) NULL,
- [User] [nvarchar](200) NULL,
- CONSTRAINT [PK_MyLog] PRIMARY KEY CLUSTERED
- (
- [Id] ASC
- )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS= ON) ON [PRIMARY]
- ) ON [PRIMARY]
- GO
เมื่อสร้าง Database และ Table ตาม script ตามข้างต้นเสร็จแล้ว
กลับมาที่ Visual Studio ไปเปิดไฟล์ NLog.config ขึ้นมาเพื่มค่าตามตารางด้านล่างนี้
- <?xml version="1.0" encoding="utf-8" ?>
- <nlog xmlns="http://www.nlog-project.org/schemas/NLog.netfx40.xsd"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="int.log">
- <!–See http://nlog-project.org/wiki/Configuration_file
- for information on customizing logging rules and outputs. –>
- <targets>
- <!– add your targets here –>
- <target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}.log"
- layout="${longdate} ${uppercase:${level}} ${message}"
- archiveEvery="Day"archiveAboveSize="10000" archiveNumbering="Sequence" />
- <target xsi:type="EventLog" name="evl" layout=" ${longdate}|${level:uppercase=true}|${logger}|${message}"
- machineName="NINE-WNB" source="NLog01" eventId="9001" log="Application" />
- <target xsi:type="Database" name="db" keepConnection="false"
- useTransactions="true"
- connectionString="Data Source=NINE-WNB\MSSQL2K8R2;Initial Catalog=LoggingDB;Integrated Security=True">
- <commandText>
- insert into MyLog ([CreateDate], [Origin], [Level], [Message], [Exception], [StackTrace], [Module], [User]) values (@createDate, @origin, @logLevel, @message, @exception, @stackTrace, @module, @user);
- </commandText>
- <parameter name="@createDate" layout="${longdate}"/>
- <parameter name="@origin" layout="${callsite}"/>
- <parameter name="@logLevel" layout="${level}"/>
- <parameter name="@message" layout="${message}"/>
- <parameter name="@exception" layout="${exception:format=Message,StackTrace}"/>
- <parameter name="@stackTrace" layout="${stacktrace}"/>
- <parameter name="@module" layout="${mdc:item=module}"/>
- <parameter name="@user" layout="${mdc:item=user}"/>
- </target>
- </targets>
- <rules>
- <!– add your logging rules here –>
- <logger name="*" minlevel="Trace" writeTo="file" />
- <logger name="*" minlevel="Trace" writeTo="evl" />
- <logger name="*" minlevel="Trace" writeTo="db" />
- </rules>
- </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 เพื่อแก้ไขโค้ดตามตารางด้านล่างนี้
- public static void TestLog()
- {
- //Assign variable value
- NLog.MappedDiagnosticsContext.Set("module", "Main Program");
- NLog.MappedDiagnosticsContext.Set("user", "Nine");
- //write log
- logger.Trace("Test trace message");
- logger.Debug("Test debug message");
- logger.Info("Test informational message");
- logger.Warn("Test warning message");
- logger.Error("Test error message");
- logger.Fatal("Test fatal error message");
- // custom log level
- logger.Log(LogLevel.Info, "Test fatal error message");
- }
จากโค้ดด้านบนเป็นการส่งค่าที่ต้องการจะบันทึก จากโปรแกรมลง Log Table เพื่อเก็บ User ที่ทำรายการ และ Module ที่ทำการบันทึก Log ในครั้งนี้
ทำการกด F5 เพื่อทดสอบ
กด Enter เพื่อจบโปรแกรม
และลองไปเปิด Table ใน Database ดูจะได้ผลลัพธ์ตามภาพด้านล่าง
ตัว 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
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 : http://nine69.wordpress.com
