glog使用指南

本文详细介绍了如何在C++应用中使用Google日志库glog,包括设置严重级别、条件记录、调试模式支持、VLOG的使用、失败信号处理器以及Windows平台的注意事项。此外,还讨论了如何控制日志输出、性能优化和日志保护措施。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何使用Google日志库(glog)

https://rpg.ifi.uzh.ch/docs/glog.html
译者:kimi.ai

介绍

Google glog 是一个实现应用级别日志记录的库。这个库提供了基于C++风格流的日志API和各种辅助宏。你可以通过简单地将内容流式传输到LOG(特定严重级别)来记录一条消息,例如:

#include <glog/logging.h>

int main(int argc, char* argv[]) {
    // 初始化Google的日志库。
    google::InitGoogleLogging(argv[0]);

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

Google glog定义了一系列宏,简化了许多常见的日志记录任务。你可以:

  • 按严重级别记录消息,从命令行控制日志行为,
  • 基于条件记录日志,当预期条件未满足时中止程序,
  • 引入你自己的详细日志级别,等等。

本文档描述了glog支持的功能。请注意,本文档没有描述此库中的所有特性,而只是最有用的一部分。如果你想找到不太常见的特性,请检查src/glog目录下的头文件。

严重级别

你可以指定以下严重级别之一(按严重程度递增的顺序):INFOWARNINGERRORFATAL记录一个FATAL消息会终止程序(在消息记录之后)

请注意,给定严重级别的消息不仅记录在该严重级别的日志文件中,还记录在所有较低严重级别的日志文件中。例如,严重级别为FATAL的消息将记录在严重级别为FATALERRORWARNINGINFO的日志文件中。

DFATAL严重级别在调试模式下(即没有定义NDEBUG宏)记录一个FATAL错误,但在生产中通过自动将严重级别降低到ERROR来避免停止程序。

除非另有说明,glog将日志写入到文件名"/tmp/<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>" (e.g., "/tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474")

默认情况下,glog将严重级别为ERRORFATAL的日志消息复制到标准错误(stderr)以及日志文件中。

设置标志

有几个标志影响glog的输出行为。如果你的机器上安装了Google gflags库configure脚本(有关此脚本的详细信息,请参见包中的INSTALL文件)将自动检测并使用它,允许你在命令行上传递标志。例如,如果你想打开标志--logtostderr,你可以使用以下命令行启动你的应用程序:

./your_application --logtostderr=1

如果Google gflags库没有安装,你可以通过环境变量设置标志,将标志名称前缀为"GLOG_",例如:

GLOG_logtostderr=1 ./your_application

以下标志最常用:

  • logtostderr(布尔值,默认为false
    • 将日志消息记录到stderr而不是日志文件。
  • stderrthreshold(整数,默认为2,即ERROR
    • 除了日志文件之外,还将此级别或更高级别的日志消息复制到stderr。
  • minloglevel(整数,默认为0,即INFO
    • 记录此级别或更高级别的日志消息。
  • log_dir(字符串,默认为空"")
    • 如果指定,日志文件将写入此目录,而不是默认的日志目录。
  • v(整数,默认为0)
  • vmodule(字符串,默认为空"")
    • 每个模块的详细日志级别。参数必须包含一个逗号分隔的<模块名称>=<日志级别>列表。<模块名称>是一个全局模式(例如,gfs*表示所有名称以"gfs"开头的模块),与文件名基础(即忽略.cc/.h./-inl.h的名称)匹配。<日志级别>会覆盖--v给出的任何值。另请参见关于详细日志记录的部分
  • 还有其他在logging.cc中定义的标志。请使用grep搜索源代码中的"DEFINE_"以查看所有标志的完整列表。

你也可以通过修改全局变量FLAGS_*在你的程序中修改标志值。大多数设置在更新FLAGS_*后立即生效。例外的是与目标文件相关的标志。例如,你可能想在调用google::InitGoogleLogging之前设置FLAGS_log_dir。这里是一个例子:

LOG(INFO) << "file";
// 大多数标志在更新值后立即生效。
FLAGS_logtostderr = 1;
LOG(INFO) << "stderr";
FLAGS_logtostderr = 0;
// 这不会改变日志目的地。如果你想设置这个值,
// 你应该在调用google::InitGoogleLogging之前做这个。
FLAGS_log_dir = "/some/log/directory";
LOG(INFO) << "the same file";

条件/偶尔记录

  1. 有时,你可能只想在某些条件下记录一条消息。你可以使用以下宏执行条件记录:

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

    "Got lots of cookies"消息只有在变量num_cookies超过10时才会记录。如果一行代码执行多次,可能有助于只在某些间隔记录一条消息。这种记录对于信息性消息最有用。

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

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

  3. 你可以使用以下宏将条件和偶尔记录结合起来。

    LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER << "th big cookie";
    
  4. 你可以使用以下宏限制输出到前n次发生的情况,而不是每nth次输出一条消息:

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

    输出日志消息,直到执行的前20次。再次,google::COUNTER标识符指示正在发生的重复。

调试模式支持

特殊的"调试模式"日志宏只在调试模式下有效,并且在非调试模式编译时被编译为无。使用这些宏可以避免由于过度记录而减慢你的生产应用程序。

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<<(std::ostream, ...)定义。

你可以像这样向错误消息添加内容:

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

我们非常注意确保每个参数恰好被评估一次,并且任何合法传递给函数参数的表达式在这里也是合法的。特别是,参数可以是临时表达式,这些表达式将在语句结束时被销毁,例如:

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

如果一个参数是指针,而另一个是NULL,编译器会报告错误。为了解决这个问题,简单地将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接受第三个浮点参数,指定可接受的错误范围。

详细日志记录vlog

当你在追踪困难的错误时,详细的日志消息非常有用。然而,你可能想要在通常的开发中忽略过于详细的日志。对于这种详细日志,glog提供了VLOG宏,它允许你定义自己的数字日志级别。--v命令行选项控制哪些详细消息被记录:

VLOG(1) << "当我运行程序时,如果--v=1或更高,我会打印出来";
VLOG(2) << "当我运行程序时,如果--v=2或更高,我会打印出来";

使用VLOG,详细级别越低,消息被记录的可能性越大。例如,如果--v==1VLOG(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

将会:

  • a. 打印来自mapreduce.{h,cc}的VLOG(2)及更低级别的消息。
  • b. 打印来自file.{h,cc}的VLOG(1)及更低级别的消息。
  • c. 打印以"gfs"为前缀的文件的VLOG(3)及更低级别的消息。
  • d. 打印来自其他地方的VLOG(0)及更低级别的消息。

©所示的通配功能支持’*‘(匹配0个或多个字符)和’?'(匹配任何单个字符)通配符。请也查看命令行标志部分。

还有VLOG_IS_ON(n)“详细级别”条件宏。当--v等于或大于n时,此宏返回true。使用方式如下:

if (VLOG_IS_ON(2)) {
    // 做一些日志准备和记录
    // 这些不能只用VLOG(2)完成;
}

详细级别条件宏VLOG_IFVLOG_EVERY_NVLOG_IF_EVERY_N类似于LOG_IFLOG_EVERY_NLOF_IF_EVERY,但接受一个数字详细级别而不是严重级别。

VLOG_IF(1, (size > 1024))
    << "当size超过1024,并且你运行程序时 --v=1 或更高,我会打印";
VLOG_EVERY_N(1, 10)
    << "每10次出现一次,并且当你运行程序时 --v=1 或更高。当前出现是 " << google::COUNTER;
VLOG_IF_EVERY_N(1, (size > 1024), 10)
    << "在size超过1024的情况下,每10次出现一次,当你运行程序时 --v=1 或更高。" << "当前出现是 " << google::COUNTER;

失败信号处理器

库提供了一个方便的信号处理器,当程序在某些信号(如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() {
    // 报告一些事情...
    exit(1);
}

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

默认情况下,glog尝试转储堆栈跟踪并在程序上运行时退出状态为1。只有在你运行程序的架构支持堆栈跟踪时(截至2008年9月,glog支持x86和x86_64的堆栈跟踪),才会生成堆栈跟踪。

原始日志记录(Raw Logging)

glog/raw_logging.h 头文件可以用来进行线程安全的日志记录,它不会分配任何内存或获取任何锁。因此,这个头文件中定义的宏可以被用于低级别的内存分配和同步代码中。有关详细信息,请查看 src/glog/raw_logging.h.in

Google风格的perror()

PLOG()PLOG_IF()PCHECK() 的行为与它们的 LOG*CHECK 对应物完全相同,不同之处在于它们会在输出行的末尾附加当前 errno 状态的描述。例如:

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]

日志到syslog

SYSLOGSYSLOG_IFSYSLOG_EVERY_N 宏可用于将日志记录到 syslog 以及普通的日志中。请注意,将日志记录到 syslog 可能会严重影响性能,特别是如果 syslog 配置为远程日志记录的话!在您使用这些宏之前,请确保您了解输出到 syslog 的影响。通常,建议谨慎使用这些宏。

剥离日志消息

日志消息中使用的字符串可能会增加您的二进制文件大小,并带来隐私问题。因此,您可以使用 GOOGLE_STRIP_LOG 宏指示 glog 移除所有低于某个严重级别的字符串,从而避免这些问题。如果您的应用中有如下代码:

#define GOOGLE_STRIP_LOG 1    // 这必须在 #include 之前!
#include <glog/logging.h>

编译器将会移除严重级别低于指定整数值的所有日志消息。由于 VLOG 日志在 INFO 严重级别(数值为 0),将 GOOGLE_STRIP_LOG 设置为 1 或更高将移除所有与 VLOG 相关的日志消息以及 INFO 日志语句。

Windows用户的注意事项

Google glog 定义了一个严重级别 ERROR,这在 windows.h 中也有所定义。您可以通过在包含 glog/logging.h 之前定义 GLOG_NO_ABBREVIATED_SEVERITIES 来使 glog 不定义 INFOWARNINGERRORFATAL。即使使用这个宏,您仍然可以使用类似 iostream 的日志功能:

#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#include <glog/logging.h>

// ...
LOG(ERROR) << "This should work";
LOG_IF(ERROR, x > y) << "This should be also OK";

然而,您将无法再在 glog/logging.h 定义的函数中使用 INFOWARNINGERRORFATAL

#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#include <glog/logging.h>

// ...
// 这不会工作。
// google::FlushLogFiles(google::ERROR);

// 使用这个代替。
google::FlushLogFiles(google::GLOG_ERROR);

如果您不需要 windows.h 定义的 ERROR,还有一些其他的解决方法,有时这些方法可能不奏效:

  • 在包含 windows.h 之前定义 WIN32_LEAN_AND_MEANNOGDI
  • 在包含 windows.h 之后取消定义 ERROR

有关更多详细信息,请参见 这个问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值