เหตุใดคนตัดไม้จึงแนะนำให้ใช้คนตัดไม้ต่อชั้นเรียน


88

ตามเอกสารของ NLog:

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

นี่เป็นวิธีเดียวกับที่ log4net ทำงาน เหตุใดจึงเป็นแนวทางปฏิบัติที่ดี


1
อืม. ดูเหมือนว่าจะมีสองประเด็นที่นี่ - ปัญหาหนึ่งมีอ็อบเจ็กต์บันทึกจริงต่อคลาสและปัญหาหนึ่งมีชื่อของบันทึกเหมือนกับคลาส
Peter Recore

คำตอบ:


59

ด้วย 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 เพื่อดูว่าใครเป็นคนเรียกมันหรือรหัสของคุณจะต้องส่งผ่านในผู้โทรเสมอ ด้วยรูปแบบคนตัดไม้ต่อคลาสคุณยังคงทำสิ่งนี้ได้ แต่คุณสามารถทำได้ครั้งเดียวต่อชั้นเรียนแทนที่จะเป็นครั้งเดียวต่อการโทรหนึ่งครั้งและขจัดปัญหาด้านประสิทธิภาพที่ร้ายแรง


ขอบคุณที่ช่วยชี้แจงสิ่งต่างๆ เราเพียงแค่ใส่ชื่อคลาสและวิธีการลงในข้อความด้วยตนเอง (เช่น "ImageCreator.CreateThumbnail () เรียก") แต่จะดีกว่าถ้าคนตัดไม้สามารถจัดการได้
Daniel T.

1
เพียงแค่ FYI มันกลายเป็นแนวทางปฏิบัติที่ "ดีกว่า" ในการมี Logger ต่ออินสแตนซ์แทนที่จะเป็นแบบต่อคลาส (เช่นแบบคงที่) เพราะทำให้ง่ายต่อการจับข้อมูลเช่นข้อมูลเธรด เห็นได้ชัดว่ามันเป็นเรื่องของรสนิยมไม่ใช่ "กฎที่ยากและรวดเร็ว" แต่ฉันอยากจะโยนมันออกไป
Will Hartung

7
@ คุณช่วยอธิบายเพิ่มเติมอีกนิดได้ไหม เมื่อบันทึกโดยใช้ Logger ต่อคลาสฉันจะล็อก ID เธรดเสมอเพื่อให้ผู้บันทึกสามารถรับข้อมูลเธรดปัจจุบันได้ ข้อมูลด้ายอื่น ๆ จะมีให้สำหรับคนตัดไม้เช่นกัน
Jeremy Wiebe

@Jeremy Wiebe: นี่เป็นเหตุผลเดียวเหรอ? ในการใช้งานจะไม่มีปัญหาถ้าฉันใช้ตัวแปร global ชนิดเดียวสำหรับทั้งแอป?
gmoniava

1
@Giorgi ไม่ฉันไม่คิดอย่างนั้น วันนี้คุณสามารถรับข้อมูลจำนวนมากได้ด้วยแอตทริบิวต์ CallerInformation ซึ่งทำให้หนึ่งคนตัดไม้ต่อชั้นเรียนมีความเกี่ยวข้องน้อยลงเล็กน้อย - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe

16

ข้อดีสำหรับการใช้ "ตัวบันทึกต่อไฟล์" ใน 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}ในเค้าโครง


5

ฉันเห็นเหตุผลบางประการสำหรับตัวเลือกนี้

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

4

นอกจากนี้ยังมีประโยชน์ด้านประสิทธิภาพในกรณีของ NLog ผู้ใช้ส่วนใหญ่จะใช้

Logger logger = LogManager.GetCurrentClassLogger()

การค้นหาคลาสปัจจุบันจากการติดตามสแต็กจะใช้ประสิทธิภาพบางอย่าง (แต่ไม่มาก)


3

ในกรณีส่วนใหญ่ชื่อของคลาสจะเป็นชื่อที่ดีสำหรับคนตัดไม้ เมื่อสแกนไฟล์บันทึกคุณจะเห็นข้อความบันทึกและเชื่อมโยงโดยตรงกับบรรทัดรหัส

