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";
}
glog定义了一些简化打日志任务的宏。你可以通过重要级别来记录消息,从命令行控制消息行为,基于条件记录日志,当期望的条件不满足时终止程序,引入自己的详细级别,甚至更多。这个文档描述glog支持的功能。注意这个文档不会描述这个库中的所有的特性,但是会描述其中最有用的那些。如果你想要找到不那么常用的特性,可以查看下在src/glog
目录下的头文件。
可以指定如下的级别,按照重要性递增的:
除非指定了其他的标志,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拷贝ERROR和FATAL级别的日志消息除了到日志文件中之外还会拷贝到标准错误中。
很多标志都影响glog的输出行为。如果Google的gflags库也在你的机器上安装了, 那么configure脚本将自动检查并使用它,允许你在命令行传递flags。例如,如果你想要打开标志--logtostderr
,你可以用下面的命令启动你的应用程序:
./your_application --logtostderr=1
如果Google的gflags库没有安装,你可以通过环境变量设置flags,在flag的名字的前面增加GLOG_
前缀,例如:
GLOG_logtostderr=1 ./your_application
下面的flags是最常使用的:
还有很多其他的标志定义在logging.cc
文件中。用grep搜索源码"DEFINE_"可以看到完整的flags的列表。
你可以在程序中修改flag的值,通过修改全局变量FLAGS_*
的值来实现。大部分设置在你更新了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 . 改变目标文件的必须要在Init之前才会生效的
FLAGS_log_dir = "/some/log/directory";
LOG(INFO) << "the same file";
有时候可能想要在特定的条件下打日志,你可以使用下面的宏来执行条件日志:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
其中仅当num_cookies大于10才会记录这条日志。如果一行代码可能会被多次执行,那么仅仅在特定的间隔记录一条日志的功能非常有用。这种日志对于想查信息的消息非常有用:
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
上面这行在1st,11th,21st次执行的时候输出日志消息。注意,特殊的google::COUNTER
值是用来标志哪次重复正在发生。
你可以用下面的宏来组合条件和偶然日志:
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER << "th big cookie";
你可以限制头N次出现输出日志:
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
头20次执行的时候输出日志。同样,google::COUNTER指明哪次重复正在发生。
特定的debug模式仅仅在debug模式下有效,并且在非debug模式下直接变成空。使用这些宏可以避免因为过量的记日志而降低生产应用的性能。
DLOG(INFO) << "Found Cookies";
DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookies";
在你的程序中频繁检查期望的条件是一个好的实践,这样可以尽可能早的检查到错误。CHECK宏当条件不满足的时候,提供终止应用的能力,这就类似于标准C库中定义的assert宏。
当条件不为true的时候CHECK宏终止程序。跟assert不同的是,CHECK宏不是被NDEBUG控制的,因此,这个检查的过程不管是什么编译模式下都会执行。因此,在下面的例子中fp->Write(x)
总是会执行的:
CHECK(fp->Write(x) == 4) << "Write failed!";
有一系列的助手宏来做相等性和不等性的检查——CHECK_EQ, CHECK_NE, CHECK_LE, CHECK_LT, CHECK_GE, CHECK_GT
.他们比较两个值,并当结果跟期望的不一致时,记录一个包含两个值的FATAL消息。这个值必须要定义了operator<< (ostream&, ...)
。
你可能这样追加错误消息:
CHECK_NE(1, 2) << ": The world must be ending!";
我们非常仔细保证每个参数都被严格的求值一次,并且任何可以传递给函数参数的东西在这里都是合法的。特别的,参数可能是临时表达式,在其出现的语句结束的时候对象将会被销毁,例如:
CHECK_EQ(string("abc")[1], 'b');
如果其中的一个参数是指针,并且另外一个参数是NULL的情况下,编译器将会报错。为了避免这种情况,简单的将static_cast转换成目标指针类型就行:
CHECK_EQ(some_ptr, static_caste<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_STREQ,CHECK_STRNE,CHECK_STRCASEEQ和CHECK_STRCASENE
。 CASE版本不区分大小写。 您可以安全地为此宏传递NULL指针。 他们将NULL和任何非NULL字符串视为不相等。 两个NULL相等。
请注意,这两个参数都可能是临时字符串,它们在当前“完整表达式”结束的时候被销毁(例如,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
将打印:
通配符功能支持*
(匹配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_IF,VLOG_EVERY_N和VLOG_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;
库提供了一个方便的信号处理器,这个信号处理器当程序在特定信号(如SIGSEGV)上崩溃的时候,将dump有用信息。信号处理器可以通过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)
默认情况下,信号处理器将dump信息输出到标准错误。也可以通过InstallFailureWriter()
定制输出目的地。
glog提供的条件日志宏(如CHECK,LOG_IF,VLOG,…)都是非常仔细实现的,当条件是false的时候都不会执行右边的表达式。因此,下面的check不会牺牲你的应用程序的性能:
CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();
FATAL严重级别或者没有满足的CHECK条件都会终止应用程序。可以通过InstallFailureFunction()
函数来改变这种终止应用程序的行为。
void YourFailureFunction() {
// Reports something...
exit(1);
}
int main(int argc, char* argv[]) {
google::InstallFailureFunction(&YourFailureFunction);
}
默认情况下,glog会尝试dump stacktrace并用状态1来使程序退出。仅当您在glog支持堆栈跟踪的体系结构上运行程序时才生成stacktrace(从2008年9月开始,glog支持x86和x86_64的堆栈跟踪)。
头文件<glog/raw_logging.h>
可用于线程安全日志记录
,该日志记录不分配任何内存或获取任何锁。 因此,在此头文件中定义的宏可以在低级内存分配和同步代码中使用。请检查src/glog/raw_logging.h.in
了解详细信息。
PLOG()和PLOG_IF()和PCHECK()
的行为与它们的LOG*和CHECK等效项完全相同,不同之处在于,它们将对errno当前状态的描述附加到其输出行中。 例如:
PCHECK(write(1, NULL, 2) >= 0) << "Write NULL failed";
这个校验将以如下的错误信息失败(主要是把errno给打印出来了):
F0825 185142 test.cc:22] Check failed: write(1, NULL, 2) >= 0 Write NULL failed: Bad address [14]
SYSLOG,SYSLOG_IF和SYSLOG_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_LOG设置为1或更高级别会删除所有与VLOG相关的日志消息以及INFO日志语句。