Skip to content

Latest commit

 

History

History
899 lines (658 loc) · 24 KB

API_REFERENCE.md

File metadata and controls

899 lines (658 loc) · 24 KB

API Reference

← Back to Home | 简体中文

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)
Go
import bq "github.com/Tencent/BqLog/go/v2"
// the third argument is the category names, nil for a plain log
logObj := bq.Create_log("my_log", config, nil)

Key points:

  1. The return value will never be null in any language. If creation fails, check via is_valid().
  2. If log_name is empty, BqLog auto-assigns a unique name like "AutoBqLog_1".
  3. Calling create_log with an existing name reuses the object and updates its config.
  4. 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")
Go
logObj := bq.Get_log_by_name("my_log")

Note: Ensure the Log has been created via create_log first, otherwise is_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.

C++ only fast mode

Besides the normal mode (log_obj.info(...) and the other level functions), C++ has a fast mode: six macros that write exactly the same log entry while costing the calling thread much less.

BQ_LOG_FAST_INFO(log_obj, "Hello {}, count={}", "world", 42);
BQ_LOG_FAST_ERROR(log_obj, "Error code: {}", err_code);
// BQ_LOG_FAST_VERBOSE, BQ_LOG_FAST_DEBUG, BQ_LOG_FAST_INFO, BQ_LOG_FAST_WARNING, BQ_LOG_FAST_ERROR, BQ_LOG_FAST_FATAL

// A generated category log: the same macro, with the category before the format string, as in info(cat, ...)
BQ_LOG_FAST_INFO(my_category_log, my_category_log.cat.Shop.Seller, "order {} paid", order_id);

The first call at each call site registers its format string once; after that, the thread only checks the level, stores the timestamp and the raw arguments into its own buffer, and returns. The format string is not copied or hashed again, and nothing goes through a library function call.

Use it on hot paths. For everything else, the normal mode is the better default, because fast mode has a price:

  • Fixed log object, category and format string per call site. A call site binds the log object and format string of its first call (the category is a compile-time type, so it is fixed anyway). Passing a different log object or a different format string to the same call site later (for example through a helper function that forwards its arguments into one BQ_LOG_FAST_* line) is not supported: the entry still goes to the first log object with the first format string. Configuration changes (reset_config, levels, category masks) do apply.
  • Slightly more memory. Each registered format string is kept once in the log's metadata area for its lifetime, and every thread that uses fast mode keeps one 128-byte thread local slot.
  • Weaker IDE hints. These are macros, so code completion and parameter hints are not as helpful as with the member functions of the normal mode. Argument types are still checked at compile time.

Categories are supported: when the second argument is log.cat.xxx the entry goes to that category, when it is the format string the default category is used. Which one is decided at compile time, so a call with a category is as fast as one without. Supported arguments are the same as in the normal mode. When a level is configured to print a stack trace (log.print_stack_levels) or the log runs in sync mode, a fast mode call quietly falls back to the normal mode, so the output stays correct; it is just not faster in that case.

Macro-free fast log (C++20)

From C++20 the fast path is also available as a member function template, with the format string as a template argument:

log_obj.fast_info<"Hello {}, count={}">("world", 42);
log_obj.fast_error<"Error code: {}">(err_code);
// fast_verbose, fast_debug, fast_info, fast_warning, fast_error, fast_fatal

// With a category: the category first, then the format string
my_category_log.fast_info<my_category_log.cat.Shop.Seller, "order {} paid">(order_id);

It writes the same entries as BQ_LOG_FAST_* at the same cost. Unlike the macros, a call site is not bound to one log object, and IDE completion and compiler diagnostics work normally. This is also the fast log available through import bq.log;, since modules do not export macros.

The format string may be "", u8"", u"", U"" or L"". With MSVC, compile narrow non-ASCII literals with /utf-8.

Each distinct format string becomes part of a symbol name, so unstripped binaries grow faster than with the macros; strip release builds as usual.

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. Without no_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
Go
logObj.Info("Hello {}, count={}", "world", 42)
logObj.Error("Error code: {}", errCode)
// All levels: Verbose, Debug, Info, Warning, Error, Fatal

// A log created with category names (Create_category_log) takes the category index first
catLog := bq.Create_category_log("my_cat_log", config, []string{"", "Gameplay"})
catLog.Info_c(1, "score={}", 42)
// Verbose_c, Debug_c, Info_c, Warning_c, Error_c, Fatal_c

Any number of arguments. Built-in numbers, bool and string (including named types such as enums) keep their native types, so formatting still happens asynchronously on the worker thread. Values implementing fmt.Stringer / error are logged through String() / Error(), any other value through fmt.Sprint, and nil as null. Arguments are only converted after the level and category check passes. Every level function returns bool: whether the entry was written.

