API Reference
March 27, 2026 · View on GitHub
All language Wrappers share consistent API naming and semantics. Each API below shows all supported languages.
1. Create a Log object
Create a Log through the create_log static function. If the name already exists, the existing object is returned and its configuration is updated (some fields like buffer_size cannot be changed, see Configuration).
C++
#include <bq_log/bq_log.h>
// bq::string accepts char*, std::string, std::string_view implicitly
static bq::log bq::log::create_log(const bq::string& log_name, const bq::string& config_content);
Java / Kotlin
import bq.log;
bq.log logObj = bq.log.create_log("my_log", config);
C#
using bq;
bq.log logObj = bq.log.create_log("my_log", config);
TypeScript (Node.js / HarmonyOS ArkTS)
import { bq } from "@pippocao/bqlog"; // Node.js ESM
// import { bq } from "bqlog"; // HarmonyOS ArkTS
const logObj = bq.log.create_log("my_log", config);
Python
from bq.log import log
log_obj = log.create_log("my_log", config)
Key points:
- The return value will never be null in any language. If creation fails, check via
is_valid(). - If
log_nameis empty, BqLog auto-assigns a unique name like"AutoBqLog_1". - Calling
create_logwith an existing name reuses the object and updates its config. - Safe to call in global / static initializers — no Static Initialization Order issues.
2. Get a Log object
Retrieve an already-created Log object by name:
C++
static bq::log bq::log::get_log_by_name(const bq::string& log_name);
Java / Kotlin
bq.log logObj = bq.log.get_log_by_name("my_log");
C#
bq.log logObj = bq.log.get_log_by_name("my_log");
TypeScript
const logObj = bq.log.get_log_by_name("my_log");
Python
log_obj = log.get_log_by_name("my_log")
Note: Ensure the Log has been created via
create_logfirst, otherwiseis_valid()returns false.
3. Write logs
BqLog provides 6 log levels: verbose, debug, info, warning, error, fatal (importance increasing).
Format parameters use {} placeholders following C++20 std::format rules (no positional index or time formatting). Strongly recommend using format parameters instead of string concatenation for best performance and compression.
C++
log_obj.info("Hello {}, count={}", "world", 42);
log_obj.error("Error code: {}", err_code);
// All levels: verbose, debug, info, warning, error, fatal
Supported STR types: char*, char16_t*, char32_t*, wchar_t*, std::string, std::u16string, std::wstring, Unreal FString / FName / FText, etc.
Java / Kotlin
import static bq.utils.param.no_boxing;
logObj.info("Hello {}, count={}", "world", no_boxing(42));
logObj.error("Error code: {}", no_boxing(errCode));
// All levels: verbose, debug, info, warning, error, fatal
Important: Use
bq.utils.param.no_boxing()to wrap primitive types (int,float,boolean, etc.) to avoid boxing and GC pressure. Withoutno_boxing, primitives will be auto-boxed.
C#
logObj.info("Hello {}, count={}", "world", 42);
logObj.error("Error code: {}", errCode);
// All levels: verbose, debug, info, warning, error, fatal
When parameter count ≤ 12, zero boxing/unboxing occurs. Beyond 12 parameters, boxing degrades gracefully.
TypeScript
logObj.info("Hello {}, count={}", "world", 42);
logObj.error("Error code: {}", errCode);
// All levels: verbose, debug, info, warning, error, fatal
Parameters are passed directly via NAPI — no extra boxing.
Python
log_obj.info("Hello {}, count={}", "world", 42)
log_obj.error("Error code: {}", err_code)
# All levels: verbose, debug, info, warning, error, fatal
Supported parameter types
- Null pointer →
null - Pointer →
0xhex address bool, single/double/four-byte characters- 8/16/32/64-bit integers and unsigned integers
- 32-bit and 64-bit floating point numbers
- All string types in each language
- C# / Java: any object (via
ToString()) - C++: POD types (1/2/4/8 bytes), custom types (see Advanced Usage — Custom parameter types)

