Google glog是一个实现应用级别日志的库。该库提供基于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"; }
Google glog 定义了一系列宏用于简化很多常见的日志任务。你可以按照严重级别记录日志、使用命令行控制日志行为、基于条件记录日志、预期情形没有出现时退出程序、引入你自己的日志等级,等等。该文档介绍了glog支持的一些功能。请注意,该文档只介绍了该库最有用的一些功能,并非全部。如果你想了解一些比较不常使用的功能,你可以查看 src/glog 目录下面的头文件。
你可以使用以下严重级别(按照严重性递增): INFO 、 WARNING 、 ERROR 和 FATAL 。记录 FATAL 日志会(在信息记录完成后)终止程序。注意,特定级别的信息并非只保存到对应的日志文件,还会保存到低等级的日志文件。例如FATAL的日志会保存到 FATAL 、 ERROR 、 WARNING 和 INFO 对应的文件。
DFATAL等级在调试模式下记录 FATAL 信息(注意,没有定义宏 NDEBUG ),但不会像生产环境中那样终止程序执行,而是自动降低到 ERROR 级别。
除非特别指定,glog日志文件名格式为 “ /tmp/<program name>.<hostname>.<username>.log.<severity level>.<date>.<time>.<pid> ”。(例如" /tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474 ")。默认情况下,glog还会把 ERROR 和 FATAL 级别的信息打印到标准输出。
很多标记会影响 glog 的输出行为。如果你安装了 Google gflags library , configure 脚本(查看软件包中的 INSTALL 脚本了解该脚本的详细信息)会自动检测并使用该库,从而允许你通过命令行传递标记。例如,如果你想启用 –logtostderr 标记,你可以用下面的命令行启动程序:
./your_application --logtostderr=1
如果你没有安装 Google gflags 库,你可以通过在标记名称前面使用 “ GLOG_ ”前缀的环境变量设置标记,例如:
GLOG_logtostderr=1 ./your_application
下面是一些通常会用到的标记:
logtostderr (bool, default=false) 将日志输出到 stderr 而不是日志文件。 注意:你可以使用1、true或者yes(大小写敏感)设置二值标记为真。同样,你也可以使用 0、false或者no(大小写敏感)设置二值标记为假。 stderrthreshold (int, default=2, which is ERROR) 除了将信息输出到日志文件外,还将等于、高于该等级的信息输出到stderr。严重级别 INFO、WARNING、ERROR 和 FATAL 分别对应数字0、1、2和3. minloglevel (int, default=0, which is INFO) 只记录不小于该等级的日志信息。同样,严重级别 INFO、WARNING、ERROR 和 FATAL 分别对应数字0、1、2和3. log_dir (string, default="") 如果指定了该标记,日志文件会被写到该目录而不是默认日志目录。 v (int, default=0) 显示所有不大于该等级的日志信息。可以被 --vmodule 覆盖。可以阅读详细日志章节进一步了解该标记。 vmodule (string, default="") 每个模块的详细日志级别。该参数由逗号分隔的 <module name>=<log level> 对组成。<module name> 是一个全局模式(例如 gfs* 代表所有以 “gfs”开头的模块),和文件名匹配(即忽略.cc/.h/-inl.h 后缀)。<log level> 会覆盖 --v 指定的值。阅读详细日志章节了解该标记。
logging.cc 中还定义了一些其它的标记。你可以通过用 grep 在该文件中搜索关键字 " DEFINE_ " 获取标记的完整列表。
你也可以通过在你的程序中更改全局变量 FLAGS * 改变标记的值。大部分设定在你更新 FLAGS * 后就会立即生效。和目标文件相关的标记例外。例如,你需要在调用 google::InitGoogleLogging 之前设置标记 FLAGS log dir ,下面是个具体例子:
LOG(INFO) << "file"; // 大部分标记在更新值后立即生效 FLAGSlogtostderr = 1; LOG(INFO) << "stderr"; FLAGSlogtostderr = 0; // 这不会改变日志目录。如果你想要设置这个值,你要在调用 // google::InitGoogleLogging 之前设置。 FLAGSlogdir = "/some/log/directory"; LOG(INFO) << "the same file";
有时候,你可能想只有在某些情形下才记录日志。你可以使用下面的宏进行日志条件记录:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; "Got lots of cookies" 消息只有当变量 num_cookies 超过 10后才会记录。如果一行代码可能执行多次,按照特定间隔记录日志也许会非常有用。这种类型日志通常适用于消息性信息。 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 次执行。
特殊的 “调试模式” 日志宏只在调试模式下有效、而在非调试模式编译时会被编译去除。使用下面的宏避免在生产环境中由于庞大的日志拖慢你的程序。
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 使得当一个条件没有满足时退出执行的能力、这和标准 C 库中定义的宏 assert 类似。
如果某个条件为假 CHECK 就会退出程序。和 assert 不同,它不由 NDEBUG 控制,因此 check 的执行和编译模式无关。因此,下面例子中的 fp->Write(x) 总会执行。
CHECK(fp->Write(x) == 4) << "Write failed!";
这里有很多好用的等值/不等值宏 – CHECK EQ , CHECKNE , CHECK LE , CHECK LT , CHECKGE , 以及 CHECKGT 。它们会比较这两个值,如果比较结果和预期不同,就会以 FATAL 日志形式记录这两个值。值必须定义了 <<(ostream,…) 操作符。
你可以像下面这样追加错误信息:
CHECK_NE(1, 2) << ": The world must be ending!";
我们尽可能保证每个参数都只会计算一次值,任何可以作为函数参数传递的东西在这里都合法。尤其是、参数可能是该语句执行后就会被删除的临时表达式,例如:
CHECK_EQ(string("abc")[1], 'b');
如果上面的一个参数一个是指针而另一个为空,编译器就会报错。为了解决这个问题,你可以使用 static_cast 将 NULL 转换为需要的指针类型。
CHECK_EQ(some_ptr, static_cast<SomeType*>(NULL));
更好的办法是使用 CHECK NONULL 宏:
Better yet, use the CHECKNOTNULL macro:
CHECK_NOTNULL(some_ptr); some_ptr->DoSomething();
由于该宏返回指定的指针,因此在构造函数初始化列表中非常有用。
struct S { S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {} Something* ptr_; };
注意由于它的特性你不能像 C++ 流那样使用这个宏。请使用上面介绍的 CHECK_EQ 在退出程序之前记录自定义的信息。
如果你想和 C 字符串(char*)比较,一些好用的宏可以进行大小写敏感(不敏感)比较 – CHECK STREQ , CHECKSTRNE , CHECK STRCASEEQ , 以及 CHECKSTRCASENE 。 带有 CASE 的是大小写敏感的。你可以放心地传递空指针给这些宏。它们认为空指针和任何非空字符串是不相等的,两个空指针之间是相等的。
注意,两个参数都可以是在当前表达式之后会被析构的临时字符串(例如 CHECK STREQ(Foo().c str(), Bar().c_str()),其中 Foo 和 Bar 函数都返回 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
会:
a. 打印 mapreduce.{h,cc} 中 VLOG(2)以及以下的信息。
b. 打印 file.{h,cc} 中 VLG(1) 及以下的信息。
c. 打印用 “gfs” 作为前缀的文件中 VLOG(3) 及以下的信息。
d. 打印所有文件中 VLOG(0) 以及以下的信息。
c 中使用的通配符功能支持 ‘*’(匹配一个或多个字符)以及‘?’(匹配一个字符)。
还有 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 IF , VLOGEVERY N 以及 VLOGIF 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;
该库还提供了一个非常方便的信号处理器,当程序由于类似 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 提供的条件记录宏(例如, CHECK , LOG_IF , VLOG , …) 经过了很好的优化,当条件失败是不会去计算右边表达式的值。因此,下面的检查不会损害你应用程序的性能。
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 支持调用栈的体系结构上运行你的程序时才会导出调用找。(截至2008年9月, glog支持 x86 和 x86_64体系结构上的堆栈跟踪
头文件 <glog/raw_logging.h> 能用于线程安全日志记录,它不需要分配任何内存或者获取任何锁。因此,该头文件里定义的宏能用于低层次的内存分配和同步代码。查看 src/glog/raw_logging.h 了解详细信息。
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]
事例程序: test-glog.cpp
Reference: