Tại sao trình ghi nhật ký khuyên bạn nên sử dụng trình ghi nhật ký cho mỗi lớp?


88

Theo tài liệu của NLog:

Hầu hết các ứng dụng sẽ sử dụng một bộ ghi cho mỗi lớp, trong đó tên của bộ ghi giống với tên của lớp.

Đây cũng giống như cách log4net hoạt động. Tại sao đây là một thực hành tốt?


1
hmm. Có vẻ như có hai vấn đề ở đây - một là có một đối tượng nhật ký thực tế cho mỗi lớp và một là tên của nhật ký giống với lớp đó.
Peter Recore

Câu trả lời:


59

Với log4net, việc sử dụng một trình ghi nhật ký cho mỗi lớp giúp dễ dàng nắm bắt nguồn của thông báo nhật ký (tức là lớp ghi vào nhật ký). Nếu bạn không có một trình ghi nhật ký cho mỗi lớp, nhưng thay vào đó có một trình ghi nhật ký cho toàn bộ ứng dụng, bạn cần sử dụng nhiều thủ thuật phản ánh hơn để biết thông báo nhật ký đến từ đâu.

So sánh những điều sau:

Nhật ký mỗi lớp

using System.Reflection;
private static readonly ILog _logger = 
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);    

public void SomeMethod()
{
    _logger.DebugFormat("File not found: {0}", _filename);
}

Một trình ghi nhật ký cho mỗi ứng dụng (hoặc tương tự)

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller

-- or --

Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller

Sử dụng ví dụ thứ hai, Người ghi nhật ký sẽ cần phải xây dựng một dấu vết ngăn xếp để xem ai đang gọi nó hoặc mã của bạn sẽ luôn phải chuyển trong trình gọi. Với kiểu ghi nhật ký cho mỗi lớp, bạn vẫn làm điều này, nhưng bạn có thể thực hiện một lần cho mỗi lớp thay vì một lần cho mỗi cuộc gọi và loại bỏ vấn đề hiệu suất nghiêm trọng.


Cảm ơn, điều đó giúp làm sáng tỏ mọi thứ. Chúng tôi chỉ đưa tên lớp và phương thức vào thông báo theo cách thủ công (tức là "ImageCreator.CreateThumbnail () được gọi là"), nhưng sẽ tốt hơn nếu trình ghi có thể xử lý nó.
Daniel T.

1
Chỉ là FYI, việc có một Bộ ghi nhật ký cho mỗi cá thể, thay vì cho mỗi lớp (tức là tĩnh) trở nên "tốt hơn" vì điều đó làm cho việc nắm bắt thông tin như thông tin luồng dễ dàng hơn. Rõ ràng đó là một vấn đề của hương vị, không có "quy tắc cứng và nhanh", nhưng tôi chỉ muốn loại bỏ điều đó.
Will Hartung

7
@will, bạn có thể giải thích thêm một chút không? Khi ghi nhật ký bằng Logger per Class, tôi luôn ghi lại ID luồng để trình ghi nhật ký có thể lấy thông tin luồng hiện tại. Bất kỳ thông tin chủ đề nào khác cũng sẽ có sẵn cho trình ghi nhật ký.
Jeremy Wiebe

@Jeremy Wiebe: Đây có phải là lý do duy nhất? Về mặt chức năng, không có vấn đề gì nếu tôi sử dụng biến toàn cục duy nhất của trình ghi loại cho toàn bộ ứng dụng?
giorgim

1
@Giorgi Không, tôi không nghĩ vậy. Bạn có thể nhận được rất nhiều thông tin này những ngày này với các thuộc tính CallerInformation mà làm cho một logger mỗi lớp một chút ít liên quan - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe

15

Lợi thế khi sử dụng "trình ghi trên mỗi tệp" trong NLog: bạn có khả năng quản lý / lọc nhật ký theo không gian tên và tên lớp. Thí dụ:

<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 có một đoạn mã rất hữu ích để làm điều này. Các nloggerđoạn mã sẽ tạo ra đoạn mã sau:

private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

Vì vậy, chỉ có một vài lần nhấn phím và bạn có trình ghi nhật ký cho mỗi lớp. Nó sẽ sử dụng không gian tên và tên lớp làm tên của trình ghi nhật ký. Để đặt tên khác cho trình ghi lớp của bạn, bạn có thể sử dụng điều này:

private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");

