ตามเอกสารของ NLog:
แอพพลิเคชั่นส่วนใหญ่จะใช้คนตัดไม้หนึ่งคนต่อคลาสโดยที่ชื่อของคนตัดไม้จะเหมือนกับชื่อของคลาส
นี่เป็นวิธีเดียวกับที่ log4net ทำงาน เหตุใดจึงเป็นแนวทางปฏิบัติที่ดี
ตามเอกสารของ NLog:
แอพพลิเคชั่นส่วนใหญ่จะใช้คนตัดไม้หนึ่งคนต่อคลาสโดยที่ชื่อของคนตัดไม้จะเหมือนกับชื่อของคลาส
นี่เป็นวิธีเดียวกับที่ log4net ทำงาน เหตุใดจึงเป็นแนวทางปฏิบัติที่ดี
คำตอบ:
ด้วย log4net การใช้คนตัดไม้หนึ่งคนต่อคลาสทำให้ง่ายต่อการจับแหล่งที่มาของข้อความบันทึก (เช่นคลาสที่เขียนไปยังบันทึก) หากคุณไม่มีคนตัดไม้หนึ่งคนต่อชั้นเรียน แต่มีคนตัดไม้หนึ่งคนสำหรับทั้งแอปแทนคุณต้องใช้เทคนิคการสะท้อนเพิ่มเติมเพื่อให้ทราบว่าข้อความบันทึกมาจากไหน
เปรียบเทียบสิ่งต่อไปนี้:
using System.Reflection;
private static readonly ILog _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void SomeMethod()
{
_logger.DebugFormat("File not found: {0}", _filename);
}
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller
-- or --
Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
เมื่อใช้ตัวอย่างที่สอง Logger จะต้องสร้าง stack trace เพื่อดูว่าใครเป็นคนเรียกมันหรือรหัสของคุณจะต้องส่งผ่านในผู้โทรเสมอ ด้วยรูปแบบคนตัดไม้ต่อคลาสคุณยังคงทำสิ่งนี้ได้ แต่คุณสามารถทำได้ครั้งเดียวต่อชั้นเรียนแทนที่จะเป็นครั้งเดียวต่อการโทรหนึ่งครั้งและขจัดปัญหาด้านประสิทธิภาพที่ร้ายแรง
ข้อดีสำหรับการใช้ "ตัวบันทึกต่อไฟล์" ใน NLog: คุณมีความเป็นไปได้ในการจัดการ / กรองบันทึกตามเนมสเปซและชื่อคลาส ตัวอย่าง:
<logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" />
<logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" />
<logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" />
<!-- Hide other messages from StupidLibrary -->
<logger name="StupidLibrary.*" final="true" />
<!-- Log all but hidden messages -->
<logger name="*" writeTo="AllLogs" />
NLogger มีข้อมูลโค้ดที่มีประโยชน์มากในการดำเนินการนี้ nlogger
ข้อมูลโค้ดจะสร้างรหัสต่อไปนี้:
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
ดังนั้นการกดแป้นพิมพ์เพียงไม่กี่ครั้งและคุณมีคนตัดไม้ต่อชั้นเรียน มันจะใช้เนมสเปซและชื่อคลาสเป็นชื่อของคนตัดไม้ ในการตั้งชื่ออื่นให้กับคนตัดไม้ชั้นเรียนของคุณคุณสามารถใช้สิ่งนี้:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
และอย่างที่ @JeremyWiebe กล่าวคุณไม่จำเป็นต้องใช้เทคนิคในการเรียกชื่อคลาสที่พยายามบันทึกข้อความ: ชื่อของคนตัดไม้ (ซึ่งโดยปกติจะเป็นชื่อของคลาส) สามารถเข้าสู่ไฟล์ได้อย่างง่ายดาย (หรือเป้าหมายอื่น ๆ ) โดยใช้${logger}
ในเค้าโครง
ฉันเห็นเหตุผลบางประการสำหรับตัวเลือกนี้
นอกจากนี้ยังมีประโยชน์ด้านประสิทธิภาพในกรณีของ NLog ผู้ใช้ส่วนใหญ่จะใช้
Logger logger = LogManager.GetCurrentClassLogger()
การค้นหาคลาสปัจจุบันจากการติดตามสแต็กจะใช้ประสิทธิภาพบางอย่าง (แต่ไม่มาก)
ในกรณีส่วนใหญ่ชื่อของคลาสจะเป็นชื่อที่ดีสำหรับคนตัดไม้ เมื่อสแกนไฟล์บันทึกคุณจะเห็นข้อความบันทึกและเชื่อมโยงโดยตรงกับบรรทัดรหัส
ตัวอย่างที่ดีซึ่งนี่ไม่ใช่แนวทางที่ดีที่สุดคือบันทึก SQL ของ Hibernate มีคนตัดไม้ที่ใช้ร่วมกันชื่อ "Hibernate.SQL" หรืออะไรทำนองนั้นโดยที่คลาสต่างๆจำนวนหนึ่งจะเขียน SQL ดิบลงในหมวดหมู่คนตัดไม้เดียว
จากมุมมองของการพัฒนามันง่ายที่สุดถ้าคุณไม่ต้องสร้างวัตถุคนตัดไม้ในแต่ละครั้ง ในทางกลับกันถ้าคุณไม่ทำ แต่คุณสร้างแบบไดนามิกโดยใช้การสะท้อนกลับจะทำให้ประสิทธิภาพช้าลง ในการแก้ปัญหานี้คุณสามารถใช้รหัสต่อไปนี้ซึ่งสร้างตัวบันทึกแบบไดนามิกแบบอะซิงโครนัส:
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinForms
{
class log
{
public static async void Log(int severity, string message)
{
await Task.Run(() => LogIt(severity, message));
}
private static void LogIt(int severity, string message)
{
StackTrace st = new StackTrace();
StackFrame x = st.GetFrame(2); //the third one goes back to the original caller
Type t = x.GetMethod().DeclaringType;
Logger theLogger = LogManager.GetLogger(t.FullName);
//https://github.com/NLog/NLog/wiki/Log-levels
string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" };
int level = Math.Min(levels.Length, severity);
theLogger.Log(LogLevel.FromOrdinal(level), message);
}
}
}
สองเหตุผลที่เกิดขึ้นในใจทันที:
อาจเป็นเพราะคุณต้องการบันทึกวิธีการที่สามารถมองเห็นได้เฉพาะคลาสโดยไม่ทำลายการห่อหุ้มการทำเช่นนี้ยังช่วยให้ใช้คลาสในแอปพลิเคชันอื่นได้ง่ายโดยไม่ทำลายฟังก์ชันการบันทึก
ทำให้ง่ายต่อการกำหนดค่าภาคผนวกตามเนมสเปซหรือคลาส
หากคุณใช้ NLOG คุณสามารถระบุ callite ใน config ได้ซึ่งจะบันทึกชื่อคลาสและเมธอดที่มีคำสั่งการบันทึก
<property name="CallSite" value="${callsite}" />
จากนั้นคุณสามารถใช้ค่าคงที่สำหรับชื่อคนตัดไม้ของคุณหรือชื่อแอสเซมบลี
ข้อจำกัดความรับผิดชอบ: ฉันไม่รู้ว่า NLOG รวบรวมข้อมูลนี้อย่างไรการคาดเดาของฉันจะเป็นการสะท้อนดังนั้นคุณอาจต้องพิจารณาประสิทธิภาพ มีปัญหาเล็กน้อยเกี่ยวกับวิธีการ Async หากคุณไม่ได้ใช้ NLOG v4.4 หรือใหม่กว่า