ตัวอย่างที่ดีซึ่งนี่ไม่ใช่แนวทางที่ดีที่สุดคือบันทึก SQL ของ Hibernate มีคนตัดไม้ที่ใช้ร่วมกันชื่อ "Hibernate.SQL" หรืออะไรทำนองนั้นโดยที่คลาสต่างๆจำนวนหนึ่งจะเขียน SQL ดิบลงในหมวดหมู่คนตัดไม้เดียว


2

จากมุมมองของการพัฒนามันง่ายที่สุดถ้าคุณไม่ต้องสร้างวัตถุคนตัดไม้ในแต่ละครั้ง ในทางกลับกันถ้าคุณไม่ทำ แต่คุณสร้างแบบไดนามิกโดยใช้การสะท้อนกลับจะทำให้ประสิทธิภาพช้าลง ในการแก้ปัญหานี้คุณสามารถใช้รหัสต่อไปนี้ซึ่งสร้างตัวบันทึกแบบไดนามิกแบบอะซิงโครนัส:

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);

        }
    }
}

1

สองเหตุผลที่เกิดขึ้นในใจทันที:

  1. การมีบันทึกแยกกันสำหรับแต่ละคลาสทำให้ง่ายต่อการจัดกลุ่มข้อความบันทึก / ข้อผิดพลาดทั้งหมดที่เกี่ยวข้องกับคลาสที่กำหนด
  2. การมีบันทึกภายในชั้นเรียนช่วยให้คุณสามารถบันทึกรายละเอียดภายในซึ่งอาจไม่สามารถเข้าถึงได้จากภายนอกชั้นเรียน (เช่นรัฐส่วนตัวข้อมูลที่เกี่ยวข้องกับการใช้งานของชั้นเรียนเป็นต้น)

2
คุณมีคนตัดไม้ในชั้นเรียนไม่ว่าจะกำหนดไว้ที่ระดับชั้นเรียนหรือทั่วโลก คนตัดไม้ทั่วโลกไม่ได้ "อยู่นอกชั้นเรียน" จากมุมมองที่มองเห็นได้ คุณยังคงอ้างถึงคนตัดไม้ทั่วโลกจากในชั้นเรียนที่เป็นปัญหาเพื่อให้คุณสามารถมองเห็นได้อย่างเต็มที่
Robert

0

อาจเป็นเพราะคุณต้องการบันทึกวิธีการที่สามารถมองเห็นได้เฉพาะคลาสโดยไม่ทำลายการห่อหุ้มการทำเช่นนี้ยังช่วยให้ใช้คลาสในแอปพลิเคชันอื่นได้ง่ายโดยไม่ทำลายฟังก์ชันการบันทึก


1
ทำให้ยากที่จะใช้คลาสนั้นในแอปพลิเคชันอื่น คุณต้องอ้างอิงไลบรารีการบันทึกว่าคุณชอบหรือไม่
Piotr Perak

0

ทำให้ง่ายต่อการกำหนดค่าภาคผนวกตามเนมสเปซหรือคลาส


0

หากคุณใช้ NLOG คุณสามารถระบุ callite ใน config ได้ซึ่งจะบันทึกชื่อคลาสและเมธอดที่มีคำสั่งการบันทึก

<property name="CallSite" value="${callsite}" />

จากนั้นคุณสามารถใช้ค่าคงที่สำหรับชื่อคนตัดไม้ของคุณหรือชื่อแอสเซมบลี

ข้อจำกัดความรับผิดชอบ: ฉันไม่รู้ว่า NLOG รวบรวมข้อมูลนี้อย่างไรการคาดเดาของฉันจะเป็นการสะท้อนดังนั้นคุณอาจต้องพิจารณาประสิทธิภาพ มีปัญหาเล็กน้อยเกี่ยวกับวิธีการ Async หากคุณไม่ได้ใช้ NLOG v4.4 หรือใหม่กว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.