当前位置: 首页 > 工具软件 > Google Glog > 使用案例 >

google glog使用指南

益思博
2023-12-01

Google Logging (glog) 是一个 C++98 库,用于实现应用程序级日志记录。该库提供了基于 C++ 风格流和各种帮助宏的日志 API。

入门

您可以通过简单地将内容流式传输到LOG(严重性级别)来记录消息,例如,

#include <glog/logging.h>

int main(int argc, char* argv[]) {
    // Initialize Google’s logging library.
    google::InitGoogleLogging(argv[0]);

    // ...
    LOG(INFO) << "Found " << num_cookies << " cookies";
}

从源码构建

  1. 获取源代码并切换到代码目录
    git clone https://github.com/google/glog.git
    cd glog
    
  2. 运行 CMake 以配置构建树。
    cmake -S . -B build -G "Unix Makefiles"
    
  3. 之后,生成的文件可用于编译项目
    cmake --build build
    
  4. 测试构建软件(可选)
    cmake --build build --target test
    
  5. 安装构建的文件(可选)
    cmake --build build --target install
    

在 CMake 项目中使用 glog

如果您的系统中安装了 glog,您可以使用 CMake 命令 find_package在您的 CMake 项目中针对 glog 进行构建,如下所示:

cmake_minimum_required (VERSION 3.0.2)
project (myproj VERSION 1.0)

find_package (glog 0.5.0 REQUIRED)

add_executable (myapp main.cpp)
target_link_libraries (myapp glog::glog)

编译定义和选项将根据需要自动添加到您的目标程序中。

用户指南

glog 定义了一系列宏来简化许多常见的日志任务。您可以按严重性级别记录日志消息、从命令行控制日志记录行为、基于条件记录日志、在不满足预期条件时中止程序、引入您自己的详细的日志记录级别、自定义附加到日志消息的前缀等。

以下部分描述了 glog 支持的功能。

严重性级别

您可以指定以下的严重性级别(严重程度的增加顺序): INFOWARNINGERRORFATAL。记录FATAL消息会终止程序(在记录消息之后)。请注意,给定严重性级别的消息不仅会记录在该严重性级别对应的日志文件中,还会记录在所有较低严重性级别的日志文件中。例如,严重性级别为的FATAL的消息将被记录到严重性级别为FATALERRORWARNINGINFO的日志文件中。

DFATAL严重性级别记录了一个FATAL错误在调试模式下,但通过自动降低其严重性级别为ERROR在生产环境中暂停程序。

除非另有说明,glog 会写入文件/tmp/\<program name\>.\<hostname\>.\<user name\>.log.\<severity level\>.\<date\>.\<time\>.\<pid\> (例如, /tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474)。默认情况下,除了日志文件之外,glog 还会复制严重级别为ERRORFATAL的日志消息到标准错误 (stderr)。

设置标志

几个标志会影响 glog 的输出行为。如果您的机器上安装了Google gflags 库,构建系统将自动检测并使用它,允许您在命令行上传递标志。例如,如果您想打开标志--logtostderr,您可以使用以下命令行启动您的应用程序:

./your_application --logtostderr=1

如果未安装 Google gflags 库,您可以通过环境变量设置标志,在标志名称前加上前缀GLOG_,例如,

GLOG_logtostderr=1 ./your_application

以下标志是最常用的:

  • logtostderr( bool, 默认= false)
    将消息记录到stderr而不是日志文件。注意:您可以通过指定1true、 或yes (不区分大小写)来设置二进制标志为true。此外,您可以通过指定0, false, 或no(同样不区分大小写)来设置二进制标志为false
  • stderrthreshold( int, 默认=2, 即ERROR)
    除了日志文件之外,将处于或高于此级别的日志消息复制到stderr。严重性等级INFOWARNINGERRORFATAL对应的数字分别为 0123
  • minloglevel( int, 默认=0, 即INFO)
    记录处于或高于此级别的消息。同样,严重性等级INFOWARNINGERRORFATAL对应的数字分别为 0123
  • log_dirstring,默认=“”)
    如果指定,日志文件将写入此目录而不是默认日志目录。
  • v( int, 默认=0)
    显示小于或等于此标志值的所有VLOG(m)消息m。由--vmodule重载。
  • vmodulestring,默认=“”)
    每个模块的详细级别。该参数必须包含以逗号分隔的 <module name>=<log level>列表。<module name>是一个glob 模式(例如,gfs*对于名称以“gfs”开头的所有模块),与文件名基础匹配(即忽略 .cc/.h./-inl.h的名称)。<log level> 覆盖由--v给出的任何值。

