中文文档
BqLog是一款轻量级、高性能的日志系统,应用于《王者荣耀》等项目中,目前已成功部署并运行顺利。
Windows 64 位
苹果系统
Linux
iOS系统
Android(X86_64、arm64-v8a、armeabi-v7a)
Unix(在FreeBSD上通过测试)
C++
爪哇
科特林
C#
与现有的开源日志记录库相比,BqLog 提供了显着的性能优势(请参阅基准)。它不仅适用于服务器和客户端,而且与移动设备高度兼容。
内存消耗低,在10个线程、20,000,000条日志条目的Benchmark情况下,BqLog本身消耗的内存不到1MB。
提供高性能、高压缩的实时日志格式
可以在游戏引擎( Unity
、 Unreal
)中正常使用,支持Unreal提供的常见类型。
支持UTF-8
、 UTF-16
、 UTF-32
字符和字符串,以及常见的参数类型,如 bool、float、double 以及各种长度和类型的整数
支持C++20
format specifications
异步日志记录支持崩溃审查以避免数据丢失(受XLog启发)
体积极小,Android编译后动态库仅200k左右
在 Java 和 C# 中不会生成额外的堆分配,避免在运行时不断创建新对象
仅依赖于标准C语言库和平台API,并且可以在Android的ANDROID_STL = none
模式下编译
支持C++11
及以后的编译标准,可以在-Wall -Wextra -pedantic -Werror的严格要求下编译
编译模块基于CMake
,提供不同平台的编译脚本,使用方便
支持自定义参数类型
对代码建议非常友好
为什么 BqLog 这么快 - 高性能实时压缩日志格式
为什么BqLog这么快-高并发环形缓冲区
将 BqLog 集成到您的项目中
简单演示
架构概述
主流程API使用说明
1-创建日志对象
2-检索日志对象
3-记录消息
4-其他API
同步和异步日志记录
1. 异步日志记录的线程安全
追加器简介
1. 控制台Appender
2. 文本文件追加器
3. CompressedFileAppender(强烈推荐)
4.RawFileAppender
配置说明
1. 完整示例
2. 详细说明
二进制格式 Appender 的离线解码
构建说明
1. 库构建
2. 演示构建和运行
3. 自动化测试运行说明
4. 基准测试运行说明
高级使用主题
1. 无堆分配
2. 具有类别支持的日志对象
3、程序异常退出时的数据保护
4.关于NDK和ANDROID_STL=无
5. 自定义参数类型
6. 在虚幻引擎中使用BqLog
基准
1. 基准说明
2. BqLog C++ 基准代码
3. BqLog Java 基准代码
4.Log4j基准代码
5. 基准测试结果
BqLog 可以以多种形式集成到您的项目中。对于C++,它支持动态库、静态库和源文件。对于 Java 和 C#,它支持带有包装器源代码的动态库。以下是包含 BqLog 的方法:
代码存储库包含位于 /dist/dynamic_lib/ 中的预编译动态库文件。要使用库文件将 BqLog 集成到您的项目中,您需要执行以下操作:
选择与您的平台对应的动态库文件并将其添加到项目的构建系统中。
将 /dist/dynamic_lib/include 目录复制到您的项目中并将其添加到包含目录列表中。 (如果您使用的是 XCode 的 .framework 库,则可以跳过此步骤,因为 .framework 文件已包含头文件)。
代码存储库包含位于 /dist/static_lib/ 中的预编译静态库文件。要使用库文件将 BqLog 集成到您的项目中,您需要执行以下操作:
选择与您的平台对应的静态库文件并将其添加到项目的构建系统中。
将 /dist/static_lib/include 目录复制到您的项目中并将其添加到包含目录列表中。 (如果您使用的是 XCode 的 .framework 库,则可以跳过此步骤,因为 .framework 文件已包含头文件)。
BqLog还支持直接将源代码包含到您的项目中进行编译。要使用源代码集成 BqLog,请按照以下步骤操作:
将 /src 目录复制到您的项目中作为源代码参考。
将 /include 目录复制到您的项目中并将其添加到包含目录列表中。
如果在 Visual Studio 中编译 Windows 版本,请将 /Zc:__cplusplus 添加到编译选项,以确保正确确定当前 C++ 编译器标准支持。
如果使用 Android NDK 中的源代码,请参阅 4. 关于 NDK 和 ANDROID_STL = none 以了解重要注意事项。
在 C# 中,BqLog 可以通过本机动态库和 C# 包装器使用,支持 Mono、Microsoft CLR 和 Unity 引擎。 Unity 与 Mono 和 IL2CPP 模式兼容。要在 C# 中使用 BqLog,请按照下列步骤操作:
从/dist/dynamic_lib/中选择与您的平台对应的动态库文件并将其添加到您的项目中(对于Unity,请参阅Unity导入和配置插件)。
将 /wrapper/csharp/src 中的源代码文件复制到您的项目中。
在Java中,BqLog可以通过本机动态库和Java Wrapper使用,支持常见的JVM环境和Android。要将 BqLog 集成到 JVM 中,请执行以下步骤:
从/dist/dynamic_lib/中选择与您的平台对应的动态库文件并将其添加到您的项目中。
将 /wrapper/java/src 中的源代码文件复制到您的项目中。
(可选)如果您打算从 NDK 调用 BqLog,请将 /dist/dynamic_lib/include 目录复制到您的项目中并将其添加到包含目录列表中。
以下代码将向您的控制台输出超过 1000 条日志(如果在 Android 上,则为 ADB Logcat)
#如果已定义(WIN32) #include <windows.h>#endif#include <字符串>#include <bq_log/bq_log.h>int main() { #if Defined(WIN32) // 将 Windows 命令行切换为 UTF-8,因为 BqLog 以 UTF-8 编码输出所有最终文本,以避免显示问题 SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // 该字符串是日志配置。这里它配置了一个带有一个名为appender_0的appender(输出目标)的记录器,它输出到控制台。 std::string config = R"( # 该appender的输出目标是控制台appenders_config.appender_0.type=console # 该appender使用本地时间作为时间戳appenders_config.appender_0.time_zone=默认本地时间 # 该appender输出这6个级别的日志(中间没有空格)appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] )"; bq::log log = bq::log::create_log("my_first_log", config); // 使用配置创建一个日志对象 for(int i = 0; i < 1024; ++i) { log.info("这是一条信息测试日志,格式字符串为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"这是一个错误测试日志,格式字符串为UTF-32"); bq::log::force_flush_all_logs(); // BqLog默认为异步输出。为了确保日志在程序退出之前可见,请强制刷新以同步输出一次。 返回0; }
使用 System.Text;使用 System;public class demo_main { public static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; string config = @" # 该appender的输出目标是控制台appenders_config.appender_0.type=console # 该appender使用本地时间作为时间戳 ppenders_config.appender_0.time_zone=默认本地时间 # 该appender输出这6个级别的日志(中间没有空格)之间)appenders_config.appender_0.levels=[详细,调试,信息,警告,错误,致命]“; bq.log log = bq.log.create_log("my_first_log", config); // 使用配置创建日志对象 for (int i = 0; i < 1024; ++i) { log.info("这是一条信息测试日志,格式字符串为UTF-16,param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "字符串文本”,4.3464f); } bq.log.force_flush_all_logs(); Console.ReadKey(); }}
public class demo_main { public static void main(String[] args) { // TODO 自动生成的方法存根 String config = """ # 此appender 的输出目标是控制台appenders_config.appender_0.type=console # 此appender 使用本地时间for timestamps appenders_config.appender_0.time_zone=default local time # 该appender输出这6个级别的日志(中间没有空格) appenders_config.appender_0.levels=[详细、调试、信息、警告、错误、致命] """; bq.log log = bq.log.create_log("my_first_log", config); // 使用配置创建一个日志对象 for (int i = 0; i < 1024; ++i) { log.info("这是一条信息测试日志,格式字符串为 UTF-16,param int:{}, param bool :{}, param string:{}, param float:{}", i, true, “字符串文本”,4.3464f); bq.log.force_flush_all_logs(); } }
上图清楚地说明了BqLog的基本结构。图的右侧是 BqLog 库的内部实现,左侧是您的程序和代码。您的程序可以使用提供的包装器(针对不同语言的面向对象的 API)调用 BqLog。在该图中,创建了两个日志:一个名为“Log A”,另一个名为“Log B”。每个日志都附加到一个或多个 Appender。 Appender可以理解为日志内容的输出目标。这可以是控制台(Android 的 ADB Logcat 日志)、文本文件,甚至是压缩日志文件或常规二进制日志格式文件等特殊格式。
在同一个进程中,不同语言的包装器可以访问同一个 Log 对象。例如,如果在 Java 中创建了名为 Log A 的 Log 对象,则也可以通过名称 Log A 从 C++ 端访问和使用它。
在极端情况下,例如在 Android 系统上运行的 Unity 开发的游戏,您可能会在同一个应用程序中涉及 Java、Kotlin、C# 和 C++ 语言。它们都可以共享同一个 Log 对象。您可以使用create_log在Java端创建Log,然后使用get_log_by_name在其他语言中访问它。
注意:以下 API 在 bq::log(或 bq.log)类中声明。为了节省空间,仅列出 C++ API。 Java和C#中的API是相同的,这里不再赘述。
在 C++ 中, bq::string
是 BqLog 库中的 UTF-8 字符串类型。您还可以传入 c 样式字符串,例如 char 或std::string
或std::string_view
,它们将自动隐式转换。
可以使用 create_log 静态函数创建日志对象。其声明如下:
//C++ API /// <summary> /// 创建日志对象 /// </summary> /// <param name="log_name">如果日志名称为空字符串,bqLog 会自动为您分配一个唯一的日志名称。如果日志名称已经存在,它将返回之前存在的日志对象,并用新的配置覆盖之前的配置。</param> /// <param name="config_content">日志配置字符串</param> /// <returns>一个日志对象,如果创建失败,其 is_valid() 方法将返回 false</returns> static log create_log(const bq::string& log_name, const bq::string& config_content);
该代码通过传入日志对象的名称和配置字符串来创建日志对象。日志配置可以参考配置说明。以下是需要注意的几个关键点:
无论是C#还是Java,返回的日志对象永远不会为null。但是,由于配置错误或其他原因,可能会创建无效的日志对象。因此,您应该使用 is_valid() 函数来检查返回的对象。对无效对象执行操作可能会导致程序崩溃。
如果传递空字符串作为日志名称,bqLog 将自动生成唯一的日志名称,例如“AutoBqLog_1”。
对已存在的同名日志对象调用 create_log 不会创建新的日志对象,而是会用新的配置覆盖以前的配置。但此过程中有些参数无法修改;详细信息请参见配置说明。
除了在NDK中使用时(参考4.关于NDK和ANDROID_STL = none),其他情况下都可以使用该API直接在全局或静态变量中初始化日志对象。
如果已经在其他地方创建了日志对象,则可以直接使用 get_log_by_name 函数获取创建的日志对象。
//C++ API /// <summary> /// 通过名称获取日志对象 /// </summary> /// <param name="log_name">要查找的日志对象的名称</param > /// <returns>一个日志对象,如果没有找到指定名称的日志对象,则其 is_valid() 方法将返回 false</returns> static log get_log_by_name(const bq::string& log_name);
您还可以使用此函数来初始化全局变量或静态函数中的日志对象。但需要注意的是,必须确保指定名称的日志对象已经存在。否则,返回的日志对象将无法使用,其 is_valid() 方法将返回 false。
///核心日志功能,有6个日志级别: ///verbose, debug, info, warning, error, fatal template<typename STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> verbose(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> verbose(const STR& log_format_content, const Args&... args) const; 模板<类型名称 STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> debug(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> debug(const STR& log_format_content, const Args&... args) const; 模板<类型名称 STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> info(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> info(const STR& log_format_content, const Args&... args) const; 模板<类型名称 STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> warning(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> 警告(const STR& log_format_content, const Args&... args) const; 模板<类型名称 STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> error(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> error(const STR& log_format_content, const Args&... args) const; 模板<类型名称 STR> bq::enable_if_t<is_bq_log_str<STR>::value, bool> fatal(const STR& log_content) const; 模板<类型名称 STR, 类型名称...参数> bq::enable_if_t<is_bq_log_str<STR>::value, bool> fatal(const STR& log_format_content, const Args&... args) const;
记录消息时,要注意三个要点:
可以看到,我们的日志分为六个级别:verbose、debug、info、warning、error、fatal,与Android一致。它们的重要性依次增加。当输出到控制台时,它们会以不同的颜色显示。
STR参数与printf的第一个参数类似,可以是各种常见的字符串类型,包括:
Java 的 java.lang.String
C# 的字符串
C++ 的 C 风格字符串和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
甚至自定义字符串类型,可以参考自定义参数类型)
您可以在 STR 参数后添加各种参数。这些参数将被格式化为 STR 中的指定位置,遵循类似于 C++20 的 std::format 的规则(除了缺乏对位置参数和日期时间格式的支持)。例如,使用单个 {} 表示参数的默认格式,而 {:.2f} 指定格式化浮点数的精度。尽量使用格式化参数输出日志,而不是手动拼接字符串。此方法对于性能和压缩格式存储而言是最佳的。
目前支持的参数类型包括:
空指针(输出为空)
指针(以 0x 开头的十六进制地址输出)
布尔值
单字节字符(char)
双字节字符(char16_t、wchar_t、C# 的 char、Java 的 char)
四字节字符(char32_t 或 wchar_t)
8 位整数
8 位无符号整数
16 位整数
16 位无符号整数
32 位整数
32 位无符号整数
64 位整数
64 位无符号整数
32 位浮点数
64 位浮点数
C++ 中其他未知的 POD 类型(大小限制为 1、2、4 或 8 字节,分别视为 int8、int16、int32 和 int64)
字符串,包括STR参数中提到的所有字符串类型
C# 和 Java 中的任何类或对象(输出其 ToString() 字符串)
自定义参数类型,详见自定义参数类型
还有其他常用的 API 可以完成特定的任务。详细的API说明请参考bq_log/bq_log.h,以及Java和C#中的bq.log类。以下是一些需要强调的关键 API:
/// <summary> /// 取消初始化BqLog,请在程序存在之前调用此函数。 /// </summary> static void uninit();
建议在退出程序或卸载使用BqLog的自行实现的动态库之前执行uninit()
,否则在某些特定情况下退出时可能会卡住。
/// <summary> /// 如果bqLog是异步的,程序崩溃可能会导致缓冲区中的日志无法持久化到磁盘。 /// 如果启用此功能,bqLog 将在发生崩溃时尝试强制刷新缓冲区中的日志。但是, /// 此功能并不保证成功,并且仅支持 POSIX 系统。 /// </summary>静态无效enable_auto_crash_handle();
详细介绍请参见程序异常退出数据保护
/// <summary> /// 同步刷新所有日志对象的缓冲区 /// 以确保调用后处理缓冲区中的所有数据。 /// </summary> 静态无效force_flush_all_logs(); /// <summary> /// 同步刷新此日志对象的缓冲区 /// 以确保调用后处理缓冲区中的所有数据。 /// </summary> 无效force_flush();
由于 bqLog 默认使用异步日志记录,因此有时您可能希望立即同步并输出所有日志。在这种情况下,您需要强制调用force_flush()。
/// <summary> /// 注册一个回调,每当输出控制台日志消息时都会调用该回调。 /// 这可用于外部系统监视控制台日志输出。 /// </summary> /// <param name="callback"></param> static void register_console_callback(bq::type_func_ptr_console_callback 回调); /// <summary> /// 取消注册控制台回调。 /// </summary> /// <param name="callback"></param> static void unregister_console_callback(bq::type_func_ptr_console_callback 回调);
ConsoleAppender 的输出会发送到 Android 上的控制台或 ADB Logcat 日志,但这可能无法涵盖所有情况。例如,在自定义游戏引擎或自定义 IDE 中,提供了一种机制来为每个控制台日志输出调用回调函数。这允许您在程序中的任何位置重新处理和输出控制台日志。
注意:不要在控制台回调中输出任何同步的 BQ 日志,因为这很容易导致死锁。
/// <summary> /// 启用或禁用控制台附加器缓冲区。 /// 由于我们的包装器可以在 C# 和 Java 虚拟机中运行,并且我们不想直接从本机线程调用回调, /// 我们可以启用此选项。这样,所有控制台输出都将保存在缓冲区中,直到我们获取它们。 /// </summary> /// <param name="enable"></param> /// <returns></returns> static void set_console_buffer_enable(bool enable); /// <summary> /// 以线程安全的方式从控制台追加器缓冲区中获取和删除日志条目。 /// 如果控制台追加器缓冲区不为空,则将为此日志条目调用 on_console_callback 函数。 /// 请确保回调函数中不要输出同步的BQ日志。 /// </summary> /// <param name="on_console_callback">如果控制台追加器缓冲区不为空,则为获取的日志条目调用回调函数</param> /// <returns>如果控制台追加器缓冲区不为空并且已获取日志条目; </returns> static bool fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback); 否则返回 False。
除了通过控制台回调拦截控制台输出之外,您还可以主动获取控制台日志输出。有时,我们可能不希望控制台日志输出通过回调来实现,因为您不知道回调将来自哪个线程(例如,在某些 C# 虚拟机或 JVM 中,当控制台日志输出时,虚拟机可能正在执行垃圾收集)调用回调,这可能会导致挂起或崩溃)。
这里使用的方法涉及通过set_console_buffer_enable
启用控制台缓冲区。这会导致每个控制台日志输出都存储在内存中,直到我们主动调用fetch_and_remove_console_buffer
来检索它。因此,如果您选择使用此方法,请记住及时获取并清除日志,以避免内存未释放。
注意:不要在控制台回调中输出任何同步的 BQ 日志,因为这很容易导致死锁。
其他注意事项:如果您在 IL2CPP 环境中使用此代码,请确保 on_console_callback 被标记为静态不安全,并使用 [MonoPInvokeCallback(typeof(type_console_callback))] 属性进行修饰。
/// <summary> /// 修改日志配置,但有些字段不能修改,如buffer_size。 /// </summary> /// <param name="config_content"></param> /// <returns></returns> bool reset_config(const bq::string& config_content);
有时您可能想要修改程序中日志的配置。除了重新创建日志对象来覆盖配置(参见创建日志对象),您还可以使用重置接口。但请注意,并非所有配置项都可以通过这种方式修改。详细信息请参见配置说明
/// <summary> /// 暂时禁用或启用特定的 Appender。 /// </summary> /// <param name="appender_name"></param> /// <param name="enable"></param> void set_appenders_enable(const bq::string&appender_name, bool enable) ;
默认情况下,配置中的 Appender 是活动的,但这里提供了一种机制来临时禁用和重新启用它们。
/// <summary> /// 仅在配置快照时有效。 /// 它将快照缓冲区解码为文本。 /// </summary> /// <param name="use_gmt_time">每个日志的时间戳是GMT时间还是当地时间</param> /// <returns>解码后的快照缓冲区</returns> bq::string take_snapshot(bool use_gmt_time) const;
有时,某些特殊功能需要输出日志的最后一部分,这可以使用快照功能来完成。要启用此功能,您首先需要在日志配置中激活快照并设置最大缓冲区大小(以字节为单位)。此外,您需要指定要为快照过滤的日志级别和类别(可选)。详细配置请参见快照配置。当需要快照时,调用 take_snapshot() 将返回包含存储在快照缓冲区中的最新日志条目的格式化字符串。在 C++ 中,类型为bq::string
,可以隐式转换为std::string
。
命名空间bq{命名空间工具{ //这是一个用于解码二进制日志格式的实用程序类。 //使用时,首先创建一个log_decoder对象, //然后调用其decode函数进行解码。 //每次成功调用后, //您可以使用 get_last_decoded_log_entry() 检索解码结果。 //每次调用都会解码一个日志条目。 结构体log_decoder { 私人的: bq::字符串解码_文本_; bq::appender_decode_result result_ = bq::appender_decode_result::成功; uint32_t 句柄_ = 0; public: /// <summary> /// 创建一个log_decoder对象,每个log_decoder对象对应一个二进制日志文件。 /// </summary> /// <param name="log_file_path">二进制日志文件的路径,可以是相对路径或绝对路径</param> log_decoder(const bq::string& log_file_path); 〜log_decoder(); /// <summary> /// 解码日志条目。每次调用此函数只会解码 1 个日志条目 /// </summary> /// <returns>解码结果,appender_decode_result::eof 表示整个日志文件已解码</returns> bq::appender_decode_result 解码(); /// <summary> /// 获取最后的解码结果 /// </summary> /// <returns></returns> bq::appender_decode_result get_last_decode_result() const; /// <summary> /// 获取最后一次解码日志条目内容 /// </summary> /// <returns></returns> const bq::string& get_last_decoded_log_entry() const; }; } }
这是一个实用程序类,可以在运行时解码二进制类型 Appender 输出的日志文件,例如 CompressedFileAppender 和 RawFileAppender。
要使用它,首先创建一个 log_decoder 对象。然后,每次调用decode()函数时,它都会按顺序解码一个日志条目。如果返回结果为bq::appender_decode_result::success,则可以调用get_last_decoded_log_entry()获取最后一条解码日志条目的格式化文本内容。如果结果是bq::appender_decode_result::eof,则表示所有日志都已读取完毕。
BqLog 允许您通过 thread_mode 设置来配置日志对象是同步还是异步。这两种模式的主要区别如下:
同步记录 | 异步日志记录 | |
---|---|---|
行为 | 调用logging函数后,日志立即写入对应的appender中。 | 调用日志函数后,日志并没有立即写入;相反,它被移交给工作线程进行定期处理。 |
表现 | 低,因为写入日志的线程需要阻塞并等待日志写入相应的appender,然后才能从日志记录函数返回。 | 高,因为写入日志的线程不需要等待实际输出,可以在记录后立即返回。 |
线程安全 | 高,但要求日志功能执行过程中不修改日志参数。 | 高,但要求日志功能执行过程中不修改日志参数。 |
关于异步日志记录的一个常见误解是它的线程安全性较低,用户担心在工作线程处理日志时参数可能会被回收。例如:
{ const char str_array[5] = {'T', 'E', 'S', 'T', '�'}; const char* str_ptr = str_array; log_obj.info("这是测试参数:{},{}",str_array,str_ptr); }
在上面的例子中, str_array
存储在堆栈上,一旦退出作用域,它的内存就不再有效。用户可能会担心,如果使用异步日志记录,当工作线程处理日志时, str_array
和str_ptr
将成为无效变量。
但不会出现这种情况,因为BqLog在执行info
函数的过程中将所有参数内容复制到其内部ring_buffer
中。一旦info
函数返回,就不再需要str_array
或str_ptr
等外部变量。此外, ring_buffer
不会存储const char*
指针地址,而是始终存储整个字符串。
真正的潜在问题出现在以下场景中:
静态 std::string global_str = "你好世界"; // 这是一个被多个线程修改的全局变量。void thread_a() { log_obj.info("这是测试参数:{}", global_str); }
如果在info
函数执行过程中global_str
的内容发生变化,可能会导致未定义的行为。 BqLog会尽力防止崩溃,但无法保证最终输出的正确性。
Appender代表日志输出目标。 bqLog 中 Appender 的概念与 Log4j 中基本相同。目前bqLog提供了以下几种Appender:
这个Appender的输出目标是控制台,包括Android的ADB和iOS上对应的控制台。文本编码为UTF-8。
该Appender直接以UTF-8文本格式输出日志文件。
此 Appender 以压缩格式输出日志文件,这是highly recommended format by bqLog
。它在所有 Appender 中具有最高的性能,并生成最小的输出文件。但是,最终文件需要解码。解码可以是运行时解码,也可以是离线解码。
该Appender将二进制日志内容从内存直接输出到文件。它的性能比TextFileAppender高,但消耗更多的存储空间。最终文件需要解码。解码可以是运行时解码,也可以是离线解码。不建议使用此Appender。
下面对各种Appender进行综合比较:
姓名 | 输出目标 | 直接可读 | 输出性能 | 输出尺寸 |
---|---|---|---|---|
控制台附加程序 | 安慰 | ✔ | 低的 | - |
文本文件附加器 | 文件 | ✔ | 低的 | 大的 |
压缩文件附加器 | 文件 | ✘ | 高的 | 小的 |
原始文件附加器 | 文件 | ✘ | 中等的 | 大的 |
配置是指create_log和reset_config函数中的配置字符串。该字符串使用属性文件格式并支持 # 注释(但请记住以 # 开始新行作为注释)。
下面是一个完整的例子:
#此配置设置了一个带有5个附录的日志对象,其中包括两个输出到两个不同文件的textfileappender。#第一个appender命名为appender_0,其类型为consoleappenderappenders_config.appender_0.typeder_0.type系统的本地timeappenders_config.appender_0.time_zone =默认本地时间#appender_0将输出所有6个日志级别(注意:应该在这里在日志级别之间没有空格,或者将无法解析)appenders_config.appender_0.levels = [冗长,调试,信息,信息,警告,错误,致命]#第二个appender命名为appender_1,其类型为textfileaeappenderappenders_confenders_confender_appender_appender_1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.type = text_file#appender_1的时区是gmt,是utc+0appenders_config.appender_1.time_zone = gmt## appender_1仅输出级别信息的日志及以上,其他人将被忽略appenders_config.appender_1.levels = [info,警告,错误,致命,致命]#appender_1的路径将在程序的相对bqlog目录中,并以正常,正常的文件名开头的文件名,其次是iOS上的日期和.log扩展名#,它将保存在/var/mobile/containser/data/application/[app]/library/caches/bqlog#on中Android,它将保存在[android.content.context.getexternalfilesdir()]/bqlogappenders_config.appender_1.file_name = bqlog/normal#最大文件大小为10,000,000字节;如果超过超过,将创建一个新文件,请创建一个新文件。config.appender_1.max_file_size= 10000000#年龄大于十天的文件将被清理upappenders_config.appender_1.expire_days_days_days_days = 10#如果总尺寸从输出的总尺寸超过100,000,000亿字节,则文件将超过100,000,000亿字节。 oldestappenders_config.appender_1.capacity_limit = 100000000#第三个appender命名为appender_2,其类型是textfileappenderappenders_config.config.appender_2.type = text_file = text_file#appender_2将输出所有级别的logsappenders_config.appender_appender_2.2.2.2.levels = [app in app = [app y app y app]该程序的相对BQLOG目录,文件名开头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 the the the the the appender命名为appender_3,其类型为compressedfileappenderAppenders_config.appender_3.type = compressed_file#appender_3将输出所有级别的logsappenders_config.appender_3.levels = [all]#appender_3的路径将在程序的绝对路径〜/bqlog timples中,并由Compress_log启动,随后是compress_log启动,随后是compress_log启动。日期和.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将输出所有级别logSappenders_config.appender_4.levels = [all]#appender_4的路径将在程序的相对bqlog目录中,文件名从raw_log开始,然后是日期和.lograwextensionAppenders_config.appender_appender_appender_.appender_4.file_name = bqlog = bqlog/raw_log#loar_log#loogs will will will_law_log#loogs will仅当其类别从模块化,moduleb.systemc开始时,才能处理,否则将全部忽略(类别的概念在详细说明高级用法主题稍后)appenders_config.appender_4.categories_mask = [modulea,moduleb.systemc]#总异步缓冲区大小为65535字节;特定含义被解释了laterlog.buffer_size = 65535#日志的可靠性级别是正常的;解释了特定含义。reliable_level =正常#日志只有在其类别与以下三个通配符匹配时,才会处理。 [*默认,模块,moduleb.systemc]#这是一个异步日志;异步日志是性能最高的,建议的日志typelog.thread_mode = async#如果日志级别是错误的或致命的,请在每个日志entrylog.print_stack_levels = [errim,error,致命,致命,致命的]#enable snapshot功能中,包括呼叫堆栈信息,snapshot snapshot size size sige snapshot Cache size size sige signapsnapsnapshotsnapshotsnapshot .buffer_size = 65536#仅在snapshotsnapshot.levels = [info,error]#类别开头的日志中,将记录具有信息和错误级别的日志。 Moduleb.Systemc将记录在快照中,否则将被忽略SnapShot.categories_mask = [modulea.systema.classa,moduleb]
appenders_config
是附录的一组配置。以下的第一个参数appenders_config
是Appender的名称,所有具有相同名称的附录共享相同的配置。
姓名 | 必需的 | 可配置的值 | 默认 | 适用于Consoleappender | 适用于textfileappender | 适用于压缩fileappender | 适用于RawFileAppender |
---|---|---|---|---|---|---|---|
类型 | ✔ | 控制台,text_file,compressed_file,raw_file | ✔ | ✔ | ✔ | ✔ | |
使能够 | ✘ | 默认情况下是否启用了appender | 真的 | ✔ | ✔ | ✔ | ✔ |
级别 | ✘ | 日志级别数组 | [全部] | ✔ | ✔ | ✔ | ✔ |
时区 | ✘ | GMT或任何其他字符串 | 当地时间 | ✔ | ✔ | ✔ | ✔ |
文件名 | ✔ | 相对或绝对路径 | ✘ | ✔ | ✔ | ✔ | |
is_in_sandbox | ✘ | 真,假 | 错误的 | ✘ | ✔ | ✔ | ✔ |
max_file_size | ✘ | 正整数或0 | 0 | ✘ | ✔ | ✔ | ✔ |
Expire_Time_days | ✘ | 正整数或0 | 0 | ✘ | ✔ | ✔ | ✔ |
locatiess_limit | ✘ | 正整数或0 | 0 | ✘ | ✔ | ✔ | ✔ |
categories_mask | ✘ | 封闭在[]中的一系列字符串 | 空的 | ✔ | ✔ | ✔ | ✔ |
指定Appender的类型。
console
:代表consoleappender
text_file
:代表textfileappender
compressed_file
:表示压缩fileappender
raw_file
:代表RawFileAppender
默认为true
。如果设置为false
,则默认情况下将禁用Appender,并可以在以后使用set_appenders_enable
启用。
一个包含在[]
中的数组,其中包含verbose
, debug
, info
, warning
, error
, fatal
或[all]
的任何组合,以接受所有级别。注意:请勿在级别之间包含空格,否则将无法解析。
指定日志的时区。 gmt
代表格林威治的平均时间(UTC+0),任何其他字符串或将其空的字符串都将使用本地时区。时区影响两件事:
格式的文本日志的时间戳(适用于consoleappender和textfileappender)
当在指定的时区(适用于TextFileAppender,CompressedFileAppender和RawFileAppender)中跨越午夜时,将创建一个新的日志文件。
保存文件的路径和文件名前缀。该路径可以是绝对的(不建议用于Android和iOS)或相对。最终输出文件名将是此路径和名称,其次是日期,文件编号和Appender的扩展名。
仅在Android上有意义:
true
:文件存储在内部存储目录(android.content.context.getfilesdir())中。如果不可用,它们将存储在外部存储目录(android.content.context.getExternalFilesdir())中。如果也不可用,则将它们存储在CACHE Directory(android.content.context.getCachedir())中。
false
:默认情况下,文件存储在外部存储目录中。如果不可用,则将它们存储在内部存储目录中。如果也不可用,则将它们存储在缓存目录中。
字节中的最大文件大小。当保存的文件超过此大小时,将创建一个新的日志文件,并将文件编号顺序增加。 0
禁用此功能。
保留文件的最大天数。将自动删除的文件将自动删除。 0
禁用此功能。
该应用程序在输出目录中输出的文件的最大总大小。如果超过此限制,则从最旧开始删除文件,直到总尺寸在限制内。 0
禁用此功能。
如果日志对象是支持类别的日志对象,则可以用来过滤类似树的类别列表。当数组不为空时,此功能处于活动状态。例如, [*default,ModuleA,ModuleB.SystemC]
表示使用默认类别登录