Bagaimana cara menambahkan logging ke perpustakaan sehingga dapat diintegrasikan dengan mudah dengan sistem logging dari program menggunakan perpustakaan?

9

Saya menulis perpustakaan yang memiliki banyak informasi yang dapat digunakan dalam log di program yang menggunakannya, tapi saya tidak tahu cara terbaik untuk mengeksposnya sedemikian rupa sehingga program yang menggunakan perpustakaan saya bisa mengintegrasikan log perpustakaan saya dengan lognya sendiri tanpa terlihat (jika diinginkan).

Memilih perpustakaan logging khusus untuk perpustakaan saya menambah daftar dependensi untuk menggunakan perpustakaan saya serta mengikat program utama ke perpustakaan itu - dan jika beberapa perpustakaan yang digunakan oleh program utama melakukan ini, masing-masing bisa memilih perpustakaan yang berbeda .

Meskipun saya sudah punya program mendaftarkan objek aliran C ++ dengan perpustakaan untuk menggunakannya. Sepertinya itu akan menjadi tujuan yang relatif umum, tetapi saya juga berpikir tentang hanya memiliki program utama mendaftarkan fungsi callback yang akan dipanggil dengan konten dan metadata ketika data dicatat. Opsi lain hanya akan menyimpan data log di perpustakaan dalam semacam daftar untuk program utama untuk mengambil setiap kali ingin menangani data itu, membiarkan program utama memutuskan kapan waktu untuk menangani data.

Saya mencari saran dan kelebihan pendekatan yang berbeda sehingga saya dapat memutuskan apa yang terbaik dalam situasi saya.

xaxxon
sumber
1
Tidak benar-benar jawaban tetapi saya sarankan Anda melihat bagaimana toolkit Qt melakukannya. Google QtMessageHandler. Dan QMessageLogger. Ini sangat mirip dengan apa yang disarankan jawaban lain, btw.
Teimpz

Jawaban:

8

Anda dapat mengekspos beberapa metode untuk menerima penebangan dari perpustakaan Anda, dan membungkus semua kecuali satu di adapter ke yang "asli" yang digunakan di perpustakaan.

Misalnya Anda memutuskan untuk secara internal memiliki std::function<void(std::string)>koleksi yang setiap panggilan balik logger. Anda memberikan:

void registerLogCallback(std::function<void(std::string)> callback); 
// Main logging implemention

dan juga

registerLogStream(std::ostream stream) { 
    registerLogCallback([stream](std::string message){ stream << message; }); 
}

dan juga

template<typename OutputIterator>
registerLogOutputIterator(OutputIterator iter) { 
    registerLogCallback([iter](std::string message){ *iter++ = message; }); 
}

dan variasi jenis "terima string dari suatu tempat" lagi yang ingin Anda terapkan untuk adaptor.

Caleth
sumber
Saya pikir pada akhirnya juga akan membutuhkan semacam "tingkat logging", seperti debug, peringatan, fatal, ... sehingga pengguna dapat menyaring apa yang dia butuhkan. Meskipun demikian, awal yang bagus.
Teimpz
4

Cara paling sederhana untuk memungkinkan aplikasi untuk dapat masuk ke fungsionalitas logging adalah memungkinkannya untuk mendaftarkan kelas / fungsi untuk menerima pesan log. Apa yang mereka lakukan dengan pesan itu sepenuhnya tergantung pada aplikasi.

Menggunakan C / C ++, Anda bisa menggunakan yang berikut di perpustakaan Anda:

typedef void (*LogMessageReceiver)(char const* message,
                                   void* user_data);

void registerLogMessageReceiver(LogMessageReceiver receiver,
                                void* user_data);

Aplikasi dapat mendaftarkan suatu fungsi dengan menelepon registerLogMessageReceiver. dengan tepat user_data. Di bagian logging basis kode Anda, Anda harus memastikan untuk memanggil fungsi itu dengan pesan yang sesuai dan terdaftar user_data.

Jika Anda tidak perlu khawatir tentang C, Anda bisa menggunakan kelas sebagai penerima pesan.

struct LogMessageReceiver
{
   virtual ~LogMessageReceiver() {}
   virtual void receive(std::string const& message) = 0;
};

dan menambahkan fungsi di perpustakaan untuk memungkinkan aplikasi mendaftarkan penerima pesan log.

void registerLogMessageReceiver(LogMessageReceiver* receiver);

Aplikasi dapat mendaftar LogMessageReceiverdengan memanggil fungsi di atas. Anda harus membuat beberapa keputusan kebijakan mengenai kepemilikan yang terdaftar LogMessageReceiver. Jika perpustakaan mengambil kepemilikan penerima, itu harus deletepointer. Jika perpustakaan tidak mengambil kepemilikan penerima, aplikasi harus mengurus deletepenerima.