您还可以通过修改全局变量来修改程序中的标志值FLAGS_*。大多数设置在您更新FLAGS_*后立即开始工作。与目标文件相关的标志是例外。例如,您可能需要在调用google::InitGoogleLogging之前设置FLAGS_log_dir。下面是一个例子:

LOG(INFO) << "file";
// Most flags work immediately after updating values.
FLAGS_logtostderr = 1;
LOG(INFO) << "stderr";
FLAGS_logtostderr = 0;
// This won’t change the log destination. If you want to set this
// value, you should do this before google::InitGoogleLogging .
FLAGS_log_dir = "/some/log/directory";
LOG(INFO) << "the same file";

条件/偶尔记录

有时,您可能只想在特定条件下记录消息。您可以使用以下宏来执行条件日志记录:

LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";

仅当变量num_cookies超过10时才会记录“Got lot of cookies”消息 。如果一行代码被多次执行,则在特定时间间隔内记录一条消息可能会很有用。这种日志记录对于信息性消息最有用。

LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";

上面的行在第 1、11、21、… 次执行时输出日志消息。请注意,google::COUNTER的值用于标识正在发生的重复。

您可以使用以下宏结合将条件记录和偶尔记录使用。

LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
                                        << "th big cookie";

除了每 n 次输出一条消息,您还可以将输出限制只在前 n 次出现时输出:

LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";

输出执行前20次的日志消息。同样, google::COUNTER的值用于标识正在发生的重复。

其他时候,只希望根据时间定期记录消息。例如,每 10 毫秒记录一条消息:

LOG_EVERY_T(INFO, 0.01) << "Got a cookie";

或者每 2.35 秒记录一条消息:

LOG_EVERY_T(INFO, 2.35) << "Got a cookie";

调试模式支持

特殊的“调试模式”日志宏仅在调试模式下有效,并且在非调试模式编译时被编译为空。使用这些宏可以避免由于过多的日志记录而减慢应用程序的速度。

DLOG(INFO) << "Found cookies";
DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";

CHECK

经常检查程序中的预期条件以尽早发现错误是一种很好的做法。该CHECK宏提供了在不满足条件时中止应用程序的能力,类似于标准 C 库中定义的assert宏。

如果条件不成立,CHECK将会中止应用程序。与assert不同,它不受NDEBUG的控制,因此无论编译模式如何,都将执行检查。因此,fp->Write(x)在以下示例中始终执行:

CHECK(fp->Write(x) == 4) << "Write failed!";

有检查各种相等/不相等的辅助宏- CHECK_EQCHECK_NECHECK_LECHECK_LTCHECK_GECHECK_GT。它们比较两个值,并在结果不符合预期时记录包含这两个值的FATAL消息。这些值必须已经被operator<<(ostream, ...)定义。

您可以像这样附加到错误消息:

CHECK_NE(1, 2) << ": The world must be ending!";

我们非常小心地确保每个参数只计算一次,并且任何合法的可以作为函数参数传递的东西在这里都是合法的。特别是,参数可能是临时表达式,最终会在语句的末尾被销毁,例如:

CHECK_EQ(string("abc")[1], ’b’);

如果参数之一是指针而另一个是NULL ,则编译器会报告错误。要解决此问题,只需static_cast NULL到所需要的指针类型即可。

CHECK_EQ(some_ptr, static_cast<SomeType*>(NULL));

更好的是,使用CHECK_NOTNULL宏:

CHECK_NOTNULL(some_ptr);
some_ptr->DoSomething();

由于这个宏返回给定的指针,这在构造函数初始化项列表中非常有用。

struct S {
    S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
    Something* ptr_;
};

请注意,由于此功能,您不能将此宏用作 C++ 流。请使用上述提到的CHECK_EQ在宏在中止应用程序之前记录消息。