Supported parameter types

  • Null pointer → null
  • Pointer → 0x hex 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())
  • Go: any value (via String() / Error(), otherwise fmt.Sprint)
  • C++: POD types (1/2/4/8 bytes), custom types (see Advanced Usage — Custom parameter types)

Log Level


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()
Go
bq.Force_flush_all_logs()
logObj.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()
Go
bq.Enable_auto_crash_handle()

See also: Advanced Usage — Data protection on abnormal exit and log.recovery config.

Recovery capability

Whether log.recovery=true is supported on the current platform:

C++
bq::recovery_capability capability = bq::log::get_recovery_capability();
Native C ABI
bq_recovery_capability __api_get_recovery_capability(void);
Java / Kotlin
bq.def.recovery_capability capability = bq.log.get_recovery_capability();
C#
bq.def.recovery_capability capability = bq.log.get_recovery_capability();
TypeScript
const capability = bq.log.get_recovery_capability();
Python
capability = log.get_recovery_capability()
Go
capability := bq.Get_recovery_capability()

ready means recovery is available; any other value names what is missing.

Platform capabilities

What the linked library was actually built with, as bits — the same values it prints once at startup, so a bug report describes the binary instead of the caller's headers.

C++
uint32_t capabilities = bq::log::get_platform_capabilities();
if ((capabilities & bq::platform_capability::memory_map) == 0) {
    // crash recovery is unavailable on this build
}
Native C ABI
uint32_t __api_get_platform_capabilities(void);
Java / Kotlin
int capabilities = bq.log.get_platform_capabilities();
boolean memory_map = (capabilities & bq.def.platform_capability.memory_map) != 0;
C#
bq.def.platform_capability capabilities = bq.log.get_platform_capabilities();
bool memory_map = capabilities.HasFlag(bq.def.platform_capability.memory_map);
TypeScript
const capabilities = bq.log.get_platform_capabilities();
const memory_map = (capabilities & bq.platform_capability.memory_map) !== 0;
Python
capabilities = log.get_platform_capabilities()
memory_map = bool(capabilities & bq.defs.platform_capability.memory_map)
Go
capabilities := bq.Get_platform_capabilities()
memory_map := capabilities&def.Platform_capability_memory_map != 0   // def = the bq/def package
Bit Present means
memory_map buffers are file-backed; crash recovery needs it
multi_process several processes may share one log directory
cross_process_file_lock a second process can be kept out of an instance
crash_handler enable_auto_crash_handle() installs a handler
backtrace the stack field of an entry carries a backtrace
dirent expire_time_* and capacity_limit work
thread_name threads report a name instead of thread_<id>
default_base_dir a writable working directory was found; without it reset_base_dir is mandatory
user_counter timestamps come from a platform counter instead of the system clock

A console clears most of these on purpose; see Integration Guide — console platforms.


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)
Go
// callback: func(log_id uint64, category_idx int32, level def.Log_level, content string)
// (def is "github.com/Tencent/BqLog/go/v2/def")
bq.Register_console_callback(callback)
bq.Unregister_console_callback(callback)

Note:

  1. Do not call any synchronous BqLog flush functions inside the callback — this will deadlock.
  2. 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, Go), 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 unsafe with [MonoPInvokeCallback(typeof(type_console_callback))].

Go:

bq.Set_console_buffer_enable(true)

// fetches one entry per call, returns false when the buffer is empty;
// keep fetching from one goroutine pinned with runtime.LockOSThread
for bq.Fetch_and_remove_console_buffer(func(log_id uint64, category_idx int32, level def.Log_level, content string) {
    // ...
}) {
}

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)
Go
success := logObj.Reset_config(newConfig)

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)
Go
logObj.Set_appender_enable("appender_name", false)  // disable
logObj.Set_appender_enable("appender_name", true)   // re-enable

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")
Go
snapshot := logObj.Take_snapshot("UTC+8")

10. Decode binary log files

For decoding CompressedFileAppender logs at runtime:

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");

Go:

decoder := bq.Decoder_create("path/to/file.logcompr", "optional_private_key")
if decoder.Is_valid() {
    defer decoder.Destroy()
    for text, ok := decoder.Decode(); ok; text, ok = decoder.Decode() {
        // process text...
    }
}
// Or decode entire file at once:
bq.Decode_file("input.logcompr", "output.txt", "optional_key")

Offline command-line decoder

Download {os}_{arch}_tools_{version} from Releases, or install it with a package manager: brew install bqlog puts it on the PATH; vcpkg needs vcpkg install "bqlog[tools]" and installs it under installed/<triplet>/tools/bqlog/.

./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 builds
  • JAVA_HOME — for Java wrapper (most scripts enable it by default)