Và, như @JeremyWiebe đã nói, bạn không cần phải sử dụng thủ thuật để lấy tên của lớp đang cố gắng ghi thông báo: Tên của trình ghi nhật ký (thường là tên của lớp) có thể dễ dàng ghi vào tệp (hoặc mục tiêu khác) bằng cách sử dụng ${logger}trong bố cục.


5

Tôi có thể thấy một vài lý do cho sự lựa chọn này.

  • Bạn sẽ luôn biết một câu lệnh nhật ký cụ thể đến từ đâu, nếu bạn bao gồm tên của bộ ghi nhật ký trong định dạng đầu ra nhật ký của mình.
  • Bạn có thể kiểm soát những báo cáo nhật ký nào bạn thấy ở cấp độ chi tiết bằng cách bật hoặc tắt một số trình ghi nhật ký nhất định hoặc đặt cấp độ của chúng.

4

Ngoài ra còn có một lợi ích về hiệu suất trong trường hợp NLog. Hầu hết người dùng sẽ sử dụng

Logger logger = LogManager.GetCurrentClassLogger()

Việc tra cứu lớp hiện tại từ dấu vết ngăn xếp có một số hiệu suất (nhưng không nhiều).


3

Trong hầu hết các trường hợp, tên của lớp cung cấp một tên tốt cho trình ghi nhật ký. Khi quét các tệp nhật ký, bạn có thể thấy thông báo nhật ký và liên kết trực tiếp nó với một dòng mã.

Một ví dụ điển hình mà đây không phải là cách tiếp cận tốt nhất là nhật ký SQL của Hibernate. Có một trình ghi nhật ký được chia sẻ tên là "Hibernate.SQL" hoặc một cái gì đó tương tự, nơi một số lớp khác nhau ghi SQL thô ra một danh mục trình ghi nhật ký duy nhất.


2

Từ quan điểm phát triển, thật dễ dàng nhất nếu bạn không phải tạo một đối tượng ghi nhật ký mỗi lần. Mặt khác, nếu bạn không làm như vậy, mà thay vào đó bạn tạo nó động bằng cách sử dụng phản chiếu, nó sẽ làm chậm hiệu suất. Để giải quyết vấn đề này, bạn có thể sử dụng mã sau để tạo trình ghi nhật ký động không đồng bộ:

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

Hai lý do ngay lập tức xuất hiện trong tâm trí:

  1. Có một nhật ký riêng biệt cho mỗi lớp giúp dễ dàng nhóm tất cả các thông báo / lỗi nhật ký liên quan đến một lớp nhất định lại với nhau.
  2. Có nhật ký trong một lớp cho phép bạn ghi lại các chi tiết nội bộ có thể không truy cập được bên ngoài lớp (ví dụ: trạng thái riêng tư, thông tin liên quan đến việc triển khai của một lớp, v.v.).

2
Bạn có một trình ghi nhật ký trong lớp bất kể nó được định nghĩa ở cấp lớp hay toàn cục. Global logger không "bên ngoài" lớp từ góc độ khả năng hiển thị. Bạn vẫn đang tham chiếu trình ghi nhật ký toàn cầu từ trong lớp được đề cập để bạn có toàn quyền hiển thị.
Robert

0

Có thể vì bạn muốn có thể ghi các phương thức chỉ hiển thị cho lớp mà không phá vỡ tính đóng gói, điều này cũng giúp bạn dễ dàng sử dụng lớp trong ứng dụng khác mà không phá vỡ chức năng ghi.


1
Nó làm cho nó khó sử dụng lớp đó trong một ứng dụng khác. Bạn phải tham khảo thư viện ghi nhật ký cho dù bạn muốn hay không.
Piotr Perak

0

Giúp dễ dàng định cấu hình phần phụ theo không gian tên hoặc lớp.


0

Nếu bạn đang sử dụng NLOG, bạn có thể chỉ định vùng gọi trong cấu hình, điều này sẽ ghi lại tên lớp và phương thức nơi đặt câu lệnh ghi nhật ký.

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

Sau đó, bạn có thể sử dụng một hằng số cho tên trình ghi của bạn hoặc tên hợp ngữ.

Tuyên bố từ chối trách nhiệm: Tôi không biết NLOG thu thập thông tin này như thế nào, tôi đoán là phản ánh nên bạn có thể cần xem xét hiệu suất. Có một số vấn đề với các phương thức Async nếu bạn không sử dụng NLOG v4.4 trở lên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.