如果您比较C字符串(char *),一组实用宏执行大小写敏感以及大小写不敏感的比较- ,CHECK_STREQCHECK_STRNECHECK_STRCASEEQCHECK_STRCASENECASE版本不区分大小写。您可以安全地为这些宏传递NULL指针。他们将NULL和任何非NULL字符串视为不相等。两个NULL字符串是相等的。

请注意,这两个参数都可能是临时字符串,它们在当前“完整表达式”(例如,CHECK_STREQ(Foo().c_str(), Bar().c_str())这里FooBar 返回 C++的 std::string)的末尾被销毁 。

CHECK_DOUBLE_EQ宏检查检查两个浮点值的相等,接受一个小的误差裕度。CHECK_NEAR接受第三个浮点参数,它指定可接受的误差范围。

详细记录

当您在寻找困难的错误时,完整的日志消息非常有用。但是,您可能希望在通常的开发中忽略过于冗长的消息。对于这种详细的日志记录,glog 提供了VLOG宏,它允许您定义自己的日志记录级别。在--v命令行选项控制哪些等级的日志被记录:

VLOG(1) << "I’m printed when you run the program with --v=1 or higher";
VLOG(2) << "I’m printed when you run the program with --v=2 or higher";

使用VLOG,详细级别越低,记录消息的可能性就越大。例如,如果--v==1,VLOG(1)将被记录,但VLOG(2)不会被记录。这与严重性级别相反,其中INFO是 0, ERROR是 2。--minloglevel = 1将记录WARNING及以上的严重性级别。尽管您可以为VLOG宏和--v标志指定任何整数,但通常它们的值是小的正整数。例如,如果您编写 VLOG(0),则应指定--v=-1或更低以使其静音。这不太有用,因为在大多数情况下我们可能不需要默认的详细日志。该 VLOG宏总是在INFO日志级别进行记录(当他们记录的话)。

可以从命令行对每一个模块的详细记录进行控制:

--vmodule=mapreduce=2,file=1,gfs*=3 --v=0

这将会:

  • 从mapreduce.{h,cc}打印VLOG(2)和更低等级的消息
  • 从 file.{h,cc}打印VLOG(1)和更低等级的消息
  • 从以“gfs”为前缀的文件打印VLOG(3)和更低等级的消息
  • 从剩余的地方打印VLOG(0)和更低等级的消息

所示的通配符功能支持 ‘*’(匹配 0 个或多个字符)和 ‘?’ (匹配任何单个字符)。

还有VLOG_IS_ON(n)“详细级别”条件宏。当--v等于或大于n时,此宏返回真。用作

if (VLOG_IS_ON(2)) {
    // do some logging preparation and logging
    // that can’t be accomplished with just VLOG(2) << ...;
}

详细级别条件宏VLOG_IFVLOG_EVERY_NVLOG_IF_EVERY_N行为类似于LOG_IF,LOG_EVERY_N,LOF_IF_EVERY,但接受代表详细级别的数字而不是严重性级别。

VLOG_IF(1, (size > 1024))
   << "I’m printed when size is more than 1024 and when you run the "
      "program with --v=1 or more";
VLOG_EVERY_N(1, 10)
   << "I’m printed every 10th occurrence, and when you run the program "
      "with --v=1 or more. Present occurence is " << google::COUNTER;
VLOG_IF_EVERY_N(1, (size > 1024), 10)
   << "I’m printed on every 10th occurence of case when size is more "
      " than 1024, when you run the program with --v=1 or more. ";
      "Present occurence is " << google::COUNTER;

自定义日志前缀格式

glog 支持通过接收用户提供的用于生成此类字符串的回调来更改附加到日志消息的前缀的格式。该功能必须在编译时由WITH_CUSTOM_PREFIX标志启用。

对于每个日志条目,将使用包含严重性、文件名、行号、线程 ID 和事件时间的LogMessageInfo结构调用回调。它还将获得对输出流的引用,其内容将被添加到最终日志行中的实际消息之前。

例如:

/* This function writes a prefix that matches glog's default format.
 * (The third parameter can be used to receive user-supplied data, and is
 * NULL by default.)
 */
void CustomPrefix(std::ostream &s, const LogMessageInfo &l, void*) {
   s << l.severity[0]
   << setw(4) << 1900 + l.time.year()
   << setw(2) << 1 + l.time.month()
   << setw(2) << l.time.day()
   << ' '
   << setw(2) << l.time.hour() << ':'
   << setw(2) << l.time.min()  << ':'
   << setw(2) << l.time.sec() << "."
   << setw(6) << l.time.usec()
   << ' '
   << setfill(' ') << setw(5)
   << l.thread_id << setfill('0')
   << ' '
   << l.filename << ':' << l.line_number << "]";
}

