hmm. tampaknya ada dua masalah di sini - satu memiliki objek log aktual per kelas, dan yang lainnya memiliki nama log yang sama dengan kelasnya.
Peter Recore
Jawaban:
59
Dengan log4net, menggunakan satu pencatat per kelas membuatnya mudah untuk menangkap sumber pesan log (mis. Kelas yang menulis ke log). Jika Anda tidak memiliki satu pencatat per kelas, tetapi memiliki satu pencatat untuk seluruh aplikasi, Anda perlu menggunakan lebih banyak trik refleksi untuk mengetahui dari mana pesan log tersebut berasal.
Bandingkan berikut ini:
Log per kelas
using System.Reflection;
privatestaticreadonly ILog _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
publicvoidSomeMethod()
{
_logger.DebugFormat("File not found: {0}", _filename);
}
Satu logger per aplikasi (atau serupa)
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller
-- or --
Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
Menggunakan contoh kedua, Logger perlu membuat pelacakan tumpukan untuk melihat siapa yang memanggilnya atau kode Anda harus selalu diteruskan ke pemanggil. Dengan gaya logger-per-class, Anda masih melakukan ini, tetapi Anda dapat melakukannya sekali per kelas, bukan sekali per panggilan dan menghilangkan masalah kinerja yang serius.
Terima kasih, itu membantu memperjelas banyak hal. Kami hanya memasukkan nama kelas dan metode ke dalam pesan secara manual (yaitu "ImageCreator.CreateThumbnail () dipanggil"), tetapi akan lebih baik jika logger dapat menanganinya.
Daniel T.
1
Untuk diketahui, praktiknya menjadi "lebih baik" untuk memiliki Logger per instance, daripada per kelas (yaitu statis) karena itu membuatnya lebih mudah untuk menangkap informasi seperti informasi thread. Jelas ini masalah selera, bukan "aturan keras dan cepat", tapi saya ingin membuangnya begitu saja.
Will Hartung
7
@Apakah, dapatkah Anda menjelaskannya sedikit lagi? Saat masuk menggunakan Pencatat per Kelas, saya selalu mencatat ID utas sehingga pencatat bisa mendapatkan informasi utas saat ini. Info utas lainnya juga akan tersedia untuk logger.
Jeremy Wiebe
@ Jeremy Wiebe: Apakah ini satu-satunya alasan? Secara fungsional tidak ada masalah jika saya menggunakan variabel global tunggal tipe logger untuk seluruh aplikasi?
gmoniava
1
@ Giorgi Tidak, kurasa tidak. Anda bisa mendapatkan banyak informasi ini hari ini dengan atribut CallerInformation yang membuat satu logger per kelas menjadi sedikit kurang relevan - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe
16
Keuntungan menggunakan "logger per file" di NLog: Anda memiliki kemungkinan untuk mengelola / memfilter log dengan namespace dan nama kelas. Contoh:
<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 memiliki potongan kode yang sangat berguna untuk melakukan ini. The nloggerpotongan akan membuat kode berikut:
Jadi hanya sedikit penekanan tombol dan Anda memiliki logger per kelas. Ini akan menggunakan namespace dan nama kelas sebagai nama logger. Untuk menyetel nama yang berbeda ke logger kelas Anda, Anda dapat menggunakan ini:
Dan, seperti yang dikatakan @JeremyWiebe, Anda tidak perlu menggunakan trik untuk mendapatkan nama kelas yang mencoba memasukkan pesan: Nama logger (biasanya nama kelas) dapat dengan mudah masuk ke file (atau target lain) dengan menggunakan ${logger}in layout.
Saya dapat melihat beberapa alasan untuk pilihan ini.
Anda akan selalu tahu dari mana pernyataan log tertentu berasal, jika Anda menyertakan nama logger dalam format keluaran log Anda.
Anda dapat mengontrol pernyataan log apa yang Anda lihat pada tingkat yang sangat detail dengan mengaktifkan atau menonaktifkan penebang tertentu, atau menyetel levelnya.
Dalam banyak kasus, nama kelas memberikan nama yang bagus untuk logger. Saat memindai file log, Anda dapat melihat pesan log dan mengaitkannya secara langsung dengan sebaris kode.
Contoh yang bagus di mana ini bukan pendekatan terbaik, adalah log SQL Hibernate. Ada logger bersama bernama "Hibernate.SQL" atau semacamnya, di mana sejumlah kelas yang berbeda menulis SQL mentah ke kategori logger tunggal.
Dari sudut pandang pengembangan, paling mudah jika Anda tidak perlu membuat objek logger setiap saat. Di sisi lain, jika Anda tidak melakukannya, tetapi Anda membuatnya secara dinamis menggunakan refleksi, itu akan memperlambat kinerja. Untuk mengatasi ini, Anda dapat menggunakan kode berikut yang membuat logger secara dinamis secara asinkron:
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespaceWinForms
{
classlog
{
publicstaticasyncvoidLog(int severity, string message)
{
await Task.Run(() => LogIt(severity, message));
}
privatestaticvoidLogIt(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-levelsstring[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" };
int level = Math.Min(levels.Length, severity);
theLogger.Log(LogLevel.FromOrdinal(level), message);
}
}
}
Memiliki log terpisah untuk setiap kelas memudahkan pengelompokan semua pesan / error log yang berkaitan dengan kelas tertentu.
Memiliki log di dalam kelas memungkinkan Anda untuk mencatat detail internal yang mungkin tidak dapat diakses di luar kelas (misalnya, status privat, informasi yang berhubungan dengan implementasi kelas, dll.).
Anda memiliki logger di kelas terlepas dari apakah itu ditentukan di tingkat kelas atau secara global. Logger global tidak berada "di luar" kelas dari perspektif visibilitas. Anda masih mereferensikan logger global dari dalam kelas tersebut sehingga Anda memiliki visibilitas penuh.
Robert
0
Mungkin karena Anda ingin dapat membuat log metode yang hanya terlihat oleh kelas tanpa merusak enkapsulasi, hal ini juga memudahkan penggunaan kelas dalam aplikasi lain tanpa merusak fungsionalitas logging.
Jika Anda menggunakan NLOG Anda dapat menentukan situs panggilan di konfigurasi, ini akan mencatat nama kelas dan metode di mana pernyataan pencatatan berada.
<property name="CallSite"value="${callsite}" />
Anda kemudian dapat menggunakan konstanta untuk nama logger Anda atau nama rakitan.
Penafian: Saya tidak tahu bagaimana NLOG mengumpulkan informasi ini, dugaan saya akan menjadi refleksi jadi Anda mungkin perlu mempertimbangkan kinerjanya. Ada beberapa masalah dengan metode Async jika Anda tidak menggunakan NLOG v4.4 atau yang lebih baru.
Jawaban:
Dengan log4net, menggunakan satu pencatat per kelas membuatnya mudah untuk menangkap sumber pesan log (mis. Kelas yang menulis ke log). Jika Anda tidak memiliki satu pencatat per kelas, tetapi memiliki satu pencatat untuk seluruh aplikasi, Anda perlu menggunakan lebih banyak trik refleksi untuk mengetahui dari mana pesan log tersebut berasal.
Bandingkan berikut ini:
Log per kelas
using System.Reflection; private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public void SomeMethod() { _logger.DebugFormat("File not found: {0}", _filename); }
Satu logger per aplikasi (atau serupa)
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller -- or -- Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
Menggunakan contoh kedua, Logger perlu membuat pelacakan tumpukan untuk melihat siapa yang memanggilnya atau kode Anda harus selalu diteruskan ke pemanggil. Dengan gaya logger-per-class, Anda masih melakukan ini, tetapi Anda dapat melakukannya sekali per kelas, bukan sekali per panggilan dan menghilangkan masalah kinerja yang serius.
sumber
Keuntungan menggunakan "logger per file" di NLog: Anda memiliki kemungkinan untuk mengelola / memfilter log dengan namespace dan nama kelas. Contoh:
<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 memiliki potongan kode yang sangat berguna untuk melakukan ini. The
nlogger
potongan akan membuat kode berikut:private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
Jadi hanya sedikit penekanan tombol dan Anda memiliki logger per kelas. Ini akan menggunakan namespace dan nama kelas sebagai nama logger. Untuk menyetel nama yang berbeda ke logger kelas Anda, Anda dapat menggunakan ini:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
Dan, seperti yang dikatakan @JeremyWiebe, Anda tidak perlu menggunakan trik untuk mendapatkan nama kelas yang mencoba memasukkan pesan: Nama logger (biasanya nama kelas) dapat dengan mudah masuk ke file (atau target lain) dengan menggunakan
${logger}
in layout.sumber
Saya dapat melihat beberapa alasan untuk pilihan ini.
sumber
Ada juga keuntungan kinerja dalam kasus NLog. Sebagian besar pengguna akan menggunakan
Mencari kelas saat ini dari pelacakan tumpukan membutuhkan beberapa (tetapi tidak banyak) kinerja.
sumber
Dalam banyak kasus, nama kelas memberikan nama yang bagus untuk logger. Saat memindai file log, Anda dapat melihat pesan log dan mengaitkannya secara langsung dengan sebaris kode.
Contoh yang bagus di mana ini bukan pendekatan terbaik, adalah log SQL Hibernate. Ada logger bersama bernama "Hibernate.SQL" atau semacamnya, di mana sejumlah kelas yang berbeda menulis SQL mentah ke kategori logger tunggal.
sumber
Dari sudut pandang pengembangan, paling mudah jika Anda tidak perlu membuat objek logger setiap saat. Di sisi lain, jika Anda tidak melakukannya, tetapi Anda membuatnya secara dinamis menggunakan refleksi, itu akan memperlambat kinerja. Untuk mengatasi ini, Anda dapat menggunakan kode berikut yang membuat logger secara dinamis secara asinkron:
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); } } }
sumber
Dua alasan segera muncul di benak:
sumber
Mungkin karena Anda ingin dapat membuat log metode yang hanya terlihat oleh kelas tanpa merusak enkapsulasi, hal ini juga memudahkan penggunaan kelas dalam aplikasi lain tanpa merusak fungsionalitas logging.
sumber
Memudahkan untuk mengonfigurasi appender berdasarkan namespace atau kelas.
sumber
Jika Anda menggunakan NLOG Anda dapat menentukan situs panggilan di konfigurasi, ini akan mencatat nama kelas dan metode di mana pernyataan pencatatan berada.
<property name="CallSite" value="${callsite}" />
Anda kemudian dapat menggunakan konstanta untuk nama logger Anda atau nama rakitan.
Penafian: Saya tidak tahu bagaimana NLOG mengumpulkan informasi ini, dugaan saya akan menjadi refleksi jadi Anda mungkin perlu mempertimbangkan kinerjanya. Ada beberapa masalah dengan metode Async jika Anda tidak menggunakan NLOG v4.4 atau yang lebih baru.
sumber