来自Google的Glog是一个应用程序的日志库。它提供基于C++风格的流的日志API,以及各种辅助的宏。打印日志只需以流的形式传给 LOG(level) ,例如:
#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定义了一系列的宏来简化记录日志的工作。你可以:按级别打印日志,通过命令行控制日志行为,按条件打印日志,不满足条件时终止程序,引入自定义的日志级别,等等。
下载安装
源码下载地址: https://github.com/google/glog.git
安装方法:
终端进入glog目录,以root身份运行以下命令:
$ ./autogen.sh && ./configure && make && make install
# glog的头文件位于/usr/local/include
# glog的库文件位于/usr/local/lib
# gflags 库会默认安装在 /usr/local/lib/ 下,头文件放在 /usr/local/include/gflags/
window平台安装使用参考:https://blog.csdn.net/Pig_Pig_Bang/article/details/81632962
主要支持功能:
1, 参数设置,以命令行参数的方式设置标志参数来控制日志记录行为;
2, 严重性分级,根据日志严重性分级记录日志;
3, 可有条件地记录日志信息;
4, 条件中止程序。丰富的条件判定宏,可预设程序终止条件;
5, 异常信号处理。程序异常情况,可自定义异常处理过程;
6, 支持debug功能。可只用于debug模式;
7, 自定义日志信息;
8, 线程安全日志记录方式;
9, 系统级日志记录;
10, google perror风格日志信息;
11, 精简日志字符串信息。
日志级别
可以指定下面这些级别(按严重性递增排序): INFO, WARNING, ERROR, and FATAL 。打印 FATAL 消息会在打印完成后终止程序。和其他日志库类似,级别更高的日志会在同级别和所有低级别的日志文件中打印。
DFATAL 级别会在调试模式(没有定义 NDEBUG 宏)中打印 FATAL 日志,但是会自动降级为 ERROR 级别,而不终止程序。
如果不指定的话,Glog输出到文件 /tmp/...log..-
<Serverity level><number> <time>.<number> <pid> <file>:<line number> <messages>
在GLog记录的消息的开头以I, W, E, F来标示信息的严重级别;
#include <glog/logging.h>
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
LOG(ERROR) << "There is error !!!";
return 0;
}
设置
一些flag会影响Glog的输出行为。如果安装了GFlags库,编译时会默认使用它,这样就可以在命令行传递flag(别忘了调用 ParseCommandLineFlags 初始化)。比如你想打开 --logtostderr flag,可以这么用:
./your_application --logtostderr=1
如果没有安装GFlags,那可以通过环境变量来设置,在flag名前面加上前缀 GLOG_ 。比如:
GLOG_logtostderr=1 ./your_application
常用的flag有:
logtostderr ( bool ,默认为 false )
日志输出到stderr,不输出到日志文件。
colorlogtostderr ( bool ,默认为 false )
输出彩色日志到stderr。
stderrthreshold ( int ,默认为2,即 ERROR )
将大于等于该级别的日志同时输出到stderr。日志级别 INFO, WARNING, ERROR, FATAL 的值分别为0、1、2、3。
minloglevel ( int ,默认为0,即 INFO )
打印大于等于该级别的日志。日志级别的值同上。
log_dir ( string ,默认为 “” )
指定输出日志文件的目录。
v ( int ,默认为0)
显示所有 VLOG(m) 的日志, m 小于等于该flag的值。会被 --vmodule 覆盖。
vmodule ( string ,默认为 “” )
每个模块的详细日志的级别。参数为逗号分隔的一组 = 。 支持通配(即gfs代表所有gfs开头的名字),匹配不包含扩展名的文件名(忽略 .cc/.h./-inl.h 等)。 会覆盖 --v 指定的值。
logging.cc 中还定义了其他一些flag。grep一下 DEFINE_ 可以看到全部。
也可以通过修改 FLAGS_ 全局变量来改变flag的值。
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的目录
如果我们想指定log文件输出位置,那么我们可以对GLog设置一下标志(FLAGS_log_dir);
#include <glog/logging.h>
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
FLAGS_log_dir = "./"; // 将日志文件输出到本地
LOG(ERROR) << "There is error !!!";
return 0;
}
在本地就会产生日志文件,日志文件有四个基本文件.INFO,.WARNING,.ERROR,.FATAL,这些文件都汇总了这四类日志信息;
- 设置只在终端显示Log
当我们只想将日志信息输出到stderr在终端中显示的时候,我们可以设置(FLAGS_logtostderr);
设置FLAGS_logtostderr = 1将使得日志信息记录到stderr而不会保存到本地日志文件中,及时你设置了FLAGS_log_dir; - 设置只在终端显示Log
当我们只想将日志信息输出到stderr在终端中显示的时候,我们可以设置(FLAGS_logtostderr);
设置FLAGS_logtostderr = 1将使得日志信息记录到stderr而不会保存到本地日志文件中,及时你设置了FLAGS_log_dir; - 设置记录Log到本地以及终端显示
当然我们可以鱼和熊掌兼得,我们可以通过设置FLAGS_alsologtostderr = 1将实现日志信息输出到stderr,并且会记录到本地日志文件; - 设置记录到stderr的log的级别
这仅仅是对于记录Log到stderr中的设置;
当我们需要将WARNING等级的log记录到stderr时,我们可以通过设置FLAGS_stderrthreshold = 1,那么WARNING以及更高级别的Log将会记录到stderr中;INFO: 0, WARNING: 1, ERROR: 2 FATAL: 3; - 设置需要记录log的级别
我们可以指定那些Log被记录(无论是在本地还是stderr),通过设置FLAGS_minloglevel = 0标志,默认是0,设置这个标志表明,INFO以及更高级别的Log将会被记录;INFO: 0, WARNING: 1, ERROR: 2 FATAL: 3; - 条件记录
我们可以使用LOG_IF()来到达有条件的输出日志的目的;
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
FLAGS_logtostderr = 1; // 设置将Log记录到stderr
for(int i = 0; i < 20; i++) {
LOG_IF(INFO, i > 15) << "i > 15"; //当i > 15时,记录Log;
}
return 0;
}
- 周期记录
通过LOG_EVERY_N()实现周期性的输出日志,意思解释说,LOG_EVERY_N()执行n次才输出一次Log;
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
FLAGS_logtostderr = 1; // 设置将Log记录到stderr
for(int i = 0; i < 20; i++) {
LOG_EVERY_N(INFO, 3) << "i: " << i; //每隔3次输出一次信息;
}
return 0;
}
- 条件加周期记录
通过LOG_IF_EVERY_N实现,当满足条件之后,每隔n次输出Log;
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
FLAGS_logtostderr = 1; // 设置将Log记录到stderr
for(int i = 0; i < 20; i++) {
LOG_IF_EVERY_N(INFO, i > 10, 3) << "i > 10, i: " << i; //当i > 10之后,每隔3次输出一次信息;
}
return 0;
}
- 限制Log输出次数
通过LOG_FIRST_N()实现,只输出前n次信息;
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]); // 初始化GLog库
FLAGS_logtostderr = 1; // 设置将Log记录到stderr
for(int i = 0; i < 20; i++) {
LOG_FIRST_N(INFO, 4) << "i: " << i; // 输出前4次Log
}
return 0;
}
支持调试模式
调试模式的宏仅在调试模式下有效,在非调试模式编译时为空。
在宏前面加D就支持了调试模式;
DLOG(), DLOG_IF(), DLOG_EVERY_N(), …
崩溃处理
当程序出现崩溃时,GLog也可以提供Bug定位支持。通过google::InstallFailureSignalHandler()安装信号处理程序,当程序出现崩溃时,会输出崩溃的位置等相关信息
线程安全日志记录
glog提供了线程安全的日志记录方式。在<glog/raw_logging.h>文件中提供了相关的宏,如,RAW_CHECK,RAW_LOG等。这些宏的功能与CHECK,LOG等一致,除此以外支持线程安全,不需要为其分配任何内存和提供额外的锁(lock)机制
系统级日志记录
glog除了提供了普通的日志记录宏,还提供SYSLOG, SYSLOG_IF,和 SYSLOG_EVERY_N宏,这些宏将日志信息通过syslog()函数记录到系统日志。
google perror风格日志信息
glog提供了与LOG*和CHECK宏作用等价的PLOG()、PLOG_IF() 和PCHECK()宏,不同的是,后者在记录日志信息的时候,会将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]
精简日志字符串信息
日志信息的字符串会占用比较大的内存空间,另外还带来隐私泄露的问题。glog提供了GOOGLE_STRIP_LOG宏在编译时候去除日志的字符串信息。
其他说明
windows平台使用注意
因为glog的严重性级别中使用了ERROR宏,与<windows.h>文件中冲突,可通过以下两种方式避免:
a,在包含<windows.h>文件之前,定义宏WIN32_LEAN_AND_MEAN 或者NOGDI。
b,在包含<windows.h>文件之后,undef掉ERROR定义
CHECK宏
常做状态检查以尽早发现错误是一个很好的编程习惯。 CHECK 宏和标准库中的 assert 宏类似,可以在给定的条件不满足时终止程序。CHECK 和 assert 不同的是,它不由 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 ,编译器会报错。可以给 NULL 加上对应类型的 static_cast 来绕过。
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_STREQ, CHECK_STRNE, CHECK_STRCASEEQ, CHECK_STRCASENE 。 CASE 的版本是不区分大小写的。这里可以传入 NULL 。 NULL 和任何非 NULL 的字符串是不等的,两个 NULL 是相等的。
这里的参数都可以是临时字符串,比如 CHECK_STREQ(Foo().c_str(), Bar().c_str()) 。
CHECK_DOUBLE_EQ 宏可以用来检查两个浮点值是否等价,允许一点误差。 CHECK_NEAR 还可以传入第三个浮点参数,指定误差。
细节日志
当你在追比较复杂的bug的时候,详细的日志信息非常有用。但同时,在通常开发中需要忽略太详细的信息。对这种细节日志的需求,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) 则不会打印。对 VLOG 宏和 --v flag可以指定任何整数,但通常使用较小的正整数。 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时返回true。比如:
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;
失败信号处理
Glog库还提供了一个信号处理器,能够在 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)
注意: InstallFailureSignalHandler() 在x86_64系统架构上可能会引发退栈的死锁,导致递归地调用 malloc 。这是内置的退栈的bug,建议在安装Glog之前安装libunwind。更多解释可以看Glog的 INSTALL 文件。
apt-get install libunwind libunwind-dev
默认情况,信号处理器把失败信息导出到stderr。可以用 InstallFailureWriter() 定制输出位置。
其他
Glog并不自带CMake支持,如果想在CMake脚本中使用它,可以把 FindGlog.cmake 添加到CMake的模块目录下。然后像下面这样使用:
find_package (Glog REQUIRED)
include_directories (${GLOG_INCLUDE_DIR})add_executable (foo main.cc)
target_link_libraries (foo glog)
性能
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会导出stacktrace,程序以状态1退出。stacktrace只在Glog支持栈跟踪的系统架构(x86和x86_64)上导出。
原始日志
<glog/raw_logging.h> 可用于要求线程安全的日志,它不分配任何内存,也不加锁。因此,该头文件中定义的宏可用于底层的内存分配和同步的代码。
谷歌风格的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]
跳过日志
打印日志的代码中的字符串会增加可执行文件的大小,而且也会带来泄密的风险。可以通过使用 GOOGLE_STRIP_LOG 宏来删除所有低于特定级别的日志:
比如使用下面的代码:
#define GOOGLE_STRIP_LOG 1 // this must go before the #include!
#include <glog/logging.h>
编译器会删除所有级别低于该值的日志。因为 VLOG 的日志级别是 INFO (等于0),设置 GOOGLE_STRIP_LOG 大于等于1会删除所有 VLOG 和 INFO 日志。
Windows用户的注意事项
Glog定义的 ERROR 日志级别,和 windows.h 中的定义有冲突。可以在引入 glog/logging.h 之前定义 GLOG_NO_ABBREVIATED_SEVERITIES ,这样Glog就不会定义 INFO, WARNING, ERROR, FATAL 。不过你仍然可以使用原来的宏:
#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 中的函数中使用 INFO, WARNING, ERROR, FATAL 了。
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#include <glog/logging.h>
// …
// This won’t work.
// google::FlushLogFiles(google::ERROR);
// Use this instead.
google::FlushLogFiles(google::GLOG_ERROR);
如果不需要使用 windows.h 中定义的 ERROR ,那么也可以尝试下面的方法:
- 在引入 windows.h 之前 #define WIN32_LEAN_AND_MEAN 或 NOGDI 。
- 在引入 windows.h 之后 #undef ERROR 。