为了能够使用CustomPrefix(),在记录初始化过程中传入它的指针:InitGoogleLogging(argv[0], &CustomPrefix);

可选地,InitGoogleLogging()void *类型的第三个参数传递给回调函数。

故障信号处理程序

该库提供了一个方便的信号处理程序,当程序在某些信号上崩溃时,它会转储有用的信息,例如SIGSEGV。 信号处理程序可以通过google::InstallFailureSignalHandler()安装。 以下是信号处理程序的输出示例。

*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
PC: @           0x412eb1 TestWaitingLogSink::send()
    @     0x7f892fb417d0 (unknown)
    @           0x412eb1 TestWaitingLogSink::send()
    @     0x7f89304f7f06 google::LogMessage::SendToLog()
    @     0x7f89304f35af google::LogMessage::Flush()
    @     0x7f89304f3739 google::LogMessage::~LogMessage()
    @           0x408cf4 TestLogSinkWaitTillSent()
    @           0x4115de main
    @     0x7f892f7ef1c4 (unknown)
    @           0x4046f9 (unknown)

默认情况下,信号处理程序将故障转储写入标准错误。您可以通过InstallFailureWriter()自定义目标位置。

消息性能

glog 提供的条件日志宏(例如 CHECKLOG_IFVLOG等)经过精心实现,当条件为假时不会执行右侧表达式。因此,以下检查可能不会牺牲应用程序的性能。

CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();

用户定义的失效函数

FATAL严重性级别的消息或不满足CHECK条件将会终止您的程序。您可以通过InstallFailureFunction更改终止的行为。

void YourFailureFunction() {
  // Reports something...
  exit(1);
}

int main(int argc, char* argv[]) {
  google::InstallFailureFunction(&YourFailureFunction);
}

默认情况下,glog 会尝试转储堆栈跟踪并使程序以状态 1 退出。仅当您在 glog 支持堆栈跟踪的架构上运行程序时才会生成堆栈跟踪。

原始记录

头文件<glog/raw_logging.h>可用于线程安全日志记录,它不分配任何内存或获取任何锁。因此,这个头文件中定义的宏可以被低级内存分配和同步代码使用。

谷歌风格 perror()

PLOG()PLOG_IF()PCHECK()的行为与LOG*CHECK完全一样 ,除了它们将error的当前状态的描述附加到它们的输出行之外。例如

PCHECK(write(1, NULL, 2) >= 0) << "Write NULL failed";

此检查失败并显示以下错误消息。

F0825 185142 test.cc:22] Check failed: write(1, NULL, 2) >= 0 Write NULL failed: Bad address [14]

系统日志

SYSLOGSYSLOG_IFSYSLOG_EVERY_N宏可用。除了正常日志之外,这些日志还记录到 syslog。请注意,将日志记录到 syslog 会极大地影响性能,尤其是在将syslog 配置为远程日志记录的情况下!在使用这些宏之前,请确保您了解输出到syslog的含义。一般来说,谨慎使用这些宏是明智的。

删除日志消息

日志消息中使用的字符串会增加二进制文件的大小并带来隐私问题。因此,您可以使用GOOGLE_STRIP_LOG宏命令 glog 删除所有低于特定严重性级别的字符串 :

如果您的应用程序有这样的代码:

#define GOOGLE_STRIP_LOG 1    // this must go before the #include!
#include <glog/logging.h>

编译器将删除严重性小于指定整数值的日志消息。由于VLOG日志处于严重级别 INFO(数值0),因此设置GOOGLE_STRIP_LOG1 或更高会删除与VLOG相关联的所有日志消息以及INFO日志语句。

自动删除旧日志

要启用日志清理器:

google::EnableLogCleaner(3); // keep your logs for 3 days

然后 glog 会在执行刷新时检查是否有过期日志。在此示例中,您项目中最后修改时间超过 3 天的任何日志文件都将被unlink()

可以随时禁用此功能(如果已启用)

google::DisableLogCleaner();
 类似资料: