G3log 入门

2021/1/19 toolslog

G3log 是一个异步的“崩溃安全”记录器。且可以选择使用默认的日志接收器(将所有 LOG 调用保存到文件中1,或者使用自定义的日志接收器,或同时使用这两种接收器,或使用所需数量的接收器。

# G3Log

  • G3log 是 g2log 第三代的名称,代表 带有动态接收器的 g3log
  • G3log 是一个异步的“崩溃安全”日志系统。更多关于内容在此 g2log version (opens new window)
  • 可以选择使用默认的日志接收器(将所有 LOG 调用保存到文件中),或者使用自定义的日志接收器,或同时使用这两种接收器,或使用所需数量的接收器。

# 可选使用流式或类似 printf 的语法

LOG(INFO) << "streaming API is as easy as ABC or " << 123;

LOGF(WARNING, "Printf-style syntax is also %s", "available");
1
2
3

# 条件记录

int less = 1; int more = 2

LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";

// 或使用类似 printf 的语法
LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
1
2
3
4
5
6

# 约定的设计

CHECK(false) will trigger a "fatal" message. It will be logged, and then the

application will exit.

int less = 1; int more = 2

CHECK(less != more); // not FATAL
CHECK(less > more) << "CHECK(false) triggers a FATAL message";
1
2
3
4

# 详细的API文档

请查看API.markdown (opens new window)以获得详细的API文档

# 优势

  1. 易于使用,简洁语法和快速的日志器。

  2. 所有日志的慢速 I/O 磁盘访问都在后台线程中完成。这样可以确保调用程序可以立即继续执行其他任务,而不必等待 LOG 调用完成。

  3. G3log 提供日志记录,按 约定的设计记录以及在关闭时将日志刷新到文件。

  4. 线程安全,因此可以多线程使用。

  5. 崩溃安全。它将在关闭之前将生成的日志保存到接收器。日志器将捕获某些致命事件 (Linux/OSX: signals,Windows: 致命 OS 异常和信号),因此,如果程序由于分段错误 SIGSEGV 而崩溃,它将记录并保存崩溃和退出之前所有的缓存日志。

  6. 跨平台。OSX,Windows,Ubuntu,CentOS 均有客户端测试和使用

  7. G3log 和 G2log 在全球范围内用于商业产品以及业余项目。

  8. 源代码在公共领域免费提供的。可以选择更改,使用和任何操作,而无需附加任何声明。

  9. 存在两个版本的 g3log。

    • g3log (opens new window):旨在方便添加自定义接收器。它至少在以下 Linux(Clang/gcc),Windows(mingw,visual studio 2013) 的平台上进行了测试。如果具有完整的 C++14 支持(最新版本的 C++11 支持截止于:v1.3.1),建议使用 g3log。
    • g2log (opens new window): 最原始的版本。简单,易于修改,并获得最多的 OS 支持。在 OSX/Clang,Ubuntu,CentOS,Windows/mingw,Windows/Visual Studio 等环境中均有使用。g2log 的重点是“稳步发展”和编译器支持。g3log 中只有经过时间检验的功能才会被其纳入 g2log。目前没有积极的开发或支持 g2log,但是如果您需要帮助,请随时向我提问。

# 接收器

接收器 (opens new window) 是 LOG 调用的接收器。G3log 带有默认的接收器 (与 G3log 使用的相同),可用于将日志保存到文件。接收器可以是任何类型的类,没有限制,只要它可以以 std::string g3::LogMessageMover 形式接收 LOG 消息即可。

std::string 已预先格式化。g3::LogMessageMover 是一个包装的结构,包含用在自定义接收器中自定义处理的原始数据。

G3log 的接收器,会被添加到使用 std :unique_ptr 的日志器。可以通过 handler 使用公开的 API 将调用异步转发到接收器。

创建自定义接收器非常简单。下方示例说明了使用自定义日志格式来制作自定义接收器所需要的内容,但仅用于将颜色添加到默认日志格式中。接收器将彩色日志转发到 cout

// 在 Customsink.hpp 文件中
#pragma once
#include <string>
#include <iostream>
#include <g3log/logmessage.hpp>

struct CustomSink {

// Linux xterm color
// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
  enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};

  FG_Color GetColor(const LEVELS level) const {
     if (level.value == WARNING.value) { return YELLOW; }
     if (level.value == DEBUG.value) { return GREEN; }
     if (g3::internal::wasFatal(level)) { return RED; }

     return WHITE;
  }

  void ReceiveLogMessage(g3::LogMessageMover logEntry) {
     auto level = logEntry.get()._level;
     auto color = GetColor(level);

     std::cout << "\033[" << color << "m"
       << logEntry.get().toString() << "\033[m" << std::endl;
  }
};

// 在 main.cpp 中,main() 函数

auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
                                     &CustomSink::ReceiveLogMessage);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 添加和删除接收器

在程序运行期间可以安全地删除和添加接收器。

记住

  • 记录器的初始化应在启动可能调用记录器的任何其他线程之前进行。
  • 记录器(RAII 概念)的破坏应在调用记录器的其他线程关闭之后发生。

添加接收器

   auto sinkHandle1 = logworker->addSink(std::make_unique<CustomSink>(),
                                         &CustomSink::ReceiveLogMessage);
   auto sinkHandle2 = logworker->addDefaultLogger(argv[0],
                                                  path_to_log_file);
   logworker->removeSink(std::move(sinkHandle1)); // 以线程安全的方式删除 sinkHandle1
   logworker->removeAllSinks(); // 以线程安全的方式删除所有接收器
1
2
3
4
5
6

更多水槽 可以在 github.com/KjellKod/g3sinks (opens new window) 中找到。

# 代码范例

Example usage where a custom sink is added. A function is called though the sink handler to the actual sink object. 添加自定义接收器的示例用法。通过接收器调用实际接收对象的一个函数。

// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>

#include "CustomSink.h"

int main(int argc, char**argv) {
   using namespace g3;
   std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
   auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
                                          &CustomSink::ReceiveLogMessage);

   // 在可以接收 LOG 调用之前初始化记录器
   initializeLogging(logworker.get());
   LOG(WARNING) << "This log call, may or may not happend before"
                << "the sinkHandle->call below";

   // 您可以以线程安全的方式在接收器上调用函数
   // 该调用在自定义接收器上异步执行。
   std::future<void> received = sinkHandle->call(&CustomSink::Foo,
                                                 param1, param2);

   // 如果 LogWorker 已初始化,则在范围退出处将调用 g3::internal::shutDownLogging()。
   // 这很重要,因为它可以防止来自静态实体或其他实体的 LOG 调用(这些实体稍后会超出范围)。
   //
   // 也可以手动调用它:
   g3::internal::shutDownLogging();
}

// some_file.cpp : 展示使记录器在软件的其他部分中工作是多么容易

#include <g3log/g3log.hpp>

