中文文档
BqLog adalah sistem logging ringan dan berkinerja tinggi yang digunakan dalam proyek seperti "Honor of Kings", dan telah berhasil diterapkan serta berjalan dengan lancar.
jendela 64bit
macOS
Linux
iOS
Android (X86_64, arm64-v8a, armeabi-v7a)
Unix (Lulus tes di FreeBSD)
C++
Jawa
Kotlin
C#
Dibandingkan dengan pustaka logging sumber terbuka yang sudah ada, BqLog menawarkan keunggulan performa yang signifikan (lihat Tolok Ukur). Ini tidak hanya cocok untuk server dan klien tetapi juga sangat kompatibel dengan perangkat seluler.
Dengan konsumsi memori yang rendah, dalam kasus Benchmark 10 thread dan 20.000.000 entri log, BqLog sendiri mengonsumsi kurang dari 1 MB memori.
Menyediakan format log real-time berkinerja tinggi dan kompresi tinggi
Dapat digunakan secara normal di mesin game ( Unity
, Unreal
), dengan dukungan untuk tipe umum yang disediakan untuk Unreal.
Mendukung karakter dan string UTF-8
, UTF-16
, UTF-32
, serta tipe parameter umum seperti bool, float, double, dan berbagai panjang dan tipe bilangan bulat
Mendukung format specifications
C++20
Logging asinkron mendukung tinjauan kerusakan untuk menghindari kehilangan data (terinspirasi oleh XLog)
Ukurannya sangat kecil, dengan perpustakaan dinamis hanya sekitar 200 ribu setelah kompilasi Android
Tidak menghasilkan alokasi heap tambahan di Java dan C#, menghindari pembuatan objek baru secara konstan selama runtime
Hanya bergantung pada pustaka bahasa C standar dan API platform, dan dapat dikompilasi dalam mode ANDROID_STL = none
Android
Mendukung C++11
dan standar kompilasi yang lebih baru, dan dapat dikompilasi berdasarkan persyaratan ketat -Wall -Wextra -pedantic -Werror
Modul kompilasi didasarkan pada CMake
dan menyediakan skrip kompilasi untuk berbagai platform, sehingga mudah digunakan
Mendukung jenis parameter khusus
Sangat ramah terhadap saran kode
Mengapa BqLog begitu cepat - Format Log Terkompresi Waktu Nyata Berkinerja Tinggi
Mengapa BqLog begitu cepat - Ring Buffer Konkurensi Tinggi
Mengintegrasikan BqLog ke dalam Proyek Anda
Demo Sederhana
Ikhtisar Arsitektur
Petunjuk Penggunaan API Proses Utama
1-Membuat Objek Log
2-Mengambil Objek Log
3-Mencatat Pesan
4-API lainnya
Logging Sinkron dan Asinkron
1. Keamanan thread pada logging asinkron
Pengantar Appender
1. KonsolPenambahan
2. Penambah File Teks
3. CompressedFileAppender (Sangat Direkomendasikan)
4.Penambahan File Mentah
Petunjuk Konfigurasi
1. Contoh Lengkap
2. Penjelasan Mendetail
Decoding Offline dari Penambah Format Biner
Petunjuk Pembuatan
1. Pembangunan Perpustakaan
2. Demo Bangun dan Jalankan
3. Petunjuk Uji Coba Otomatis
4. Petunjuk Jalankan Tolok Ukur
Topik Penggunaan Tingkat Lanjut
1. Tidak Ada Alokasi Tumpukan
2. Mencatat Objek dengan Dukungan Kategori
3. Perlindungan Data pada Program Keluar Tidak Normal
4. Tentang NDK dan ANDROID_STL = tidak ada
5. Jenis Parameter Khusus
6. Menggunakan BqLog di Unreal Engine
Tolok ukur
1. Deskripsi Tolok Ukur
2. Kode Tolok Ukur BqLog C++
3. Kode Tolok Ukur Java BqLog
4. Kode Tolok Ukur Log4j
5. Hasil Tolok Ukur
BqLog dapat diintegrasikan ke dalam proyek Anda dalam berbagai bentuk. Untuk C++, ini mendukung perpustakaan dinamis, perpustakaan statis, dan file sumber. Untuk Java dan C#, ini mendukung perpustakaan dinamis dengan kode sumber wrapper. Di bawah ini adalah metode untuk memasukkan BqLog:
Repositori kode mencakup file perpustakaan dinamis yang telah dikompilasi yang terletak di /dist/dynamic_lib/. Untuk mengintegrasikan BqLog ke dalam proyek Anda menggunakan file perpustakaan, Anda perlu melakukan hal berikut:
Pilih file perpustakaan dinamis yang sesuai dengan platform Anda dan tambahkan ke sistem pembangunan proyek Anda.
Salin direktori /dist/dynamic_lib/include ke proyek Anda dan tambahkan ke daftar direktori include. (Jika Anda menggunakan pustaka .framework XCode, Anda dapat melewati langkah ini karena file .framework sudah menyertakan file header).
Repositori kode mencakup file perpustakaan statis yang telah dikompilasi yang terletak di /dist/static_lib/. Untuk mengintegrasikan BqLog ke dalam proyek Anda menggunakan file perpustakaan, Anda perlu melakukan hal berikut:
Pilih file perpustakaan statis yang sesuai dengan platform Anda dan tambahkan ke sistem pembangunan proyek Anda.
Salin direktori /dist/static_lib/include ke proyek Anda dan tambahkan ke daftar direktori include. (Jika Anda menggunakan pustaka .framework XCode, Anda dapat melewati langkah ini karena file .framework sudah menyertakan file header).
BqLog juga mendukung penyertaan langsung kode sumber ke dalam proyek Anda untuk kompilasi. Untuk mengintegrasikan BqLog menggunakan kode sumber, ikuti langkah-langkah berikut:
Salin direktori /src ke proyek Anda sebagai referensi kode sumber.
Salin direktori /include ke proyek Anda dan tambahkan ke daftar direktori include.
Jika mengkompilasi versi Windows di Visual Studio, tambahkan /Zc:__cplusplus ke opsi kompilasi untuk memastikan dukungan standar kompiler C++ saat ini ditentukan dengan benar.
Jika menggunakan kode sumber di NDK Android, silakan merujuk ke 4. Tentang NDK dan ANDROID_STL = none untuk pertimbangan penting.
Di C#, BqLog dapat digunakan melalui perpustakaan dinamis asli dan C# Wrapper, mendukung mesin Mono, Microsoft CLR, dan Unity. Unity kompatibel dengan mode Mono dan IL2CPP. Untuk menggunakan BqLog di C#, ikuti langkah-langkah berikut:
Pilih file perpustakaan dinamis yang sesuai dengan platform Anda dari /dist/dynamic_lib/ dan tambahkan ke proyek Anda (untuk Unity, lihat Impor Unity dan konfigurasikan plug-in).
Salin file kode sumber dari /wrapper/csharp/src ke proyek Anda.
Di Java, BqLog dapat digunakan melalui perpustakaan dinamis asli dan Java Wrapper, mendukung lingkungan JVM umum dan Android. Untuk mengintegrasikan BqLog ke JVM, ikuti langkah-langkah berikut:
Pilih file perpustakaan dinamis yang sesuai dengan platform Anda dari /dist/dynamic_lib/ dan tambahkan ke proyek Anda.
Salin file kode sumber dari /wrapper/java/src ke proyek Anda.
(Opsional) Salin direktori /dist/dynamic_lib/include ke proyek Anda dan tambahkan ke daftar direktori include jika Anda ingin memanggil BqLog dari NDK.
Kode berikut akan menampilkan lebih dari 1000 log ke konsol Anda (atau ADB Logcat jika di Android)
#jika ditentukan (WIN32) #include <windows.h>#endif#include <string>#include <bq_log/bq_log.h>int main() { #jika ditentukan(WIN32) // Alihkan baris perintah Windows ke UTF-8 karena BqLog mengeluarkan semua teks akhir dalam pengkodean UTF-8 untuk menghindari masalah tampilan SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // String ini adalah konfigurasi log. Di sini ia mengonfigurasi logger dengan satu appender (target keluaran) bernama appender_0, yang menghasilkan keluaran ke konsol. std::string config = R"( # Target keluaran appender ini adalah konsol appenders_config.appender_0.type=console # Appender ini menggunakan waktu lokal untuk stempel waktu appenders_config.appender_0.time_zone=default waktu lokal # Appender ini mengeluarkan log dari 6 level ini (tidak ada spasi di antaranya) appenders_config.appender_0.levels=[verbose,debug,info,peringatan,kesalahan,fatal] )"; bq::log log = bq::log::create_log("my_first_log", config); // Membuat objek log menggunakan konfigurasi for(int i = 0; i < 1024; ++i) { log.info("Ini adalah log pengujian info, format stringnya adalah UTF-8, param int:{}, param bool :{}, param string8:{}, param string16:{}, param string32:{} , param float:{}", i, true, "utf8-string", u"utf16-string", U"utf32-string", 4.3464f); } log.error(U"Ini adalah log pengujian kesalahan, format stringnya adalah UTF-32"); bq::log::force_flush_all_logs(); // BqLog defaultnya adalah keluaran asinkron. Untuk memastikan log terlihat sebelum program keluar, paksa flush untuk menyinkronkan output satu kali. kembali 0; }
menggunakan Sistem.Teks;menggunakan Sistem;kelas publik demo_main { public static void Main(string[] args) { Console.OutputEncoding = Pengkodean.UTF8; Konsol.InputEncoding = Pengkodean.UTF8; string config = @" # Target keluaran appender ini adalah konsol appenders_config.appender_0.type=console # Appender ini menggunakan waktu lokal untuk stempel waktu ppenders_config.appender_0.time_zone=default waktu lokal # Appender ini mengeluarkan log dari 6 level ini (tidak ada spasi di dalamnya antara) appenders_config.appender_0.levels=[verbose,debug,info,peringatan,kesalahan,fatal] "; bq.log log = bq.log.create_log("my_first_log", config); // Buat objek log menggunakan konfigurasi for (int i = 0; i < 1024; ++i) { log.info("Ini adalah log pengujian info, format stringnya adalah UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, " Teks String", 4.3464f); } bq.log.force_flush_all_logs(); Konsol.ReadKey(); }}
public class demo_main { public static void main(String[] args) { // TODO Metode yang dibuat secara otomatis stub String config = """ # Target keluaran appender ini adalah konsol appenders_config.appender_0.type=console # Appender ini menggunakan waktu lokal for timestamps appenders_config.appender_0.time_zone=waktu lokal default # Appender ini mengeluarkan log dari 6 level ini (tanpa spasi di antaranya) appenders_config.appender_0.levels=[verbose,debug,info,peringatan,kesalahan,fatal] """; bq.log log = bq.log.create_log("my_first_log", config); // Membuat objek log menggunakan konfigurasi for (int i = 0; i < 1024; ++i) { log.info("Ini adalah log pengujian info, format stringnya adalah UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "Teks String", 4.3464f); } bq.log.force_flush_all_logs(); } }
Diagram di atas dengan jelas menggambarkan struktur dasar BqLog. Di sisi kanan diagram adalah implementasi internal perpustakaan BqLog, sedangkan di sisi kiri adalah program dan kode Anda. Program Anda dapat memanggil BqLog menggunakan pembungkus yang disediakan (API berorientasi objek untuk berbagai bahasa). Dalam diagram, dua Log dibuat: satu bernama "Log A" dan yang lainnya bernama "Log B." Setiap Log dilampirkan ke satu atau lebih Appender. Appender dapat dipahami sebagai target keluaran konten log. Ini bisa berupa konsol (log ADB Logcat untuk Android), file teks, atau bahkan format khusus seperti file log terkompresi atau file format log biner biasa.
Dalam proses yang sama, pembungkus untuk bahasa berbeda dapat mengakses objek Log yang sama. Misalnya, jika objek Log bernama Log A dibuat di Java, objek tersebut juga dapat diakses dan digunakan dari sisi C++ dengan nama Log A.
Dalam kasus ekstrem, seperti game yang dikembangkan Unity yang berjalan di sistem Android, Anda mungkin melibatkan bahasa Java, Kotlin, C#, dan C++ dalam aplikasi yang sama. Mereka semua dapat berbagi objek Log yang sama. Anda dapat membuat Log di sisi Java menggunakan create_log, lalu mengaksesnya dalam bahasa lain menggunakan get_log_by_name.
Catatan: API berikut dideklarasikan di kelas bq::log (atau bq.log). Untuk menghemat ruang, hanya C++ API yang dicantumkan. API di Java dan C# identik dan tidak akan terulang di sini.
Di C++, bq::string
adalah tipe string UTF-8 di perpustakaan BqLog. Anda juga dapat meneruskan string gaya c seperti char atau std::string
atau std::string_view
, yang akan dikonversi secara otomatis dan implisit.
Objek log dapat dibuat menggunakan fungsi statis create_log. Deklarasinya adalah sebagai berikut:
//C++ API /// <summary> /// Membuat objek log /// </summary> /// <param name="log_name">Jika nama log adalah string kosong, bqLog akan secara otomatis memberi Anda a nama log yang unik. Jika nama log sudah ada, maka akan mengembalikan objek log yang sudah ada sebelumnya dan menimpa konfigurasi sebelumnya dengan konfigurasi baru.</param> /// <param name="config_content">String konfigurasi log</param> /// <returns>Objek log, jika pembuatan gagal, metode is_valid() akan mengembalikan false</returns> log statis create_log(const bq::string& log_name, const bq::string& config_content);
Kode ini membuat objek log dengan memasukkan nama objek log dan string konfigurasi. Konfigurasi log dapat direferensikan dalam Petunjuk Konfigurasi. Berikut adalah beberapa poin penting yang perlu diperhatikan:
Terlepas dari apakah itu C# atau Java, objek log yang dikembalikan tidak akan pernah bernilai null. Namun, karena kesalahan konfigurasi atau alasan lain, objek log yang tidak valid mungkin dibuat. Oleh karena itu, Anda harus menggunakan fungsi is_valid() untuk memeriksa objek yang dikembalikan. Melakukan operasi pada objek yang tidak valid dapat menyebabkan program terhenti.
Jika string kosong diteruskan sebagai nama log, bqLog akan secara otomatis menghasilkan nama log unik, seperti "AutoBqLog_1."
Memanggil create_log pada objek log yang sudah ada dengan nama yang sama tidak akan membuat objek log baru tetapi akan menimpa konfigurasi sebelumnya dengan yang baru. Namun, beberapa parameter tidak dapat diubah dalam proses ini; lihat Petunjuk Konfigurasi untuk detailnya.
Kecuali saat menggunakan di NDK (lihat 4. Tentang NDK dan ANDROID_STL = none), Anda dapat menginisialisasi objek log secara langsung dalam variabel global atau statis menggunakan API ini dalam situasi lain.
Jika objek log telah dibuat di tempat lain, Anda bisa mendapatkan objek log yang dibuat secara langsung menggunakan fungsi get_log_by_name.
//C++ API /// <summary> /// Dapatkan objek log berdasarkan namanya /// </summary> /// <param name="log_name">Nama objek log yang ingin Anda temukan</param > /// <returns>Objek log, jika objek log dengan nama tertentu tidak ditemukan, metode is_valid() akan mengembalikan false</returns> static log get_log_by_name(const bq::string& log_name);
Anda juga dapat menggunakan fungsi ini untuk menginisialisasi objek log dalam variabel global atau fungsi statis. Namun, perhatikan bahwa Anda harus memastikan objek log dengan nama yang ditentukan sudah ada. Jika tidak, objek log yang dikembalikan tidak akan dapat digunakan, dan metode is_valid()-nya akan menghasilkan false.
///Fungsi log inti, ada 6 level log: ///verbose, debug, info, peringatan, kesalahan, templat fatal<typename STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> verbose(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> verbose(const STR& log_format_content, const Args&... args) const; templat<nama ketik STR> bq::enable_if_t<is_bq_log_str<STR>::nilai, bool> debug(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> debug(const STR& log_format_content, const Args&... args) const; templat<nama ketik STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> info(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> info(const STR& log_format_content, const Args&... args) const; templat<ketik nama STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> peringatan(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> peringatan(const STR& log_format_content, const Args&... args) const; templat<nama ketik STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> error(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> error(const STR& log_format_content, const Args&... args) const; templat<nama ketik STR> bq::enable_if_t<is_bq_log_str<STR>::nilai, bool> fatal(const STR& log_content) const; templat<nama ketik STR, nama ketik...Args> bq::enable_if_t<is_bq_log_str<STR>::value, bool> fatal(const STR& log_format_content, const Args&... args) const;
Saat mencatat pesan, perhatikan tiga poin utama:
Seperti yang Anda lihat, log kami dibagi menjadi enam level: verbose, debug, info, peringatan, kesalahan, dan fatal, konsisten dengan Android. Pentingnya mereka meningkat secara berurutan. Saat dikeluarkan ke konsol, mereka akan muncul dalam warna berbeda.
Parameter STR mirip dengan parameter pertama printf dan dapat berupa berbagai tipe string yang umum, termasuk:
Java.lang.String di Java
string C#
Berbagai pengkodean string gaya C C++ dan std::string
( char*
, char16_t*
, char32_t*
, wchar_t*
, std::string
, std::u8string
, std::u16string
, std::u32string
, std::wstring
, std::string_view
, std::u16string_view
, std::u32string_view
, std::wstring_view
dan bahkan tipe string khusus, yang dapat Anda rujuk ke inCustom Parameter Types )
Anda dapat menambahkan berbagai parameter setelah parameter STR. Parameter ini akan diformat ke tempat yang ditentukan di STR, mengikuti aturan yang mirip dengan std::format C++20 (kecuali kurangnya dukungan untuk argumen posisi dan format tanggal dan waktu). Misalnya, penggunaan {} tunggal mewakili pemformatan default sebuah parameter, dan {:.2f} menetapkan presisi untuk memformat angka floating-point. Cobalah untuk menggunakan parameter yang diformat untuk mengeluarkan log daripada menggabungkan string secara manual. Pendekatan ini optimal untuk kinerja dan penyimpanan format terkompresi.
Jenis parameter yang didukung saat ini meliputi:
Pointer nol (keluaran sebagai nol)
Pointer (keluaran sebagai alamat heksadesimal dimulai dengan 0x)
bodoh
Karakter byte tunggal (char)
Karakter byte ganda (char16_t, wchar_t, char C#, char Java)
Karakter empat byte (char32_t atau wchar_t)
bilangan bulat 8-bit
Bilangan bulat 8-bit yang tidak ditandatangani
bilangan bulat 16-bit
Bilangan bulat 16-bit yang tidak ditandatangani
bilangan bulat 32-bit
Bilangan bulat 32-bit yang tidak ditandatangani
bilangan bulat 64-bit
Bilangan bulat 64-bit yang tidak ditandatangani
Angka floating-point 32-bit
Angka floating-point 64-bit
Tipe POD lain yang tidak diketahui di C++ (terbatas pada ukuran 1, 2, 4, atau 8 byte, masing-masing diperlakukan sebagai int8, int16, int32, dan int64)
String, termasuk semua tipe string yang disebutkan dalam Parameter STR
Kelas atau objek apa pun di C# dan Java (mengeluarkan string ToString())
Tipe parameter kustom, seperti yang dirinci dalam Tipe Parameter Kustom
Ada API tambahan yang umum digunakan yang dapat menyelesaikan tugas tertentu. Untuk deskripsi API mendetail, lihat bq_log/bq_log.h, serta kelas bq.log di Java dan C#. Berikut beberapa API utama yang perlu disorot:
/// <ringkasan> /// Hapus inisialisasi BqLog, harap aktifkan fungsi ini sebelum program Anda ada. /// </ringkasan> statis batal unit();
Disarankan untuk menjalankan uninit()
sebelum keluar dari program atau menghapus instalasi pustaka dinamis yang diterapkan sendiri yang menggunakan BqLog, jika tidak, program mungkin macet saat keluar dalam kondisi tertentu.
/// <ringkasan> /// Jika bqLog tidak sinkron, crash pada program dapat menyebabkan log di buffer tidak disimpan ke disk. /// Jika fitur ini diaktifkan, bqLog akan mencoba melakukan pembersihan paksa log di buffer jika terjadi kerusakan. Namun, /// fungsi ini tidak menjamin kesuksesan, dan hanya mendukung sistem POSIX. /// </ringkasan> statis batal aktifkan_auto_crash_handle();
Untuk pengenalan mendetail, lihatPerlindungan Data pada Program Keluar Tidak Normal
/// <ringkasan> /// Hapus buffer semua objek log secara bersamaan /// untuk memastikan bahwa semua data dalam buffer diproses setelah panggilan. /// </ringkasan> kekosongan statis force_flush_all_logs(); /// <ringkasan> /// Hapus buffer objek log ini secara sinkron /// untuk memastikan bahwa semua data dalam buffer diproses setelah panggilan. /// </ringkasan> batal force_flush();
Karena bqLog menggunakan logging asinkron secara default, ada kalanya Anda mungkin ingin segera menyinkronkan dan mengeluarkan semua log. Dalam kasus seperti itu, Anda perlu memanggil paksa force_flush().
/// <ringkasan> /// Daftarkan panggilan balik yang akan dipanggil setiap kali pesan log konsol dikeluarkan. /// Ini dapat digunakan oleh sistem eksternal untuk memantau keluaran log konsol. /// </summary> /// <param name="callback"></param> static void register_console_callback(bq::type_func_ptr_console_callback panggilan balik); /// <ringkasan> /// Membatalkan pendaftaran panggilan balik konsol. /// </summary> /// <param name="callback"></param> static void unregister_console_callback(bq::type_func_ptr_console_callback panggilan balik);
Output dariConsoleAppender masuk ke konsol atau log ADB Logcat di Android, namun ini mungkin tidak mencakup semua situasi. Misalnya, di mesin game khusus atau IDE khusus, mekanisme disediakan untuk memanggil fungsi panggilan balik untuk setiap keluaran log konsol. Hal ini memungkinkan Anda memproses ulang dan mengeluarkan log konsol di mana pun dalam program Anda.
Perhatian Tambahan: Jangan menampilkan log BQ tersinkronisasi apa pun dalam panggilan balik konsol karena dapat dengan mudah menyebabkan kebuntuan.
/// <ringkasan> /// Mengaktifkan atau menonaktifkan buffer penambahan konsol. /// Karena wrapper kita dapat berjalan di mesin virtual C# dan Java, dan kita tidak ingin langsung memanggil callback dari thread asli, /// kita dapat mengaktifkan opsi ini. Dengan cara ini, semua keluaran konsol akan disimpan di buffer sampai kita mengambilnya. /// </summary> /// <param name="enable"></param> /// <returns></returns> static void set_console_buffer_enable(bool aktifkan); /// <summary> /// Ambil dan hapus entri log dari buffer penambah konsol dengan cara yang aman untuk thread. /// Jika buffer penambahan konsol tidak kosong, fungsi on_console_callback akan dipanggil untuk entri log ini. /// Harap pastikan untuk tidak menampilkan log BQ yang disinkronkan dalam fungsi panggilan balik. /// </summary> /// <param name="on_console_callback">Fungsi panggilan balik yang akan dipanggil untuk entri log yang diambil jika buffer penambah konsol tidak kosong</param> /// <returns>True jika buffer konsol appender tidak kosong dan entri log diambil; jika tidak, False dikembalikan.</returns> static bool Fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback);
Selain mencegat keluaran konsol melalui callback konsol, Anda dapat secara aktif mengambil keluaran log konsol. Terkadang, kita mungkin tidak ingin keluaran log konsol datang melalui panggilan balik karena Anda tidak tahu dari thread mana panggilan balik itu berasal (misalnya, di beberapa mesin virtual C#, atau JVM, VM mungkin melakukan pengumpulan sampah saat konsol panggilan balik dipanggil, yang berpotensi menyebabkan hang atau crash).
Metode yang digunakan di sini melibatkan pengaktifan buffer konsol melalui set_console_buffer_enable
. Hal ini menyebabkan setiap keluaran log konsol disimpan dalam memori hingga kita secara aktif memanggil fetch_and_remove_console_buffer
untuk mengambilnya. Oleh karena itu, jika Anda memilih untuk menggunakan metode ini, ingatlah untuk segera mengambil dan menghapus log untuk menghindari memori yang belum dirilis.
Perhatian Tambahan: Jangan menampilkan log BQ tersinkronisasi apa pun dalam panggilan balik konsol karena dapat dengan mudah menyebabkan kebuntuan.
Perhatian Tambahan: Jika Anda menggunakan kode ini di lingkungan IL2CPP, pastikan bahwa on_console_callback ditandai sebagai statis tidak aman dan dihiasi dengan atribut [MonoPInvokeCallback(typeof(type_console_callback))].
/// <summary> /// Memodifikasi konfigurasi log, namun beberapa kolom, seperti buffer_size, tidak dapat diubah. /// </summary> /// <param name="config_content"></param> /// <returns></returns> bool reset_config(const bq::string& config_content);
Terkadang Anda mungkin ingin mengubah konfigurasi log dalam program Anda. Selain membuat ulang objek log untuk menimpa konfigurasi (Lihat Membuat Objek Log), Anda juga dapat menggunakan antarmuka reset. Namun perlu diingat bahwa tidak semua item konfigurasi dapat dimodifikasi dengan cara ini. Untuk detailnya, lihat Petunjuk Konfigurasi
/// <ringkasan> /// Menonaktifkan atau mengaktifkan Appender tertentu untuk sementara. /// </summary> /// <param name="appender_name"></param> /// <param name="enable"></param> void set_appenders_enable(const bq::string& appender_name, bool aktifkan) ;
Secara default, Appenders dalam konfigurasi aktif, tetapi mekanisme disediakan di sini untuk menonaktifkan sementara dan mengaktifkannya kembali.
/// <ringkasan> /// Hanya berfungsi ketika snapshot dikonfigurasi. /// Ini akan memecahkan kode buffer snapshot menjadi teks. /// </summary> /// <param name="use_gmt_time">apakah stempel waktu setiap log adalah waktu GMT atau waktu lokal</param> /// <returns>buffer snapshot yang didekode</returns> bq::string take_snapshot(bool use_gmt_time) const;
Terkadang, fitur khusus tertentu memerlukan keluaran bagian terakhir log, yang dapat dilakukan menggunakan fitur snapshot. Untuk mengaktifkan fitur ini, Anda harus terlebih dahulu mengaktifkan snapshot di konfigurasi log dan mengatur ukuran buffer maksimum, dalam byte. Selain itu, Anda perlu menentukan tingkat log dan kategori yang akan difilter untuk snapshot (opsional). Untuk konfigurasi mendetail, silakan merujuk ke Konfigurasi Snapshot. Ketika snapshot diperlukan, pemanggilan take_snapshot() akan mengembalikan string yang diformat yang berisi entri log terbaru yang disimpan dalam buffer snapshot. Dalam C++, tipenya adalah bq::string
, yang secara implisit dapat dikonversi menjadi std::string
.
namespace bq{ namespace tools {//Ini adalah kelas utilitas untuk mendekode format log biner. //Untuk menggunakannya, pertama-tama buat objek log_decoder, //lalu panggil fungsi decode untuk melakukan decode. //Setelah setiap panggilan berhasil, //Anda dapat menggunakan get_last_decoded_log_entry() untuk mengambil hasil dekode. //Setiap panggilan menerjemahkan satu entri log. struct log_decoder { pribadi: bq::string decode_text_; bq::appender_decode_result result_ = bq::appender_decode_result::sukses; uint32_t menangani_ = 0; public: /// <summary> /// Membuat objek log_decoder, dengan setiap objek log_decoder berhubungan dengan file log biner. /// </summary> /// <param name="log_file_path">jalur file log biner, dapat berupa jalur relatif atau jalur absolut</param> log_decoder(const bq::string& log_file_path); ~log_decoder(); /// <ringkasan> /// Dekode entri log. setiap panggilan fungsi ini hanya akan mendekode 1 entri log /// </summary> /// <returns>hasil dekode, appender_decode_result::eof berarti seluruh file log telah didekode</returns> bq::appender_decode_result decode(); /// <summary> /// dapatkan hasil decode terakhir /// </summary> /// <returns></returns> bq::appender_decode_result get_last_decode_result() const; /// <summary> /// dapatkan konten entri log dekode terakhir /// </summary> /// <returns></returns> const bq::string& get_last_decoded_log_entry() const; }; } }
Ini adalah kelas utilitas yang dapat memecahkan kode keluaran file log oleh Appender tipe biner saat runtime, seperti CompressedFileAppender dan RawFileAppender。
Untuk menggunakannya, pertama-tama buat objek log_decoder. Kemudian, setiap kali Anda memanggil fungsi decode(), fungsi tersebut akan mendekode satu entri log secara berurutan. Jika hasil yang dikembalikan adalah bq::appender_decode_result::success, Anda dapat memanggil get_last_decoded_log_entry() untuk mendapatkan konten teks yang diformat dari entri log terakhir yang didekodekan. Jika hasilnya bq::appender_decode_result::eof, berarti semua log sudah terbaca seluruhnya.
BqLog memungkinkan Anda mengonfigurasi apakah objek log sinkron atau asinkron melalui pengaturan thread_mode. Perbedaan utama antara kedua mode ini adalah sebagai berikut:
Pencatatan Log Sinkron | Pencatatan Asinkron | |
---|---|---|
Perilaku | Setelah memanggil fungsi logging, log segera ditulis ke appender yang sesuai. | Setelah memanggil fungsi logging, log tidak langsung ditulis; sebaliknya, ia diserahkan ke thread pekerja untuk diproses secara berkala. |
Pertunjukan | Rendah, karena thread yang menulis log perlu diblokir dan menunggu log ditulis ke appender yang sesuai sebelum kembali dari fungsi logging. | Tinggi, karena thread yang menulis log tidak perlu menunggu keluaran sebenarnya dan dapat kembali segera setelah logging. |
Keamanan Benang | Tinggi, tetapi mengharuskan parameter log tidak diubah selama pelaksanaan fungsi logging. | Tinggi, tetapi mengharuskan parameter log tidak diubah selama pelaksanaan fungsi logging. |
Kesalahpahaman yang umum mengenai logging asinkron adalah bahwa logging ini kurang aman bagi thread, karena pengguna khawatir bahwa parameter dapat diperoleh kembali pada saat thread pekerja memproses log. Misalnya:
{ const char str_array[5] = {'T', 'E', 'S', 'T', '�'}; const char* str_ptr = str_array; log_obj.info("Ini adalah parameter pengujian: {}, {}", str_array, str_ptr); }
Dalam contoh di atas, str_array
disimpan di tumpukan, dan setelah cakupannya keluar, memorinya tidak lagi valid. Pengguna mungkin khawatir jika logging asinkron digunakan, pada saat thread pekerja memproses log, str_array
dan str_ptr
akan menjadi variabel yang tidak valid.
Namun, situasi seperti ini tidak akan terjadi karena BqLog menyalin semua konten parameter ke ring_buffer
internalnya selama eksekusi fungsi info
. Setelah fungsi info
kembali, variabel eksternal seperti str_array
atau str_ptr
tidak lagi diperlukan. Selain itu, ring_buffer
tidak akan menyimpan alamat penunjuk const char*
tetapi akan selalu menyimpan seluruh string.
Potensi masalah sebenarnya muncul dalam skenario berikut:
static std::string global_str = "halo dunia"; // Ini adalah variabel global yang dimodifikasi oleh beberapa thread.void thread_a() { log_obj.info("Ini adalah parameter pengujian: {}", global_str); }
Jika konten global_str
berubah selama eksekusi fungsi info
, hal ini dapat menyebabkan perilaku tidak terdefinisi. BqLog akan melakukan yang terbaik untuk mencegah crash, namun kebenaran hasil akhir tidak dapat dijamin.
Appender mewakili target keluaran log. Konsep Appenders di bqLog pada dasarnya sama dengan di Log4j. Saat ini, bqLog menyediakan jenis Appender berikut:
Target keluaran Appender ini adalah konsol, termasuk ADB Android dan konsol terkait di iOS. Pengkodean teks adalah UTF-8.
Appender ini mengeluarkan file log secara langsung dalam format teks UTF-8.
Appender ini mengeluarkan file log dalam format terkompresi, yang merupakan highly recommended format by bqLog
. Ini memiliki kinerja tertinggi di antara semua Appender dan menghasilkan file output terkecil. Namun, file terakhir perlu didekodekan. Decoding dapat dilakukan saat decoding runtime, atau decoding offline。
Appender ini mengeluarkan konten log biner dari memori langsung ke file. Kinerjanya lebih tinggi dibandingkan TextFileAppender, namun mengkonsumsi lebih banyak ruang penyimpanan. File terakhir perlu didekodekan. Decoding dapat dilakukan saat decoding runtime, atau decoding offline. Appender ini tidak disarankan untuk digunakan.
Di bawah ini adalah perbandingan komprehensif dari berbagai Appender:
Nama | Sasaran Keluaran | Dapat Dibaca Langsung | Kinerja Keluaran | Ukuran Keluaran |
---|---|---|---|---|
ConsoleAppender | Menghibur | ✔ | Rendah | - |
TextFileAppender | Mengajukan | ✔ | Rendah | Besar |
Appender File Terkompresi | Mengajukan | ✘ | Tinggi | Kecil |
Penambah File Mentah | Mengajukan | ✘ | Sedang | Besar |
Konfigurasi mengacu pada string konfigurasi di fungsi create_log dan reset_config. String ini menggunakan format file properti dan mendukung # komentar (tapi ingat untuk memulai baris baru dengan # untuk komentar).
Di bawah ini adalah contoh lengkapnya:
# Konfigurasi ini mengatur objek log dengan total 5 appenders, termasuk dua TextFileAppenders yang menghasilkan dua file yang berbeda.# Appender pertama dinamai appender_0 dan jenisnya adalah ConsoleAppenderAppenders_config.appender_0.type = konsol# zona waktu untuk appender_0 adalah Sistem Lokal TimeAppenders_config.Appender_0.time_zone = Waktu lokal default# appender_0 akan menghasilkan semua 6 level log (Catatan: seharusnya tidak ada spasi antara level log, atau akan gagal untuk menguraikan) appenders_config.appender_0.levels = [verbose, debug, info, peringatan, kesalahan, fatal]# appender kedua bernama appender_1 dan jenisnya adalah textFileAppenderApders_configger .Appender_1.type = text_file# zona waktu untuk appender_1 adalah GMT, yaitu UTC+0 appenders_config.appender_1.time_zone = gmt# appender_1 Hanya output log info level dan di atas, yang lain akan diabaikan appenders_config.appender_1.levels = [info, peringatan, kesalahan, fatal]# Path for Appender_1 akan berada di BQLOG yang relatif BQLOG BQLOG RELATIF BQLOG RELATF dari program ini, dengan nama file dimulai dengan normal, diikuti dengan tanggal dan ekstensi .log# di iOS, itu akan disimpan di /var/seluler/wadah/data/aplikasi/[aplikasi]/pustaka/cache/bqlog# on android, itu akan disimpan di [android.content.context.getExternalSdir ()]/bqlogappenders_config.applent_1.file_name = bqlog/normal # Ukuran file maksimum adalah 10.000.000 byte; Jika terlampaui, file baru akan dibuat appenders_config.appender_1.max_file_size = 10000000# File yang lebih tua dari sepuluh hari akan dibersihkan upappenders_config.appender_1.expire_time_days = 10# Jika ukuran total output melebihi 100.000.000 byte, file akan dibersihkan dari awal dari awal dari output melebihi 100.000.000 byte, file akan dibersihkan dari awal dari output dari output, 100.000 byte, OLDESTAppenders_config.Appender_1.capacity_limit = 100000000# Appender ketiga bernama Appender_2 dan tipenya adalah TextFileAppenderAppenders_config.appender_2.type = text_file# appender_2 akan mengeluarkan semua level dari logsappenders_config.appender_2.22. Direktori BQLOG Relatif Program, dengan nama file dimulai dengan new_normal, diikuti oleh tanggal dan .log extensionappenders_config.appender_2.file_name = bqlog/new_normal# Opsi ini hanya efektif pada android, menyimpan log di direktori penyimpanan internal, yaitu [android.content.context.getFilesDir ()]/ bqlogappenders_config.appender_2.is_in_sandbox = true# appender keempat bernama appender_3 dan jenisnya CompressedFileAppenderappenders_config.appender_3.type=compressed_file# appender_3 will output all levels of logsappenders_config.appender_3.levels=[all]# The path for appender_3 will be in the absolute path ~/bqLog directory of the program, with filenames starting with compress_log, followed by tanggal dan .logcompr extensionappenders_config.appender_3.file_name = ~/bqlog/compress_log# appender kelima dinamai appender_4 dan jenisnya adalah rawfileAppenderAppenders_config.appender_4.type = raw_file# appender_4 tidak dapat dikeluarkan oleh default dan dapat digunakan. akan menghasilkan semua level Logsappenders_config.appender_4.levels = [semua]# Path for Appender_4 akan berada di direktori BQLog relatif program, dengan nama file dimulai dengan RAW_LOG, diikuti oleh tanggal dan .lograw ExtensionAppenders_config.Appender_4.file_name = BQLOG/RAW_LOG_LOG_LOG_LOG_LOG_ Hanya diproses jika kategorinya dimulai dengan Modulea, Moduleb.systemc, jika tidak semua akan diabaikan (konsep kategori dijelaskan dalam Detail dalam Topik Penggunaan Lanjutan nanti) Appenders_config.appender_4.categories_mask = [Modulea, Moduleb.systemc]# Total ukuran buffer asinkron adalah 65535 byte; Makna spesifik dijelaskan lateLog.buffer_size = 65535# Tingkat keandalan log adalah normal; Makna spesifik dijelaskan lateLog.Reliable_level = Normal# Logs hanya akan diproses jika kategorinya cocok dengan tiga wildcard berikut, jika tidak semua akan diabaikan (konsep kategori dijelaskan secara rinci dalam topik penggunaan lanjutan nanti) log.categories_mask = [*default, modulea, moduleb.systemc]# Ini adalah log asinkron; Log asinkron adalah log typelog.thread_mode = async# jika level log kesalahan atau fatal, sertakan informasi tumpukan panggilan dengan masing -masing log entrylog.print_stack_levels = [error, fatal]# Mengaktifkan fungsionalitas snapshot, ukuran cache snapshot adalah 64KSnapshot .buffer_size = 65536# Hanya log dengan info dan tingkat kesalahan yang akan direkam di snapshotsnapshot.levels = [info, kesalahan]# Hanya log yang kategorinya Dimulai dengan Modulea, MODULEB.SYSTEMC akan direkam dalam snapshot, jika tidak mereka akan diabaikanSnapshot.categories_mask = [modulea.systema.classa, moduleb]
appenders_config
adalah seperangkat konfigurasi untuk pengembara. Parameter pertama berikut appenders_config
adalah nama appender, dan semua appenders dengan nama yang sama berbagi konfigurasi yang sama.
Nama | Diperlukan | Nilai yang dapat dikonfigurasi | Bawaan | Berlaku untuk ConsoleAppender | Berlaku untuk TextFileAppender | Berlaku untuk CompressedFileAppender | Berlaku untuk RawFileAppender |
---|---|---|---|---|---|---|---|
jenis | ✔ | konsol, text_file, compressed_file, raw_file | ✔ | ✔ | ✔ | ✔ | |
memungkinkan | ✘ | Apakah appender diaktifkan secara default | BENAR | ✔ | ✔ | ✔ | ✔ |
tingkat | ✘ | Array level log | [semua] | ✔ | ✔ | ✔ | ✔ |
zona_waktu | ✘ | GMT atau string lainnya | Waktu setempat | ✔ | ✔ | ✔ | ✔ |
file_name | ✔ | Jalur relatif atau absolut | ✘ | ✔ | ✔ | ✔ | |
is_in_sandbox | ✘ | benar, salah | PALSU | ✘ | ✔ | ✔ | ✔ |
max_file_size | ✘ | Bilangan bulat positif atau 0 | 0 | ✘ | ✔ | ✔ | ✔ |
Expire_Time_days | ✘ | Bilangan bulat positif atau 0 | 0 | ✘ | ✔ | ✔ | ✔ |
kapasitas_limit | ✘ | Bilangan bulat positif atau 0 | 0 | ✘ | ✔ | ✔ | ✔ |
kategori_mask | ✘ | Array string terlampir di [] | Kosong | ✔ | ✔ | ✔ | ✔ |
Menentukan jenis appender.
console
: mewakili ConsoleAppender
text_file
: mewakili textfileAppender
compressed_file
: mewakili compressedfileAppender
raw_file
: mewakili RawFileAppender
Default ke true
. Jika diatur ke false
, appender akan dinonaktifkan secara default dan dapat diaktifkan nanti menggunakan set_appenders_enable
.
Array yang terlampir di []
, berisi kombinasi verbose
, debug
, info
, warning
, error
, fatal
, atau [all]
untuk menerima semua tingkatan. Catatan: Jangan termasuk spasi antar level, atau akan gagal diurai.
Menentukan zona waktu log. gmt
mewakili Greenwich Mean Time (UTC+0), dan string lainnya atau membiarkannya kosong akan menggunakan zona waktu setempat. Zona waktu mempengaruhi dua hal:
Cap waktu log teks yang diformat (berlaku untuk ConsoleAppender dan TextFileAppender)
File log baru akan dibuat ketika tengah malam dilintasi di zona waktu yang ditentukan (berlaku untuk TextFileAppender, CompressedFileAppender, dan RawFileAppender).
Path dan awalan nama file untuk menyimpan file. Jalurnya bisa mutlak (tidak disarankan untuk Android dan iOS) atau kerabat. Nama file output akhir adalah jalur dan nama ini, diikuti oleh tanggal, nomor file, dan ekstensi appender.
Hanya bermakna di Android:
true
: File disimpan di direktori penyimpanan internal (android.content.context.getFilesDir ()). Jika tidak tersedia, mereka disimpan di direktori penyimpanan eksternal (android.content.context.getExternalSdir ()). Jika itu juga tidak tersedia, mereka disimpan di direktori cache (android.content.context.getCachedir ()).
false
: File disimpan di direktori penyimpanan eksternal secara default. Jika tidak tersedia, mereka disimpan di direktori penyimpanan internal. Jika itu juga tidak tersedia, mereka disimpan di direktori cache.
Ukuran file maksimum dalam byte. Ketika file yang disimpan melebihi ukuran ini, file log baru dibuat, dengan nomor file bertambah secara berurutan. 0
menonaktifkan fitur ini.
Jumlah maksimum hari untuk menyimpan file. File yang lebih tua dari ini akan dihapus secara otomatis. 0
menonaktifkan fitur ini.
Ukuran total maksimum file output oleh appender ini di direktori output. Jika batas ini terlampaui, file dihapus mulai dari yang tertua hingga ukuran total dalam batas. 0
menonaktifkan fitur ini.
Jika objek log adalah objek log yang mendukung kategori, ini dapat digunakan untuk menyaring daftar kategori seperti pohon. Ketika array tidak kosong, fitur ini aktif. Misalnya, [*default,ModuleA,ModuleB.SystemC]
berarti log dengan kategori default