中文文档
BqLog is a lightweight, high-performance logging system used in projects such as "Honor of Kings," and it has been successfully deployed and is running smoothly.
Windows 64 bit
MacOS
Linux
iOS
Android(X86_64, arm64-v8a、armeabi-v7a)
Unix(Pass the test on FreeBSD)
C++
Java
Kotlin
C#
Compared to existing open-source logging libraries, BqLog offers significant performance advantages (see Benchmark). It is not only suitable for servers and clients but also highly compatible with mobile devices.
With low memory consumption, in the Benchmark case of 10 threads and 20,000,000 log entries, BqLog itself consumes less than 1 MB of memory.
Provides a high-performance, high-compression real-time log format
Can be used normally in game engines (Unity
, Unreal
), with support for common types provided for Unreal.
Supports UTF-8
, UTF-16
, UTF-32
characters and strings, as well as common parameter types like bool, float, double, and various lengths and types of integers
Supports C++20
format specifications
Asynchronous logging supports crash review to avoid data loss (inspired by XLog)
Extremely small size, with the dynamic library being only about 200k after Android compilation
Does not generate additional heap allocations in Java and C#, avoiding constant new object creation during runtime
Only depends on the standard C language library and platform APIs, and can be compiled in Android's ANDROID_STL = none
mode
Supports C++11
and later compilation standards, and can be compiled under strict requirements of -Wall -Wextra -pedantic -Werror
Compilation module is based on CMake
and provides compilation scripts for different platforms, making it easy to use
Supports custom parameter types
Very friendly to code suggestions
Why is BqLog so fast - High Performance Realtime Compressed Log Format
Why is BqLog so fast - High-Concurrency Ring Buffer
Integrating BqLog into Your Project
Simple Demo
Architecture Overview
Main Process API Usage Instructions
1-Creating a Log Object
2-Retrieving a Log Object
3-Logging Messages
4-Other APIs
Synchronous and Asynchronous Logging
1. Thread safety of asynchronous logging
Introduction to Appenders
1. ConsoleAppender
2. TextFileAppender
3. CompressedFileAppender(Highly Recommended)
4. RawFileAppender
Configuration Instructions
1. Complete Example
2. Detailed Explanation
Offline Decoding of Binary Format Appenders
Build Instructions
1. Library Build
2. Demo Build and Run
3. Automated Test Run Instructions
4. Benchmark Run Instructions
Advanced Usage Topics
1. No Heap Allocation
2. Log Objects with Category Support
3. Data Protection on Program Abnormal Exit
4. About NDK and ANDROID_STL = none
5. Custom Parameter Types
6. Using BqLog in Unreal Engine
Benchmark
1. Benchmark Description
2. BqLog C++ Benchmark Code
3. BqLog Java Benchmark Code
4. Log4j Benchmark Code
5. Benchmark Results
BqLog can be integrated into your project in various forms. For C++, it supports dynamic libraries, static libraries, and source files. For Java and C#, it supports dynamic libraries with wrapper source code. Below are the methods to include BqLog:
The code repository includes precompiled dynamic library files located in /dist/dynamic_lib/. To integrate BqLog into your project using the library files, you need to do the following:
Select the dynamic library file corresponding to your platform and add it to your project's build system.
Copy the /dist/dynamic_lib/include directory into your project and add it to the include directory list. (If you are using XCode's .framework library, you can skip this step as the .framework file already includes the header files).
The code repository includes precompiled static library files located in /dist/static_lib/. To integrate BqLog into your project using the library files, you need to do the following:
Select the static library file corresponding to your platform and add it to your project's build system.
Copy the /dist/static_lib/include directory into your project and add it to the include directory list. (If you are using XCode's .framework library, you can skip this step as the .framework file already includes the header files).
BqLog also supports direct inclusion of source code into your project for compilation. To integrate BqLog using the source code, follow these steps:
Copy the /src directory into your project as a source code reference.
Copy the /include directory into your project and add it to the include directory list.
If compiling the Windows version in Visual Studio, add /Zc:__cplusplus to the compilation options to ensure the current C++ compiler standard support is correctly determined.
If using the source code in Android's NDK, please refer to 4. About NDK and ANDROID_STL = none for important considerations.
In C#, BqLog can be used via a native dynamic library and a C# Wrapper, supporting Mono, Microsoft CLR, and Unity engines. Unity is compatible with both Mono and IL2CPP modes. To use BqLog in C#, follow these steps:
Select the dynamic library file corresponding to your platform from /dist/dynamic_lib/ and add it to your project (for Unity, refer to Unity Import and configure plug-ins).
Copy the source code files from /wrapper/csharp/src into your project.
In Java, BqLog can be used via a native dynamic library and a Java Wrapper, supporting common JVM environments and Android. To integrate BqLog into a JVM, follow these steps:
Select the dynamic library file corresponding to your platform from /dist/dynamic_lib/ and add it to your project.
Copy the source code files from /wrapper/java/src into your project.
(Optional) Copy the /dist/dynamic_lib/include directory into your project and add it to the include directory list if you intend to call BqLog from the NDK.
The following code will output over 1000 logs to your console (or ADB Logcat if on Android)
#if defined(WIN32) #include#endif#include #include int main() { #if defined(WIN32) // Switch Windows command line to UTF-8 because BqLog outputs all final text in UTF-8 encoding to avoid display issues SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // This string is the log configuration. Here it configures a logger with one appender (output target) named appender_0, which outputs to the console. std::string config = R"( # This appender's output target is the console appenders_config.appender_0.type=console # This appender uses local time for timestamps appenders_config.appender_0.time_zone=default local time # This appender outputs logs of these 6 levels (no spaces in between) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] )"; bq::log log = bq::log::create_log("my_first_log", config); // Create a log object using the config for(int i = 0; i < 1024; ++i) { log.info("This is an info test log, the format string is 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"This is an error test log, the format string is UTF-32"); bq::log::force_flush_all_logs(); // BqLog defaults to asynchronous output. To ensure logs are visible before program exit, force flush to sync output once. return 0; }
using System.Text;using System;public class demo_main { public static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; string config = @" # This appender's output target is the console appenders_config.appender_0.type=console # This appender uses local time for timestamps ppenders_config.appender_0.time_zone=default local time # This appender outputs logs of these 6 levels (no spaces in between) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] "; bq.log log = bq.log.create_log("my_first_log", config); // Create a log object using the config for (int i = 0; i < 1024; ++i) { log.info("This is an info test log, the format string is UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "String Text", 4.3464f); } bq.log.force_flush_all_logs(); Console.ReadKey(); }}
public class demo_main { public static void main(String[] args) { // TODO Auto-generated method stub String config = """ # This appender's output target is the console appenders_config.appender_0.type=console # This appender uses local time for timestamps appenders_config.appender_0.time_zone=default local time # This appender outputs logs of these 6 levels (no spaces in between) appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] """; bq.log log = bq.log.create_log("my_first_log", config); // Create a log object using the config for (int i = 0; i < 1024; ++i) { log.info("This is an info test log, the format string is UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "String Text", 4.3464f); } bq.log.force_flush_all_logs(); } }
The diagram above clearly illustrates the basic structure of BqLog. On the right side of the diagram is the internal implementation of the BqLog library, while on the left side is your program and code. Your program can call BqLog using the provided wrappers (object-oriented APIs for different languages). In the diagram, two Logs are created: one named "Log A" and the other named "Log B." Each Log is attached to one or more Appenders. An Appender can be understood as the output target of the log content. This can be the console (ADB Logcat logs for Android), text files, or even specialized formats like compressed log files or regular binary log format files.
Within the same process, wrappers for different languages can access the same Log object. For example, if a Log object named Log A is created in Java, it can also be accessed and used from the C++ side by the name Log A.
In extreme cases, such as a Unity-developed game running on the Android system, you might involve Java, Kotlin, C#, and C++ languages within the same app. They can all share the same Log object. You can create the Log on the Java side using create_log, and then access it in other languages using get_log_by_name.
Note: The following APIs are declared in the bq::log (or bq.log) class. To save space, only the C++ APIs are listed. The APIs in Java and C# are identical and will not be repeated here.
In C++, bq::string
is the UTF-8 string type in the BqLog library. You can also pass in c-style strings like char or std::string
or std::string_view
, which will be automatically and implicitly converted.
A log object can be created using the create_log static function. Its declaration is as follows:
//C++ API ////// Create a log object /// /// If the log name is an empty string, bqLog will automatically assign you a unique log name. If the log name already exists, it will return the previously existing log object and overwrite the previous configuration with the new config. /// Log config string ///A log object, if create failed, the is_valid() method of it will return false static log create_log(const bq::string& log_name, const bq::string& config_content);
The code creates a log object by passing in the name of the log object and a configuration string. The log configuration can be referenced in the Configuration Instructions. Here are a few key points to note:
Regardless of whether it is C# or Java, the returned log object will never be null. However, due to configuration errors or other reasons, an invalid log object might be created. Therefore, you should use the is_valid() function to check the returned object. Performing operations on an invalid object may cause the program to crash.
If an empty string is passed as the log name, bqLog will automatically generate a unique log name, such as "AutoBqLog_1."
Calling create_log on an already existing log object with the same name will not create a new log object but will overwrite the previous configuration with the new one. However, some parameters cannot be modified in this process; see Configuration Instructions for details.
Except when using in the NDK (refer to 4. About NDK and ANDROID_STL = none), you can initialize the log object directly in global or static variables using this API in other situations.
If a log object has already been created elsewhere, you can obtain the created log object directly using the get_log_by_name function.
//C++ API ////// Get a log object by it's name /// /// Name of the log object you want to find ///A log object, if the log object with specific name was not found, the is_valid() method of it will return false static log get_log_by_name(const bq::string& log_name);
You can also use this function to initialize a log object in global variables or static functions. However, note that you must ensure the log object with the specified name already exists. Otherwise, the returned log object will be unusable, and its is_valid() method will return false.
///Core log functions, there are 6 log levels: ///verbose, debug, info, warning, error, fatal templatebq::enable_if_t ::value, bool> verbose(const STR& log_content) const; template bq::enable_if_t ::value, bool> verbose(const STR& log_format_content, const Args&... args) const; template bq::enable_if_t ::value, bool> debug(const STR& log_content) const; template bq::enable_if_t ::value, bool> debug(const STR& log_format_content, const Args&... args) const; template bq::enable_if_t ::value, bool> info(const STR& log_content) const; template bq::enable_if_t ::value, bool> info(const STR& log_format_content, const Args&... args) const; template bq::enable_if_t ::value, bool> warning(const STR& log_content) const; template bq::enable_if_t ::value, bool> warning(const STR& log_format_content, const Args&... args) const; template bq::enable_if_t ::value, bool> error(const STR& log_content) const; template bq::enable_if_t ::value, bool> error(const STR& log_format_content, const Args&... args) const; template bq::enable_if_t ::value, bool> fatal(const STR& log_content) const; template bq::enable_if_t ::value, bool> fatal(const STR& log_format_content, const Args&... args) const;
When logging messages, pay attention to three key points:
As you can see, our logs are divided into six levels: verbose, debug, info, warning, error, and fatal, consistent with Android. Their importance increases sequentially. When output to the console, they will appear in different colors.
The STR parameter is similar to the first parameter of printf and can be various common string types, including:
Java's java.lang.String
C#'s string
Various encodings of C++'s C-style strings and 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
and even custom string types, which you can refer to inCustom Parameter Types )
You can add various parameters after the STR parameter. These parameters will be formatted into the specified places in the STR, following rules similar to C++20's std::format (except for the lack of support for positional arguments and date time format). For example, using a single {} represents a default formatting of a parameter, and {:.2f} specifies the precision for formatting a floating-point number.Try to use formatted parameters to output logs rather than concatenating strings manually. This approach is optimal for performance and compressed format storage.
Currently supported parameter types include:
Null pointers (output as null)
Pointers (output as a hexadecimal address starting with 0x)
bool
Single-byte characters (char)
Double-byte characters (char16_t, wchar_t, C#'s char, Java's char)
Four-byte characters (char32_t or wchar_t)
8-bit integers
8-bit unsigned integers
16-bit integers
16-bit unsigned integers
32-bit integers
32-bit unsigned integers
64-bit integers
64-bit unsigned integers
32-bit floating-point numbers
64-bit floating-point numbers
Other unknown POD types in C++ (limited to sizes 1, 2, 4, or 8 bytes, treated as int8, int16, int32, and int64 respectively)
Strings, including all string types mentioned in STR Parameter
Any class or object in C# and Java (outputting their ToString() string)
Custom parameter types, as detailed in Custom Parameter Types
There are additional commonly used APIs that can accomplish specific tasks. For detailed API descriptions, refer to bq_log/bq_log.h, as well as the bq.log class in Java and C#. Here are some key APIs that need to be highlighted:
////// Uninitialize BqLog, please invoke this function before your program exist. /// static void uninit();
It is recommended to execute uninit()
before exiting the program or uninstalling the self-implemented dynamic library that uses BqLog, otherwise the program may get stuck when exiting under certain specific circumstances.
////// If bqLog is asynchronous, a crash in the program may cause the logs in the buffer not to be persisted to disk. /// If this feature is enabled, bqLog will attempt to perform a forced flush of the logs in the buffer in the event of a crash. However, /// this functionality does not guarantee success, and only support POSIX systems. /// static void enable_auto_crash_handle();
For a detailed introduction, seeData Protection on Program Abnormal Exit
////// Synchronously flush the buffer of all log objects /// to ensure that all data in the buffer is processed after the call. /// static void force_flush_all_logs(); ////// Synchronously flush the buffer of this log object /// to ensure that all data in the buffer is processed after the call. /// void force_flush();
Since bqLog uses asynchronous logging by default, there are times when you might want to immediately synchronize and output all logs. In such cases, you need to forcefully call force_flush().
////// Register a callback that will be invoked whenever a console log message is output. /// This can be used for an external system to monitor console log output. /// /// static void register_console_callback(bq::type_func_ptr_console_callback callback); ////// Unregister a console callback. /// /// static void unregister_console_callback(bq::type_func_ptr_console_callback callback);
The output ofConsoleAppender goes to the console or ADB Logcat logs on Android, but this may not cover all situations. For instance, in custom game engines or custom IDEs, a mechanism is provided to call a callback function for each console log output. This allows you to reprocess and output the console log anywhere in your program.
Additional Caution: Do not output any synchronized BQ logs within the console callback as it may easily lead to deadlocks.
////// Enable or disable the console appender buffer. /// Since our wrapper may run in both C# and Java virtual machines, and we do not want to directly invoke callbacks from a native thread, /// we can enable this option. This way, all console outputs will be saved in the buffer until we fetch them. /// /// ///static void set_console_buffer_enable(bool enable); /// /// Fetch and remove a log entry from the console appender buffer in a thread-safe manner. /// If the console appender buffer is not empty, the on_console_callback function will be invoked for this log entry. /// Please ensure not to output synchronized BQ logs within the callback function. /// /// A callback function to be invoked for the fetched log entry if the console appender buffer is not empty ///True if the console appender buffer is not empty and a log entry is fetched; otherwise False is returned. static bool fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback);
In addition to intercepting console output through a console callback, you can actively fetch console log outputs. Sometimes, we may not want the console log output to come through a callback because you do not know which thread the callback will come from (for example, in some C# virtual machines, or JVMs, the VM might be performing garbage collection when the console callback is called, which could potentially lead to hangs or crashes).
The method used here involves enabling the console buffer through set_console_buffer_enable
. This causes every console log output to be stored in memory until we actively call fetch_and_remove_console_buffer
to retrieve it. Therefore, if you choose to use this method, remember to promptly fetch and clear logs to avoid unreleased memory.
Additional Caution: Do not output any synchronized BQ logs within the console callback as it may easily lead to deadlocks.
Additional Caution: If you are using this code in an IL2CPP environment, please make sure that the on_console_callback is marked as static unsafe and is decorated with the [MonoPInvokeCallback(typeof(type_console_callback))] attribute.
////// Modify the log configuration, but some fields, such as buffer_size, cannot be modified. /// /// ///bool reset_config(const bq::string& config_content);
Sometimes you may want to modify the configuration of a log within your program. In addition to recreating the log object to overwrite the configuration(SeeCreating a Log Object),you can also use the reset interface. However, note that not all configuration items can be modified this way. For details, refer to the Configuration Instructions
////// Temporarily disable or enable a specific Appender. /// /// /// void set_appenders_enable(const bq::string& appender_name, bool enable);
By default, the Appenders in the configuration are active, but a mechanism is provided here to temporarily disable and re-enable them.
////// Works only when snapshot is configured. /// It will decode the snapshot buffer to text. /// /// whether the timestamp of each log is GMT time or local time ///the decoded snapshot buffer bq::string take_snapshot(bool use_gmt_time) const;
Sometimes, certain special features require outputting the last part of the logs, which can be done using the snapshot feature.
To enable this feature, you first need to activate the snapshot in the log configuration and set the maximum buffer size, in bytes. Additionally, you need to specify the log levels and categories to be filtered for the snapshot (optional). For detailed configuration, please refer to Snapshot Configuration.
When a snapshot is needed, calling take_snapshot() will return the formatted string containing the most recent log entries stored in the snapshot buffer. In C++, the type is bq::string
, which can be implicitly converted to std::string
.
namespace bq{ namespace tools { //This is a utility class for decoding binary log formats. //To use it, first create a log_decoder object, //then call its decode function to decode. //After each successful call, //you can use get_last_decoded_log_entry() to retrieve the decoded result. //Each call decodes one log entry. struct log_decoder { private: bq::string decode_text_; bq::appender_decode_result result_ = bq::appender_decode_result::success; uint32_t handle_ = 0; public: ////// Create a log_decoder object, with each log_decoder object corresponding to a binary log file. /// /// the path of a binary log file, is can be relative path or absolute path log_decoder(const bq::string& log_file_path); ~log_decoder(); ////// Decode a log entry. each call of this function will decode only 1 log entry /// ///decode result, appender_decode_result::eof means the whole log file was decoded bq::appender_decode_result decode(); ////// get the last decode result /// ///bq::appender_decode_result get_last_decode_result() const; /// /// get the last decode log entry content /// ///const bq::string& get_last_decoded_log_entry() const; }; } }
This is a utility class that can decode log files output by binary-type Appenders at runtime, such as CompressedFileAppender and RawFileAppender。
To use it, first create a log_decoder object. Then, each time you call the decode() function, it decodes one log entry in sequence. If the result returned is bq::appender_decode_result::success, you can call get_last_decoded_log_entry() to get the formatted text content of the last decoded log entry.
If the result is bq::appender_decode_result::eof, it means all logs have been read completely.
BqLog allows you to configure whether a log object is synchronous or asynchronous through the thread_mode setting. The key differences between these two modes are as follows:
Synchronous Logging | Asynchronous Logging | |
---|---|---|
Behavior | After calling the logging function, the log is immediately written to the corresponding appender. | After calling the logging function, the log is not immediately written; instead, it is handed off to a worker thread for periodic processing. |
Performance | Low, as the thread writing the log needs to block and wait for the log to be written to the corresponding appender before returning from the logging function. | High, as the thread writing the log does not need to wait for the actual output and can return immediately after logging. |
Thread Safety | High, but it requires that the log parameters are not modified during the execution of the logging function. | High, but it requires that the log parameters are not modified during the execution of the logging function. |
A common misconception about asynchronous logging is that it is less thread-safe, with users concerned that the parameters may be reclaimed by the time the worker thread processes the log. For example:
{ const char str_array[5] = {'T', 'E', 'S', 'T', '�'}; const char* str_ptr = str_array; log_obj.info("This is test param: {}, {}", str_array, str_ptr); }
In the above example, str_array
is stored on the stack, and once the scope is exited, its memory is no longer valid. Users might worry that if asynchronous logging is used, by the time the worker thread processes the log, str_array
and str_ptr
will be invalid variables.
However, such a situation will not occur because BqLog copies all parameter contents into its internal ring_buffer
during the execution of the info
function. Once the info
function returns, the external variables like str_array
or str_ptr
are no longer needed. Moreover, the ring_buffer
will not store a const char*
pointer address but will always store the entire string.
The real potential issue arises in the following scenario:
static std::string global_str = "hello world"; // This is a global variable modified by multiple threads.void thread_a() { log_obj.info("This is test param: {}", global_str); }
If the content of global_str
changes during the execution of the info
function, it may lead to undefined behavior. BqLog will do its best to prevent a crash, but the correctness of the final output cannot be guaranteed.
An Appender represents the log output target. The concept of Appenders in bqLog is basically the same as in Log4j. Currently, bqLog provides the following types of Appenders:
The output target of this Appender is the console, including Android's ADB and the corresponding console on iOS. The text encoding is UTF-8.
This Appender outputs log files directly in UTF-8 text format.
This Appender outputs log files in a compressed format, which is the highly recommended format by bqLog
. It has the highest performance among all Appenders and produces the smallest output file. However, the final file needs to be decoded. Decoding can be done at runtime decoding,or offline decoding。
This Appender outputs the binary log content from memory directly to a file. Its performance is higher than that of TextFileAppender, but it consumes more storage space. The final file needs to be decoded. Decoding can be done at runtime decoding,or offline decoding。This Appender is not recommended for use.
Below is a comprehensive comparison of the various Appenders:
Name | Output Target | Directly Readable | Output Performance | Output Size |
---|---|---|---|---|
ConsoleAppender | Console | ✔ | Low | - |
TextFileAppender | File | ✔ | Low | Large |
CompressedFileAppender | File | ✘ | High | Small |
RawFileAppender | File | ✘ | Medium | Large |
Configuration refers to the config string in the create_log and reset_config functions. This string uses the properties file format and supports # comments (but remember to start a new line with # for comments).
Below is a complete example:
# This configuration sets up a log object with a total of 5 Appenders, including two TextFileAppenders that output to two different files.# The first Appender is named appender_0 and its type is ConsoleAppenderappenders_config.appender_0.type=console# The time zone for appender_0 is the system's local timeappenders_config.appender_0.time_zone=default local time# appender_0 will output all 6 levels of logs (note: there should be no spaces between log levels, or it will fail to parse)appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal]# The second Appender is named appender_1 and its type is TextFileAppenderappenders_config.appender_1.type=text_file# The time zone for appender_1 is GMT, which is UTC+0appenders_config.appender_1.time_zone=gmt# appender_1 only outputs logs of level info and above, others will be ignoredappenders_config.appender_1.levels=[info,warning,error,fatal]# The path for appender_1 will be in the relative bqLog directory of the program, with filenames starting with normal, followed by the date and .log extension# On iOS, it will be saved in /var/mobile/Containers/Data/Application/[APP]/Library/Caches/bqLog# On Android, it will be saved in [android.content.Context.getExternalFilesDir()]/bqLogappenders_config.appender_1.file_name=bqLog/normal# The maximum file size is 10,000,000 bytes; if exceeded, a new file will be createdappenders_config.appender_1.max_file_size=10000000# Files older than ten days will be cleaned upappenders_config.appender_1.expire_time_days=10# If the total size of output exceeds 100,000,000 bytes, files will be cleaned up starting from the oldestappenders_config.appender_1.capacity_limit=100000000# The third Appender is named appender_2 and its type is TextFileAppenderappenders_config.appender_2.type=text_file# appender_2 will output all levels of logsappenders_config.appender_2.levels=[all]# The path for appender_2 will be in the relative bqLog directory of the program, with filenames starting with new_normal, followed by the date and .log extensionappenders_config.appender_2.file_name=bqLog/new_normal# This option is only effective on Android, saving logs in the internal storage directory, which is [android.content.Context.getFilesDir()]/bqLogappenders_config.appender_2.is_in_sandbox=true# The fourth Appender is named appender_3 and its type is 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 the date and .logcompr extensionappenders_config.appender_3.file_name=~/bqLog/compress_log# The fifth Appender is named appender_4 and its type is RawFileAppenderappenders_config.appender_4.type=raw_file# appender_4 is disabled by default and can be enabled later using set_appenders_enableappenders_config.appender_4.enable=false# appender_4 will output all levels of logsappenders_config.appender_4.levels=[all]# The path for appender_4 will be in the relative bqLog directory of the program, with filenames starting with raw_log, followed by the date and .lograw extensionappenders_config.appender_4.file_name=bqLog/raw_log# Logs will only be processed if their category starts with ModuleA, ModuleB.SystemC, otherwise all will be ignored (the concept of Category is explained in detail in the advanced usage topics later)appenders_config.appender_4.categories_mask=[ModuleA,ModuleB.SystemC]# The total asynchronous buffer size is 65535 bytes; the specific meaning is explained laterlog.buffer_size=65535# The reliability level of the log is normal; the specific meaning is explained laterlog.reliable_level=normal# Logs will only be processed if their category matches the following three wildcards, otherwise all will be ignored (the concept of Category is explained in detail in the advanced usage topics later)log.categories_mask=[*default,ModuleA,ModuleB.SystemC]# This is an asynchronous log; asynchronous logs are the highest performing and recommended log typelog.thread_mode=async# If the log level is error or fatal, include call stack information with each log entrylog.print_stack_levels=[error,fatal]# Enable snapshot functionality, snapshot cache size is 64Ksnapshot.buffer_size=65536# Only logs with info and error levels will be recorded in the snapshotsnapshot.levels=[info,error]# Only logs whose category starts with ModuleA, ModuleB.SystemC will be recorded in the snapshot, otherwise they will be ignoredsnapshot.categories_mask=[ModuleA.SystemA.ClassA,ModuleB]
The appenders_config
is a set of configurations for Appenders. The first parameter following appenders_config
is the name of the Appender, and all Appenders with the same name share the same configuration.
Name | Required | Configurable Values | Default | Applicable to ConsoleAppender | Applicable to TextFileAppender | Applicable to CompressedFileAppender | Applicable to RawFileAppender |
---|---|---|---|---|---|---|---|
type | ✔ | console, text_file, compressed_file, raw_file | ✔ | ✔ | ✔ | ✔ | |
enable | ✘ | Whether the Appender is enabled by default | true | ✔ | ✔ | ✔ | ✔ |
levels | ✘ | Array of log levels | [all] | ✔ | ✔ | ✔ | ✔ |
time_zone | ✘ | gmt or any other string | Local time | ✔ | ✔ | ✔ | ✔ |
file_name | ✔ | Relative or absolute path | ✘ | ✔ | ✔ | ✔ | |
is_in_sandbox | ✘ | true, false | false | ✘ | ✔ | ✔ | ✔ |
max_file_size | ✘ | Positive integer or 0 | 0 | ✘ | ✔ | ✔ | ✔ |
expire_time_days | ✘ | Positive integer or 0 | 0 | ✘ | ✔ | ✔ | ✔ |
capacity_limit | ✘ | Positive integer or 0 | 0 | ✘ | ✔ | ✔ | ✔ |
categories_mask | ✘ | Array of strings enclosed in [] | Empty | ✔ | ✔ | ✔ | ✔ |
Specifies the type of the Appender.
console
: Represents ConsoleAppender
text_file
: Represents TextFileAppender
compressed_file
: Represents CompressedFileAppender
raw_file
: Represents RawFileAppender
Defaults to true
. If set to false
, the Appender will be disabled by default and can be enabled later using set_appenders_enable
.
An array enclosed in []
, containing any combination of verbose
, debug
, info
, warning
, error
, fatal
, or [all]
to accept all levels. Note: Do not include spaces between levels, or it will fail to parse.
Specifies the time zone of the logs. gmt
represents Greenwich Mean Time (UTC+0), and any other string or leaving it empty will use the local time zone. The time zone affects two things:
The timestamp of formatted text logs (applicable to ConsoleAppender and TextFileAppender)
A new log file will be created when midnight is crossed in the specified time zone (applicable to TextFileAppender, CompressedFileAppender, and RawFileAppender).
The path and filename prefix for saving files. The path can be absolute (not recommended for Android and iOS) or relative. The final output filename will be this path and name, followed by the date, file number, and the Appender's extension.
Only meaningful on Android:
true
: Files are stored in the Internal Storage directory (android.content.Context.getFilesDir()). If not available, they are stored in the External Storage directory (android.content.Context.getExternalFilesDir()). If that is also not available, they are stored in the Cache directory (android.content.Context.getCacheDir()).
false
: Files are stored in the External Storage directory by default. If not available, they are stored in the Internal Storage directory. If that is also not available, they are stored in the Cache directory.
The maximum file size in bytes. When the saved file exceeds this size, a new log file is created, with file numbers incrementing sequentially. 0
disables this feature.
The maximum number of days to keep files. Files older than this will be automatically deleted. 0
disables this feature.
The maximum total size of files output by this Appender in the output directory. If this limit is exceeded, files are deleted starting from the oldest until the total size is within the limit. 0
disables this feature.
If the log object is a Log object that supports categories, this can be used to filter a tree-like list of categories. When the array is not empty, this feature is active. For example, [*default,ModuleA,ModuleB.SystemC]
means that logs with the default category