中文文档
BqLog — это легкая и высокопроизводительная система журналирования, используемая в таких проектах, как «Honor of Kings», она успешно развернута и работает без сбоев.
Windows 64 бит
MacOS
Линукс
iOS
Android (X86_64, Arm64-v8a、armeabi-v7a)
Unix(Пройти тест на FreeBSD)
С++
Ява
Котлин
С#
По сравнению с существующими библиотеками журналирования с открытым исходным кодом BqLog предлагает значительные преимущества в производительности (см. Тестирование). Он не только подходит для серверов и клиентов, но также полностью совместим с мобильными устройствами.
При низком потреблении памяти, в случае Benchmark с 10 потоками и 20 000 000 записей журнала, сам BqLog потребляет менее 1 МБ памяти.
Обеспечивает высокопроизводительный формат журнала в реальном времени с высокой степенью сжатия.
Может нормально использоваться в игровых движках ( Unity
, Unreal
), с поддержкой распространенных типов, предусмотренных для Unreal.
Поддерживает символы и строки UTF-8
, UTF-16
, UTF-32
, а также общие типы параметров, такие как bool, float, double, а также различные длины и типы целых чисел.
Поддерживает format specifications
C++20
Асинхронное ведение журнала поддерживает проверку сбоев, чтобы избежать потери данных (на основе XLog).
Чрезвычайно маленький размер: динамическая библиотека после компиляции Android занимает всего около 200 КБ.
Не создает дополнительных выделений кучи в 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. КонсольАппендер
2. ТекстовыйФайлАппендер
3. CompressedFileAppender (настоятельно рекомендуется)
4. RawFileAppender
Инструкции по настройке
1. Полный пример
2. Подробное объяснение
Автономное декодирование приложений двоичного формата
Инструкции по сборке
1. Сборка библиотеки
2. Сборка и запуск демо-версии
3. Инструкции по автоматическому тестовому запуску
4. Инструкции по тестированию производительности
Темы для продвинутых пользователей
1. Никакого распределения кучи
2. Объекты журнала с поддержкой категорий
3. Защита данных при аварийном выходе из программы
4. Про NDK и ANDROID_STL = нет
5. Типы пользовательских параметров
6. Использование BqLog в Unreal Engine
Контрольный показатель
1. Описание теста
2. Тестовый код BqLog C++
3. Код тестирования Java BqLog
4. Тестовый код Log4j
5. Результаты тестирования
BqLog может быть интегрирован в ваш проект в различных формах. Для C++ он поддерживает динамические библиотеки, статические библиотеки и исходные файлы. Для Java и C# он поддерживает динамические библиотеки с исходным кодом-оболочкой. Ниже приведены способы включения BqLog:
Репозиторий кода включает предварительно скомпилированные файлы динамической библиотеки, расположенные в /dist/dynamic_lib/. Чтобы интегрировать BqLog в свой проект с помощью файлов библиотеки, вам необходимо сделать следующее:
Выберите файл динамической библиотеки, соответствующий вашей платформе, и добавьте его в систему сборки вашего проекта.
Скопируйте каталог /dist/dynamic_lib/include в свой проект и добавьте его в список каталогов включения. (Если вы используете библиотеку .framework XCode, вы можете пропустить этот шаг, поскольку файл .framework уже включает файлы заголовков).
Репозиторий кода включает предварительно скомпилированные файлы статической библиотеки, расположенные в /dist/static_lib/. Чтобы интегрировать BqLog в свой проект с помощью файлов библиотеки, вам необходимо сделать следующее:
Выберите файл статической библиотеки, соответствующий вашей платформе, и добавьте его в систему сборки вашего проекта.
Скопируйте каталог /dist/static_lib/include в свой проект и добавьте его в список каталогов включения. (Если вы используете библиотеку .framework XCode, вы можете пропустить этот шаг, поскольку файл .framework уже включает файлы заголовков).
BqLog также поддерживает прямое включение исходного кода в ваш проект для компиляции. Чтобы интегрировать BqLog с использованием исходного кода, выполните следующие действия:
Скопируйте каталог /src в свой проект в качестве ссылки на исходный код.
Скопируйте каталог /include в свой проект и добавьте его в список каталогов включения.
При компиляции версии Windows в Visual Studio добавьте /Zc:__cplusplus к параметрам компиляции, чтобы обеспечить правильное определение текущей стандартной поддержки компилятора C++.
Если вы используете исходный код в Android NDK, обратитесь к разделу 4. О NDK и ANDROID_STL = none для получения важных сведений.
В C# BqLog можно использовать через собственную динамическую библиотеку и оболочку C#, поддерживающую механизмы Mono, Microsoft CLR и Unity. Unity совместим как с режимами Mono, так и с IL2CPP. Чтобы использовать BqLog в C#, выполните следующие действия:
Выберите файл динамической библиотеки, соответствующий вашей платформе, из /dist/dynamic_lib/ и добавьте его в свой проект (для Unity см. Импорт Unity и настройку плагинов).
Скопируйте файлы исходного кода из /wrapper/csharp/src в свой проект.
В Java BqLog можно использовать через собственную динамическую библиотеку и Java-оболочку, поддерживающую распространенные среды JVM и Android. Чтобы интегрировать BqLog в JVM, выполните следующие действия:
Выберите файл динамической библиотеки, соответствующий вашей платформе, из /dist/dynamic_lib/ и добавьте его в свой проект.
Скопируйте файлы исходного кода из /wrapper/java/src в свой проект.
(Необязательно) Скопируйте каталог /dist/dynamic_lib/include в свой проект и добавьте его в список каталогов включения, если вы собираетесь вызывать BqLog из NDK.
Следующий код выведет более 1000 журналов на вашу консоль (или ADB Logcat, если на Android).
#если определено(WIN32) #include#endif#include #include int main() { #if определено(WIN32) // Переключение командной строки Windows на UTF-8, поскольку BqLog выводит весь окончательный текст в кодировке UTF-8, чтобы избежать проблем с отображением SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // Эта строка представляет собой конфигурацию журнала. Здесь он настраивает регистратор с одним приложением (цель вывода) с именем Appender_0, которое выводит данные на консоль. std::string config = R"( # Целью вывода этого приложения является консоль Appenders_config.appender_0.type=console # Это приложение использует местное время для временных меток Appenders_config.appender_0.time_zone=default Local Time # Это приложение выводит журналы этих 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, параметр int:{}, параметр bool :{}, параметр string8:{}, параметр string16:{}, параметр 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;using System;public class demo_main { public static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; string config = @" # Целью вывода этого приложения является консоль Appenders_config.appender_0.type=console # Это приложение использует местное время для временных меток ppenders_config.appender_0.time_zone=default local time # Это приложение выводит журналы этих 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-16, параметр int:{}, параметр bool :{}, строка параметров:{}, параметр float:{}", i, true, " Строковый текст", 4.3464f); } bq.log.force_flush_all_logs(); Консоль.ReadKey(); }}
public class demo_main { public static void main(String[] args) { // TODO Автоматически сгенерированная заглушка метода String config = """ # Целью вывода этого приложения является консоль Appenders_config.appender_0.type=console # Это приложение использует местное время для меток времени Appenders_config.appender_0.time_zone=default local time # Это приложение выводит журналы этих 6 уровней (без пробелов между ними) Appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] """; bq.log log = bq.log.create_log("my_first_log", config); // Создаем объект журнала, используя конфигурацию для (int i = 0; i < 1024; ++i) { log.info("Это журнал информационного теста, строка формата UTF-16, параметр int:{}, параметр bool :{}, строка параметров:{}, параметр float:{}", i, true, «Строковый текст», 4.3464f); } bq.log.force_flush_all_logs(); } }
Диаграмма выше наглядно иллюстрирует базовую структуру BqLog. В правой части диаграммы находится внутренняя реализация библиотеки BqLog, а в левой — ваша программа и код. Ваша программа может вызывать BqLog, используя предоставленные оболочки (объектно-ориентированные API для разных языков). На схеме создаются два журнала: один с именем «Журнал A», а другой с именем «Журнал B». Каждый журнал прикреплен к одному или нескольким Appenders. Appender можно понимать как цель вывода содержимого журнала. Это может быть консоль (журналы ADB Logcat для Android), текстовые файлы или даже специализированные форматы, такие как сжатые файлы журналов или обычные файлы двоичного формата журналов.
В рамках одного процесса оболочки для разных языков могут обращаться к одному и тому же объекту журнала. Например, если объект журнала с именем Log A создан в Java, к нему также можно получить доступ и использовать его со стороны C++ под именем Log A.
В крайних случаях, например, когда игра, разработанная на Unity, работает в системе Android, вы можете использовать языки Java, Kotlin, C# и C++ в одном приложении. Все они могут использовать один и тот же объект журнала. Вы можете создать журнал на стороне Java, используя create_log, а затем получить к нему доступ на других языках, используя get_log_by_name.
Примечание. Следующие API объявлены в классе bq::log (или bq.log). Для экономии места в списке перечислены только API C++. API в Java и C# идентичны и не будут здесь повторяться.
В C++ bq::string
— это строковый тип UTF-8 в библиотеке BqLog. Вы также можете передавать строки в стиле C, такие как char или std::string
или std::string_view
, которые будут автоматически и неявно преобразованы.
Объект журнала можно создать с помощью статической функции create_log. Его декларация выглядит следующим образом:
//C++ API ////// Создайте объект журнала /// /// Если имя журнала представляет собой пустую строку, bqLog автоматически назначит вам уникальное имя журнала. Если имя журнала уже существует, оно вернет ранее существовавший объект журнала и заменит предыдущую конфигурацию новой конфигурацией. /// строка конфигурации журнала ///Объект журнала, если создание не удалось, его метод is_valid() вернет false static log create_log(const bq::string& log_name, const bq::string& config_content);
Код создает объект журнала, передавая имя объекта журнала и строку конфигурации. Конфигурацию журнала можно найти в инструкциях по настройке. Вот несколько ключевых моментов, на которые следует обратить внимание:
Независимо от того, C# это или Java, возвращаемый объект журнала никогда не будет нулевым. Однако из-за ошибок конфигурации или по другим причинам может быть создан недопустимый объект журнала. Поэтому вам следует использовать функцию is_valid() для проверки возвращаемого объекта. Выполнение операций с недопустимым объектом может привести к сбою программы.
Если в качестве имени журнала передается пустая строка, bqLog автоматически сгенерирует уникальное имя журнала, например «AutoBqLog_1».
Вызов create_log для уже существующего объекта журнала с тем же именем не создаст новый объект журнала, а перезапишет предыдущую конфигурацию новой. Однако некоторые параметры в этом процессе изменить нельзя; подробности см. в инструкциях по настройке.
За исключением случаев использования в NDK (см. 4. О NDK и ANDROID_STL = none), вы можете инициализировать объект журнала непосредственно в глобальных или статических переменных, используя этот API в других ситуациях.
Если объект журнала уже был создан где-то еще, вы можете получить созданный объект журнала напрямую, используя функцию get_log_by_name.
//C++ API ////// Получить объект журнала по его имени /// /// Имя объекта журнала, который вы хотите найти ///Объект журнала, если объект журнала с определенным именем не найден, его метод is_valid() вернет false static log get_log_by_name(const bq::string& log_name);
Вы также можете использовать эту функцию для инициализации объекта журнала в глобальных переменных или статических функциях. Однако обратите внимание, что вы должны убедиться, что объект журнала с указанным именем уже существует. В противном случае возвращенный объект журнала будет непригоден для использования, а его метод is_valid() вернет false.
///Основные функции журнала, имеется 6 уровней журнала: ///подробный, отладка, информация, предупреждение, ошибка, фатальный шаблонbq::enable_if_t ::value, bool> verbose(const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> verbose(const STR& log_format_content, const Args&... args) const; шаблон<имя типа STR> bq::enable_if_t ::value, bool> debug(const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> debug(const STR& log_format_content, const Args&... args) const; шаблон<имя типа STR> bq::enable_if_t ::value, bool> info(const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> info(const STR& log_format_content, const Args&... args) const; шаблон<имя типа STR> bq::enable_if_t ::value, bool> предупреждение (const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> предупреждение (const STR& log_format_content, const Args&... args) const; шаблон<имя типа STR> bq::enable_if_t ::value, bool> error(const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> error(const STR& log_format_content, const Args&... args) const; шаблон<имя типа STR> bq::enable_if_t ::value, bool> фатальный (const STR& log_content) const; шаблон<имя типа STR, имя типа...Args> bq::enable_if_t ::value, bool> Fatal(const STR& log_format_content, const Args&... args) const;
При логировании сообщений обратите внимание на три ключевых момента:
Как видите, наши журналы разделены на шесть уровней: подробный, отладочный, информационный, предупреждение, ошибка и фатальный, в соответствии с 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 по правилам, аналогичным std::format в C++20 (за исключением отсутствия поддержки позиционных аргументов и формата даты и времени). Например, использование одного {} представляет форматирование параметра по умолчанию, а {:.2f} определяет точность форматирования числа с плавающей запятой. Попробуйте использовать форматированные параметры для вывода журналов, а не объединять строки вручную. Этот подход оптимален для производительности и хранения в сжатом формате.
В настоящее время поддерживаются следующие типы параметров:
Нулевые указатели (выводятся как ноль)
Указатели (выводятся в виде шестнадцатеричного адреса, начиная с 0x)
логическое значение
Однобайтовые символы (char)
Двухбайтовые символы (char16_t, wchar_t, char C#, char Java)
Четырехбайтовые символы (char32_t или wchar_t)
8-битные целые числа
8-битные целые числа без знака
16-битные целые числа
16-битные целые числа без знака
32-битные целые числа
32-битные целые числа без знака
64-битные целые числа
64-битные целые числа без знака
32-битные числа с плавающей запятой
64-битные числа с плавающей запятой
Другие неизвестные типы POD в C++ (ограничены размерами 1, 2, 4 или 8 байт, рассматриваются как int8, int16, int32 и int64 соответственно)
Строки, включая все типы строк, упомянутые в параметре STR.
Любой класс или объект в C# и Java (вывод строки ToString())
Типы настраиваемых параметров, как описано в разделе «Типы настраиваемых параметров».
Существуют дополнительные часто используемые API, которые могут выполнять конкретные задачи. Подробные описания API см. в bq_log/bq_log.h, а также в классе bq.log в Java и C#. Вот некоторые ключевые API, которые необходимо выделить:
////// Деинициализируйте BqLog, пожалуйста, вызовите эту функцию до того, как ваша программа появится. /// static void uninit();
Рекомендуется выполнить uninit()
перед выходом из программы или удалением самореализуемой динамической библиотеки, использующей BqLog, в противном случае программа может зависнуть при выходе при определенных обстоятельствах.
////// Если bqLog является асинхронным, сбой в программе может привести к тому, что журналы в буфере не будут сохранены на диске. /// Если эта функция включена, bqLog попытается выполнить принудительную очистку журналов в буфере в случае сбоя. Однако /// эта функциональность не гарантирует успеха и поддерживает только системы POSIX. /// static void Enable_auto_crash_handle();
Подробное описание см. в разделе Защита данных при аварийном выходе из программы.
////// Синхронно очищаем буфер всех объектов журнала ///, чтобы гарантировать обработку всех данных в буфере после вызова. /// static void Force_flush_all_logs(); ////// Синхронно очистить буфер этого объекта журнала ///, чтобы гарантировать, что все данные в буфере будут обработаны после вызова. /// void Force_flush();
Поскольку bqLog по умолчанию использует асинхронное ведение журнала, бывают случаи, когда вам может потребоваться немедленно синхронизировать и вывести все журналы. В таких случаях вам необходимо принудительно вызвать Force_flush().
////// Зарегистрируйте обратный вызов, который будет вызываться всякий раз, когда выводится сообщение журнала консоли. /// Это можно использовать для внешней системы для мониторинга вывода журнала консоли. /// /// static void Register_console_callback(bq::type_func_ptr_console_callback callback); ////// Отменяем регистрацию обратного вызова консоли. /// /// static void unregister_console_callback(bq::type_func_ptr_console_callback callback);
Вывод ConsoleAppender поступает в консоль или журналы ADB Logcat на Android, но это может не охватывать все ситуации. Например, в пользовательских игровых движках или пользовательских IDE предусмотрен механизм вызова функции обратного вызова для каждого вывода журнала консоли. Это позволяет вам повторно обрабатывать и выводить журнал консоли в любом месте вашей программы.
Дополнительное предостережение: не выводите синхронизированные журналы BQ в обратном вызове консоли, поскольку это может легко привести к взаимоблокировкам.
////// Включение или отключение буфера добавления консоли. /// Поскольку наша оболочка может работать как на виртуальных машинах C#, так и на Java, и мы не хотим напрямую вызывать обратные вызовы из собственного потока, /// мы можем включить эту опцию. Таким образом, все выходные данные консоли будут сохраняться в буфере до тех пор, пока мы их не получим. /// /// ///static void set_console_buffer_enable(bool Enable); /// /// Извлекаем и удаляем запись журнала из буфера добавления консоли потокобезопасным способом. /// Если буфер добавления консоли не пуст, для этой записи журнала будет вызвана функция on_console_callback. /// Убедитесь, что в функции обратного вызова не выводятся синхронизированные журналы BQ. /// /// Функция обратного вызова, которая будет вызываться для полученной записи журнала, если буфер добавления консоли не пуст ///True, если Буфер добавления консоли не пуст, и извлекается запись журнала; в противном случае возвращается False. static bool fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback);
Помимо перехвата вывода консоли с помощью обратного вызова консоли, вы можете активно получать выходные данные журнала консоли. Иногда мы можем не захотеть, чтобы вывод журнала консоли проходил через обратный вызов, поскольку вы не знаете, из какого потока будет поступать обратный вызов (например, в некоторых виртуальных машинах C# или JVM виртуальная машина может выполнять сбор мусора, когда консоль вызывается обратный вызов, что потенциально может привести к зависаниям или сбоям).
Используемый здесь метод включает включение буфера консоли через set_console_buffer_enable
. Это приводит к тому, что каждый вывод журнала консоли сохраняется в памяти до тех пор, пока мы активно не вызовем fetch_and_remove_console_buffer
для его получения. Поэтому, если вы решите использовать этот метод, не забудьте своевременно получить и очистить журналы, чтобы избежать невысвобождения памяти.
Дополнительное предостережение: не выводите синхронизированные журналы BQ в обратном вызове консоли, поскольку это может легко привести к взаимоблокировкам.
Дополнительное предостережение: если вы используете этот код в среде IL2CPP, убедитесь, что on_console_callback помечен как статически небезопасный и украшен атрибутом [MonoPInvokeCallback(typeof(type_console_callback))].
////// Измените конфигурацию журнала, но некоторые поля, такие как buffer_size, изменить нельзя. /// /// ///bool reset_config(const bq::string& config_content);
Иногда вам может потребоваться изменить конфигурацию журнала в вашей программе. Помимо воссоздания объекта журнала для перезаписи конфигурации (см. «Создание объекта журнала»), вы также можете использовать интерфейс сброса. Однако обратите внимание, что не все элементы конфигурации можно изменить таким образом. Подробную информацию см. в Инструкции по настройке.
////// Временно отключите или включите определенный Appender. /// /// /// void set_appenders_enable(const bq::string& Appender_name, bool Enable) ;
По умолчанию Appenders в конфигурации активны, но здесь предусмотрен механизм их временного отключения и повторного включения.
////// Работает только тогда, когда настроен снимок. /// Он декодирует буфер снимка в текст. /// /// является ли временная метка каждого журнала временем GMT или местным временем ///буфер декодированных снимков bq::string take_snapshot (bool use_gmt_time) const;
Иногда некоторые специальные функции требуют вывода последней части журналов, что можно сделать с помощью функции моментального снимка. Чтобы включить эту функцию, сначала необходимо активировать снимок в конфигурации журнала и установить максимальный размер буфера в байтах. Кроме того, вам необходимо указать уровни и категории журналов, которые будут фильтроваться для моментального снимка (необязательно). Подробную настройку см. в разделе Конфигурация моментальных снимков. Если необходим снимок, вызов take_snapshot() вернет форматированную строку, содержащую самые последние записи журнала, хранящиеся в буфере снимков. В C++ типом является bq::string
, который можно неявно преобразовать в std::string
.
namespace bq{ namespace Tools { //Это служебный класс для декодирования форматов двоичных журналов. //Чтобы использовать его, сначала создайте объект log_decoder, //затем вызовите его функцию декодирования для декодирования. //После каждого успешного вызова //вы можете использовать get_last_decoded_log_entry() для получения декодированного результата. //Каждый вызов декодирует одну запись журнала. структура log_decoder { частный: bq::string decode_text_; bq::appender_decode_result result_ = bq::appender_decode_result::success; uint32_t handle_ = 0; public: ////// Создайте объект log_decoder, где каждый объект log_decoder соответствует двоичному файлу журнала. /// /// путь к двоичному файлу журнала, может быть относительным или абсолютным путем log_decoder(const bq::string& log_file_path); ~log_decoder(); ////// Декодируем запись журнала. каждый вызов этой функции будет декодировать только 1 запись журнала /// ///результат декодирования, Appender_decode_result::eof означает, что весь файл журнала был декодирован bq::appender_decode_result decode(); ////// получаем последний результат декодирования /// ///bq::appender_decode_result get_last_decode_result () const; /// /// получаем содержимое последней записи журнала декодирования /// ///const bq::string& get_last_decoded_log_entry() const; }; } }
Это служебный класс, который может декодировать файлы журналов, выводимые приложениями двоичного типа во время выполнения, такими как CompressedFileAppender и RawFileAppender.
Чтобы использовать его, сначала создайте объект log_decoder. Затем каждый раз, когда вы вызываете функцию decode(), она последовательно декодирует одну запись журнала. Если возвращенный результат — bq::appender_decode_result::success, вы можете вызвать get_last_decoded_log_entry(), чтобы получить форматированное текстовое содержимое последней декодированной записи журнала. Если результат — bq::appender_decode_result::eof, это означает, что все журналы прочитаны полностью.
BqLog позволяет вам настроить, является ли объект журнала синхронным или асинхронным, с помощью параметра thread_mode. Основные различия между этими двумя режимами заключаются в следующем:
Синхронное ведение журнала | Асинхронное ведение журнала | |
---|---|---|
Поведение | После вызова функции журналирования журнал немедленно записывается в соответствующее приложение. | После вызова функции журналирования журнал не записывается сразу; вместо этого он передается рабочему потоку для периодической обработки. |
Производительность | Низкий, поскольку поток, записывающий журнал, должен заблокироваться и дождаться записи журнала в соответствующее приложение, прежде чем вернуться из функции регистрации. | Высокий, поскольку потоку, записывающему журнал, не нужно ждать фактического вывода, и он может вернуться сразу после регистрации. |
Безопасность потоков | Высокий, но требует, чтобы параметры журнала не изменялись во время выполнения функции журналирования. | Высокий, но требует, чтобы параметры журнала не изменялись во время выполнения функции журналирования. |
Распространенное заблуждение об асинхронном журналировании заключается в том, что оно менее потокобезопасно, поскольку пользователи обеспокоены тем, что параметры могут быть восстановлены к моменту обработки журнала рабочим потоком. Например:
{ 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 копирует все содержимое параметров в свой внутренний ring_buffer
во время выполнения info
функции. После возврата функции info
внешние переменные, такие как str_array
или str_ptr
больше не нужны. Более того, ring_buffer
не будет хранить адрес указателя const char*
, но всегда будет хранить всю строку.
Реальная потенциальная проблема возникает в следующем сценарии:
static std::string global_str = «Привет, мир»; // Это глобальная переменная, изменяемая несколькими потоками.void thread_a() { log_obj.info("Это тестовый параметр: {}", global_str); }
Если содержимое global_str
изменится во время выполнения info
функции, это может привести к неопределенному поведению. BqLog сделает все возможное, чтобы предотвратить сбой, но правильность конечного результата не может быть гарантирована.
Appender представляет собой цель вывода журнала. Концепция Appenders в bqLog в основном такая же, как и в Log4j. В настоящее время bqLog предоставляет следующие типы приложений:
Целью вывода этого Appender является консоль, включая ADB Android и соответствующую консоль iOS. Кодировка текста — UTF-8.
Этот Appender выводит файлы журналов непосредственно в текстовом формате UTF-8.
Этот Appender выводит файлы журналов в сжатом формате, который highly recommended format by bqLog
. Он имеет самую высокую производительность среди всех приложений и создает наименьший выходной файл. Однако окончательный файл необходимо декодировать. Декодирование может выполняться во время декодирования или автономного декодирования.
Этот Appender выводит содержимое двоичного журнала из памяти непосредственно в файл. Его производительность выше, чем у TextFileAppender, но он потребляет больше места для хранения. Окончательный файл необходимо декодировать. Декодирование может выполняться во время декодирования или автономного декодирования. Использование этого приложения не рекомендуется.
Ниже приведено подробное сравнение различных Appenders:
Имя | Выходная цель | Непосредственно читаемый | Выходная производительность | Выходной размер |
---|---|---|---|---|
КонсольAppender | Консоль | ✔ | Низкий | - |
ТекстФайлАппендер | Файл | ✔ | Низкий | Большой |
СжатыйФайлAppender | Файл | ✘ | Высокий | Маленький |
RawFileAppender | Файл | ✘ | Середина | Большой |
Конфигурация относится к строке конфигурации в функциях create_log и reset_config. Эта строка использует формат файла свойств и поддерживает # комментарии (но не забудьте начать новую строку с # для комментариев).
Ниже приведен полный пример:
# Эта конфигурация устанавливает объект журнала с 5 Appenders, включая два TextFileAppenders, которые выводят данные в два разных файла. # Первый Appender называется Appender_0 и его тип — ConsoleAppenderappenders_config.appender_0.type=console# Часовой пояс для Appender_0: локальное время системыappenders_config.appender_0.time_zone=default local time# Appender_0 выведет все 6 уровней журналов (примечание: между уровнями журналов не должно быть пробелов, иначе анализ не будет выполнен)appenders_config.appender_0.levels=[verbose,debug ,info,warning,error,fatal]# Второй Appender называется Appender_1 и его тип — TextFileAppenderappenders_config.appender_1.type=text_file# Часовой пояс для Appender_1 — GMT, то есть UTC+0appenders_config.appender_1.time_zone=gmt# только Appender_1 выводит журналы с информацией об уровне и выше, другие будут игнорироватьсяappenders_config.appender_1.levels=[info,warning,error,fatal]# Путь для Appender_1 будет находиться в относительном каталоге bqLog программы, имена файлов начинаются с нормального, за которым следует дата и расширение .log# В iOS они будут сохранены в /var/mobile/Containers/Data/Application/[APP]/Library/Caches/bqLog# В Android они будут сохранены в [android.content.Context .getExternalFilesDir()]/bqLogappenders_config.appender_1.file_name=bqLog/normal# Максимальный размер файла — 10 000 000 байт; если превышено, будет создан новый файлappenders_config.appender_1.max_file_size=10000000# Файлы старше десяти дней будут очищены upappenders_config.appender_1.expire_time_days=10# Если общий размер вывода превышает 100 000 000 байт, файлы будут очищены, начиная с oldappenders_config.appender_1.capacity_limit=100000000# Третий Appender называется Appender_2 и его тип TextFileAppenderappenders_config.appender_2.type=text_file# Appender_2 будет выводить все уровни logsappenders_config.appender_2.levels=[all]# Путь для Appender_2 будет в относительный каталог bqLog программы, имена файлов начинаются с new_normal, за которым следует дата и расширение .logappenders_config.appender_2.file_name=bqLog/new_normal#. Этот параметр действует только на Android, сохраняя журналы во внутреннем хранилище, то есть [android .content.Context.getFilesDir()]/bqLogappenders_config.appender_2.is_in_sandbox=true# Четвертое приложение называется Appender_3 и имеет тип CompressedFileAppenderappenders_config.appender_3.type=compressed_file# Appender_3 будет выводить все уровни logsappenders_config.appender_3.levels=[all ]# Путь к Appender_3 будет находиться в каталоге программы ~/bqLog с абсолютным путем, имена файлов начинаются с compress_log, за которым следует дата и расширение .logcomprappenders_config.appender_3.file_name=~/bqLog/compress_log# Имя пятого Appender Appender_4 и его тип — RawFileAppenderappenders_config.appender_4.type=raw_file# Appender_4 отключен по умолчанию и может быть включен позже с помощью set_appenders_enableappenders_config.appender_4.enable=false# Appender_4 выведет все уровни logsappenders_config.appender_4.levels=[all]# Путь для Appender_4 будет находиться в относительном каталоге bqLog программы, с именами файлов, начинающимися с raw_log, за которыми следует дата и расширение .lograwappenders_config.appender_4.file_name=bqLog/raw_log# Журналы будут обрабатываться, только если их категория начинается с ModuleA, ModuleB. SystemC, в противном случае все будет игнорироваться (концепция категории подробно объясняется в темах расширенного использования позже)appenders_config.appender_4.categories_mask=[ModuleA,ModuleB.SystemC]# Общий размер асинхронного буфера составляет 65535 байт; конкретное значение будет объяснено позже.log.buffer_size=65535# Уровень надежности журнала нормальный; конкретное значение будет объяснено позже.log.reliable_level=normal# Журналы будут обрабатываться только в том случае, если их категория соответствует следующим трем подстановочным знакам, в противном случае все они будут игнорироваться (концепция категории подробно объясняется в темах расширенного использования позже)log.categories_mask= [*default,ModuleA,ModuleB.SystemC]# Это асинхронный журнал; асинхронные журналы являются наиболее производительными и рекомендуемыми. typelog.thread_mode=async# Если уровень журнала является ошибкой или фатальным, включите информацию о стеке вызовов в каждый журнал. .buffer_size=65536# В снимок будут записаны только журналы с информацией и уровнями ошибок.snapshot.levels=[info,error]# В снимок будут записаны только журналы, категория которых начинается с ModuleA, ModuleB.SystemC, в противном случае они будут игнорироваться. .categories_mask=[МодульA.SystemA.КлассA,МодульB]
appenders_config
— это набор конфигураций Appenders. Первый параметр, следующий за appenders_config
— это имя приложения, и все приложения с таким же именем имеют одну и ту же конфигурацию.
Имя | Необходимый | Настраиваемые значения | По умолчанию | Применимо к ConsoleAppender | Применимо к TextFileAppender | Применимо к CompressedFileAppender | Применимо к RawFileAppender |
---|---|---|---|---|---|---|---|
тип | ✔ | консоль, текстовый_файл, сжатый_файл, необработанный_файл | ✔ | ✔ | ✔ | ✔ | |
давать возможность | ✘ | Включен ли Appender по умолчанию | истинный | ✔ | ✔ | ✔ | ✔ |
уровни | ✘ | Массив уровней журнала | [все] | ✔ | ✔ | ✔ | ✔ |
часовой_зона | ✘ | GMT или любая другая строка | Местное время | ✔ | ✔ | ✔ | ✔ |
имя файла | ✔ | Относительный или абсолютный путь | ✘ | ✔ | ✔ | ✔ | |
is_in_sandbox | ✘ | правда, ложь | ЛОЖЬ | ✘ | ✔ | ✔ | ✔ |
max_file_size | ✘ | Положительное целое число или 0 | 0 | ✘ | ✔ | ✔ | ✔ |
expire_time_days | ✘ | Положительное целое число или 0 | 0 | ✘ | ✔ | ✔ | ✔ |
емкость_предела | ✘ | Положительное целое число или 0 | 0 | ✘ | ✔ | ✔ | ✔ |
категории_маска | ✘ | Массив строк, заключенный в [] | Пустой | ✔ | ✔ | ✔ | ✔ |
Указывает тип Appender.
console
: представляет ConsoleAppender.
text_file
: представляет TextFileAppender.
compressed_file
: представляет CompressedFileAppender.
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 (android.content.Context.getCacheDir()).
false
: файлы по умолчанию хранятся в каталоге внешнего хранилища. Если они недоступны, они хранятся в каталоге Internal Storage. Если это также недоступно, они хранятся в каталоге Cache.
Максимальный размер файла в байтах. Когда размер сохраненного файла превышает этот размер, создается новый файл журнала, в котором номера файлов последовательно увеличиваются. 0
отключает эту функцию.
Максимальное количество дней хранения файлов. Файлы старше этого срока будут автоматически удалены. 0
отключает эту функцию.
Максимальный общий размер файлов, выводимых этим Appender в выходной каталог. Если этот предел превышен, файлы удаляются, начиная с самых старых, пока общий размер не окажется в пределах лимита. 0
отключает эту функцию.
Если объект журнала является объектом журнала, поддерживающим категории, его можно использовать для фильтрации древовидного списка категорий. Когда массив не пуст, эта функция активна. Например, [*default,ModuleA,ModuleB.SystemC]
означает, что журналы имеют категорию по умолчанию.