void SomeFunction() {
   ...
   LOG(INFO) << "Hello World";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

使用默认文件记录器并添加自定义接收器的示例用法

// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>

#include "CustomSink.h"

int main(int argc, char**argv) {
   using namespace g3;
   auto worker = LogWorker::createLogWorker();
   auto defaultHandler = worker->addDefaultLogger(argv[0],
                                                  path_to_log_file);

   // logger is initialized
   g3::initializeLogging(worker.get());

   LOG(DEBUG) << "Make log call, then add another sink";

   worker->addSink(std::make_unique<CustomSink>(),
                                  &CustomSink::ReceiveLogMessage);

   ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 构建 G3log

git clone https://github.com/KjellKod/g3log
cd g3log
mkdir build
cd build
1
2
3
4

# 先决条件

假设已经安装了 C++14 编译器,那么还需要以下工具来从源代码构建 g3log:

  • CMake (必要)
  • Git (可选,建议)

在构建 g3log 时,它使用 git 从该存储库的提交历史记录中计算软件版本。如果您不希望这样做,或者您的设置无法访问 git,或者从 GitHub Releases 页面下载 g3log 源归档文件,从而不必下载提交历史记录,则可以将版本作为 CMake 构建参数。 有关更多信息,请参见此 issue (opens new window)

cmake -DVERSION=1.3.2 ..
1

# 配置选项

g3log 提供以下 CMake 选项(以及默认值):

$ cmake -LAH # 列出非高级的缓存变量。有关更多详细信息,请参见 `cmake --help`。

...

// Fatal (fatal-crashes/contract) examples
ADD_FATAL_EXAMPLE:BOOL=ON

// g3log 性能测试
ADD_G3LOG_BENCH_PERFORMANCE:BOOL=OFF

// g3log 单元测试
ADD_G3LOG_UNIT_TEST:BOOL=OFF

// 使用 DBUG 日志记录级别而不是 DEBUG。
// 默认情况下,DEBUG 是调试级别
CHANGE_G3LOG_DEBUG_TO_DBUG:BOOL=OFF

// 指定单一配置生成器上的构建类型。
// 可能的值为空,Debug,Release,RelWithDebInfo,MinSizeRel 等。
CMAKE_BUILD_TYPE:STRING=

// 安装路径前缀,位于安装目录之前。
// 在 UNIX 上,此变量默认为 /usr/local
// 在 Windows 上 c:/Program Files/${PROJECT_NAME}
CMAKE_INSTALL_PREFIX:PATH=

// 构建包中使用的前缀。
// 在 Linux 上,如果未设置此选项:
// 1) 如果给出了 CMAKE_INSTALL_PREFIX,则 g3log 将其设置为 CMAKE_INSTALL_PREFIX 的值。
// 2) 否则,它将被 g3log 设置为 /usr/local。
CPACK_PACKAGING_INSTALL_PREFIX:PATH=

// 收到致命异常时启用 Visual Studio 断点。
// 仅在__DEBUG 模式下
DEBUG_BREAK_AT_FATAL_SIGNAL:BOOL=OFF

// 具有改进的堆栈跟踪的矢量异常/崩溃处理
ENABLE_FATAL_SIGNALHANDLING:BOOL=ON

// 具有改进的堆栈跟踪的矢量异常/崩溃处理
ENABLE_VECTORED_EXCEPTIONHANDLING:BOOL=ON

// iOS 版本库。
G3_IOS_LIB:BOOL=OFF

// 记录完整文件名
G3_LOG_FULL_FILENAME:BOOL=OFF

// 建立共享库
G3_SHARED_LIB:BOOL=ON

// 建立共享运行时库 MSVC
G3_SHARED_RUNTIME:BOOL=ON

// 打开/关闭日志级别。
// 禁用的级别不会将该级别的日志推送到接收器。
// 默认情况下,动态日志记录处于禁用状态
USE_DYNAMIC_LOGGING_LEVELS:BOOL=OFF

// 在日志捕获期间将动态内存用于消息缓冲区
USE_G3_DYNAMIC_MAX_MESSAGE_SIZE:BOOL=OFF

...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

有关其他选项上下文和注释,请参见 Options.cmake (opens new window)

如果您想默认设置一切,则应该:

cmake ..
1

Linux/OSX 软件包的维护者可能对 CPACK_PACKAGING_INSTALL_PREFIX 感兴趣。例如:

cmake .. -DCPACK_PACKAGING_INSTALL_PREFIX=/usr/local
1

# 生成命令

配置完成后,您可以使用以下命令构建 g3log:

# 假设您仍在 build 目录中。 我不再重复了!
cmake --build . --config Release
1
2

# 安装

从源代码以 CMake 方式安装:

cmake --build . --target install
1

# 测试

默认情况下,不会构建测试。要启用单元测试,应该设置 ADD_G3LOG_UNIT_TEST.

假设构建过程已完成,那么可以使用以下命令运行测试:

ctest -C Release
或者
make test
1
2
3

适用于 Linux 用户。

或获取所有测试的详细 gtest 输出:

cd build;
../scripts/runAllTests.sh
1
2

# CMake 模块

g3log 带有 CMake 模块。安装后,可以在 ${CMAKE_INSTALL_PREFIX}/lib/cmake/g3log 下找到它。用户可以通过以下方式在基于 CMake 的项目中使用 g3log:

find_package(g3log CONFIG REQUIRED)
target_link_libraries(main PRIVATE g3log)
1
2

为了确保 CMake 可以找到 g3log,您还需要告诉 CMake 在哪里搜索它:

cmake .. -DCMAKE_PREFIX_PATH=<g3log's install prefix>
1

# API 概述

上文中中描述了使用 g3log 所需的大多数 API。有关更多 API 文档和示例,请继续阅读 API 自述文件 (opens new window)。 将找到的示例是:

  • Sink creation and utilization
  • Logging levels
    • disable/enabled levels at runtime
    • custom logging levels
  • Fatal handling
    • custom fatal handling
    • pre fatal hook
    • override of signal handling
    • disable fatal handling
  • LOG calls
  • CHECK calls

# 性能

G3log 的目标是使所有后台日志记录到接收器,并尽可能减少日志接收器的日志开销,并尽可能减小“最坏情况延迟”。因此,对于许多处理关键任务的系统来说,g3log 是一个很好的记录器。由于平台不同,平均而言日志记录的开销将有所不同。在我的 2010 笔记本电脑上,进行极限性能测试时,平均调用时间约为 2 us。

即使出现突然的极端压力,最坏情况下的延迟也保持稳定,没有极端的峰值。有一篇博客文章,关于比较 g3log 和可能感兴趣的其他记录器的最坏情况延迟。可以在这里找到它: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/

Last Updated: 2023-10-29T08:26:04.000Z