4. Force flush
Ensure all buffered logs are written to disk. Call before program exit or at critical checkpoints.
C++
bq::log::force_flush_all_logs(); // flush all log objects
log_obj.force_flush(); // flush single log object
Java / Kotlin
bq.log.force_flush_all_logs();
logObj.force_flush();
C#
bq.log.force_flush_all_logs();
logObj.force_flush();
TypeScript
bq.log.force_flush_all_logs();
logObj.force_flush();
Python
log.force_flush_all_logs()
log_obj.force_flush()
5. Crash protection
Enable automatic buffer flush on abnormal exit (POSIX only):
C++
bq::log::enable_auto_crash_handle();
Java / Kotlin
bq.log.enable_auto_crash_handle();
C#
bq.log.enable_auto_crash_handle();
TypeScript
bq.log.enable_auto_crash_handle();
Python
log.enable_auto_crash_handle()
See also: Advanced Usage — Data protection on abnormal exit and log.recovery config.
6. Intercept / fetch Console Output
Register callback
C++
static void bq::log::register_console_callback(bq::type_func_ptr_console_callback callback);
static void bq::log::unregister_console_callback(bq::type_func_ptr_console_callback callback);
Java / Kotlin
bq.log.register_console_callback(callback);
bq.log.unregister_console_callback(callback);
C#
bq.log.register_console_callback(callback);
bq.log.unregister_console_callback(callback);
TypeScript
bq.log.register_console_callback(callback);
bq.log.unregister_console_callback(callback);
Python
log.register_console_callback(callback)
log.unregister_console_callback(callback)
Note:
- Do not call any synchronous BqLog flush functions inside the callback — this will deadlock.
- Unity / Tuanjie / Unreal plugins already redirect ConsoleAppender to the editor log window automatically.
Actively fetch (for VM environments)
When direct native-thread callbacks are not suitable (C#, Java, IL2CPP, Node.js), use buffered fetch mode:
// Enable buffering (disables register_console_callback and default console output)
bq::log::set_console_buffer_enable(true);
// Poll from your logic thread — MUST call continuously to avoid unbounded memory growth
bq::log::fetch_and_remove_console_buffer(on_console_callback);
IL2CPP: Ensure callback is
static unsafewith[MonoPInvokeCallback(typeof(type_console_callback))].
7. Modify configuration at runtime
C++
bool success = log_obj.reset_config(new_config);
Java / Kotlin
boolean success = logObj.reset_config(newConfig);
C#
bool success = logObj.reset_config(newConfig);
TypeScript
const success = logObj.reset_config(newConfig);
Python
success = log_obj.reset_config(new_config)
Some fields (buffer_size, thread_mode) cannot be modified at runtime. See Configuration.
8. Enable / disable Appenders
C++
log_obj.set_appender_enable("appender_name", false); // disable
log_obj.set_appender_enable("appender_name", true); // re-enable
Java / Kotlin
logObj.set_appender_enable("appender_name", false); // disable
logObj.set_appender_enable("appender_name", true); // re-enable
C#
logObj.set_appender_enable("appender_name", false);
logObj.set_appender_enable("appender_name", true);
TypeScript
logObj.set_appender_enable("appender_name", false);
logObj.set_appender_enable("appender_name", true);
Python
log_obj.set_appender_enable("appender_name", False)
log_obj.set_appender_enable("appender_name", True)
9. Snapshot
Capture recent logs for anomaly reporting. Requires snapshot to be enabled in Configuration.
C++
bq::string snapshot = log_obj.take_snapshot("UTC+8");
// bq::string implicitly converts to std::string
Java / Kotlin
String snapshot = logObj.take_snapshot("UTC+8");
C#
string snapshot = logObj.take_snapshot("UTC+8");
TypeScript
const snapshot: string = logObj.take_snapshot("UTC+8");
Python
snapshot = log_obj.take_snapshot("UTC+8")
10. Decode binary log files
For decoding CompressedFileAppender logs at runtime:
// C++ only — other languages use the offline command-line tool
bq::tools::log_decoder decoder("path/to/file.logcompr", "optional_private_key");
while (decoder.decode() == bq::appender_decode_result::success) {
auto& text = decoder.get_last_decoded_log_entry();
// process text...
}
// Or decode entire file at once:
bq::tools::log_decoder::decode_file("input.logcompr", "output.txt", "optional_key");
Offline command-line decoder
Download {os}_{arch}_tools_{version} from Releases:
./BqLog_LogDecoder FileToDecode [-o OutputFile] [-k PrivateKeyFile]
Note: Binary format may be incompatible between BqLog versions. Use the matching decoder version. See also: Advanced Usage — Log encryption and decryption
Synchronous vs asynchronous logging
Controlled by log.thread_mode in Configuration.
Synchronous (sync) | Asynchronous (async / independent) | |
|---|---|---|
| Behavior | Log is processed immediately before function returns | Log is written to buffer, worker thread processes later |
| Performance | Lower — caller blocks until output completes | Higher — caller returns immediately |
| Thread Safety | Safe if parameters aren't modified during the call | Same guarantee; uses high-concurrency ring buffer internally |
Thread-safety notes
BqLog copies all parameters to the internal ring buffer during the call. Once the log function returns, all data is safely stored internally. The worker thread never accesses the caller's stack.
The only unsafe scenario is other threads modifying a parameter while the log call is in progress:
static std::string global_str = "hello"; // modified by other threads concurrently
void thread_a() {
log_obj.info("param: {}", global_str); // undefined if global_str is modified during this call
}
Rule: Ensure parameters are not modified during a single log call, regardless of sync/async mode.
Appender overview
| Name | Output | Readable | Performance | Size | Encryption |
|---|---|---|---|---|---|
| ConsoleAppender | Console / ADB Logcat | Yes | Low | - | No |
| TextFileAppender | UTF-8 text file | Yes | Low | Large | No |
| CompressedFileAppender | Binary compressed file | No | High | Small | Yes (RSA2048 + AES256) |
CompressedFileAppender is the recommended default for file output.
Build
All build scripts are in /build:
/build
├── demo # Demo Build
├── lib # Native Static / Dynamic Library Build
├── test # Test Project Build
├── tools # Tools (LogDecoder, CategoryLogGenerator, etc.)
├── wrapper # Language Wrapper Projects (Java / C# etc.)
├── benchmark # Benchmark Project Build
└── plugin # Game Engine Plugins (Unity / Tuanjie / Unreal)
Required env vars:
ANDROID_NDK_ROOT— for Android buildsJAVA_HOME— for Java wrapper (most scripts enable it by default)