Menggunakan kelas sebagai penerima pesan log memungkinkan user_databit dihilangkan registerLogMessageReceiverkarena sub-jenis LogMessageReceiverbebas untuk menyimpan data apa pun yang berguna agar berfungsi. Tidak perlu mengirimkan data pengguna tambahan apa pun dalam receivefungsinya.

Dari sana, ini bisa menjadi lebih kompleks tergantung pada seberapa canggih mekanisme logging Anda.

Misalnya, Anda bisa memiliki berbagai level logging: Ringkas, Normal, Verbose, atau LoggingLevel1, LoggingLevel2, ..., LoggingLevelN.

Dalam hal ini, Anda harus mengizinkan aplikasi mengontrol tingkat pencatatan yang ingin mereka gunakan.

Ada serangkaian pilihan tanpa akhir begitu Anda memutuskan untuk bergerak melampaui mekanisme logging yang disederhanakan. Tidak masuk akal untuk menyelidiki mereka di sini.

R Sahu
sumber
Ini adalah jawaban yang bagus jika kompiler pra-C ++ 11 perlu didukung. Jika tidak, saya akan mendukung jawaban Caleth. std :: function memungkinkan menangkap yang merupakan alternatif yang jauh lebih aman dari kekosongan *
Teimpz
1

Saya akan menegaskan bahwa Anda harus memikirkan kembali perlunya logging ditambah dengan perpustakaan Anda; Khusus untuk C ++ di mana tidak ada antarmuka logger standar.

Aplikasi yang berbeda memiliki kebijakan yang berbeda tentang pencatatan. Perpustakaan harus agnostik kebijakan.

Tujuan perpustakaan adalah untuk menyediakan layanan, dan lebih disukai, menunjukkan apakah permintaan untuk layanan itu berhasil atau gagal; idealnya dengan indikasi mengapa [gagal] melalui errno, kembalikan kode, pengecualian ... Jika keinginan Anda untuk login adalah karena fungsi yang disediakan dapat gagal di banyak tempat, Anda mungkin mencoba melakukan terlalu banyak dalam satu fungsi. Mungkin tidak , tetapi pertimbangkan kemungkinannya.

Daniel
sumber
@xaxxon Saya tidak melihat mengapa ini bukan jawaban. Menurut Bagaimana cara saya menulis jawaban yang baik? sebuah [...] answer can be “don’t do that" [...].
gandakan Anda
1

Menulis pustaka yang mudah berinteraksi dengan sistem log aplikasi host adalah mudah, asalkan Anda tahu apa sistem itu. Ketika ada banyak kemungkinan untuk itu, itu lebih sulit, dan Anda dibatasi oleh tirani penyebut umum terendah. Anda bisa memiliki lapisan kompatibilitas yang mengadaptasi pustaka Anda dengan berbagai sistem log, tetapi sekarang Anda tidak dapat bergantung pada beberapa fitur unik dan bermanfaat yang hanya dimiliki oleh satu sistem. Jika pengguna akhir memiliki sistem lain, fitur unik yang bermanfaat itu tidak ada.

Di dunia .Net, ada (setidaknya) dua sistem log yang umum digunakan, log4net dan NLog. Keduanya serupa, tetapi tidak identik. Saya sudah menggunakan log4net, dan kemudian mulai menggunakan NHibernate (perpustakaan ORM). Untungnya, ia menggunakan log4net secara internal, jadi mudah untuk menambahkannya ke proyek. (Dan di sini saya tidak setuju dengan jawaban @ Daniel: Terkadang sangat berguna bagi NHibernate untuk mencatat aktivitasnya dengan detail halus dalam sistem yang sama yang saya gunakan di tempat lain, tanpa kerja tambahan.) Jika saya sudah berinvestasi di NLog, itu berarti baik Saya beralih, atau memiliki proyek yang menggunakan keduanya.

Setidaknya dengan pencatatan, biasanya ada opsi untuk membuat append pesan khusus yang dapat Anda konfigurasikan untuk meneruskan pesan yang masuk dalam satu sistem ke sistem pencatatan yang lain. Jadi, jika sudah menggunakan NLog, dan benar-benar ingin melanjutkannya, tetapi juga menggunakan NHibernate, saya dapat menggeretakkan giginya dengan menulis log4net appender yang meneruskan setiap pesan ke NLog. Akan mudah dilakukan jika API serupa.

Alternatifnya adalah benar-benar memilih sistem terbaik untuk kebutuhan Anda yang tersedia dan menggunakannya tanpa syarat, atau memiliki semacam lapisan adaptor, yang tunduk pada masalah penyebut umum terendah. Itu bukan jawaban yang sangat memuaskan.

Carl Raymond
sumber
Ini, ditambah fakta bahwa C ++ membuat hal-hal mudah menjadi sulit dan hal-hal yang sulit menjadi semakin sulit.
rwong