GLOG从入门到入门
1 C++日志需求
任何可用的工程代码,都无法保证不出bug。因此,日志系统对于工程代码非常重要。对于像耳东小白这样的初级程序员(感谢某高级程序员大佬赐予“初级程序员”封号,小白会继续努力)来说,编写自己的日志系统,缺乏这种必要~~(主要是缺乏这种能力)~~。
Google Logging (glog) is a C++98 library that implements application-level logging. The library provides logging APIs based on C+±style streams and various helper macros.
GLOG即谷歌日志系统,是一个C++98标准下开发的库,实现了应用级的日志系统。该库提供了C++风格的日志API,以及丰富的帮助宏。——翻译完毕。
GLOG github传送门
2 用CMake编译GLOG-0.6.0
很尴尬,小白并不知道哪里有编译好的GLOG版本(知道的同学可以把下载地址打在评论区),只能从github上下载源码来编译。
小白的编译环境是Win10系统 + Visual Studio 2013 + CMake-3.21.0。
此时此刻,下载的最新版本是GLOG-0.6.0。
编译其实很简单,选好源码的路径,建好编译结果的路径,编译器选64位的Visual C++(这些太基础了,连作为初级程序员的小白都觉得可以跳过了)
本着大胆乱来的精神,反正只要是没有预装的东西,且影响编译的东西,都别勾上。
这里注意把WITH_GFLAGS
WITH_GTEST
WITH_UNWIND
都不要勾选。因为没预装,看这些名称,估计也不是非得装不可(其实不是估计出来的,因为勾上了就会报error),没有就算了。
接下来就是Configure --> Generate --> Open Project一连三键。
打开.sln文件后,把INSTALL设为启动项目,然后生成x64 Release版本。(有了之前编译OpenCV和DarkNet的经验,感觉乱来的时候内心毫无波澜)。
通常如果没有啥幺蛾子的话,应该会直接编译成功,小白这里一共17个项目编译成功。
到这一步为止,好像拿到了一个编译成功的版本,那么它到底能不能用呢?
3 在C++项目中引入GLOG库
参看前人的博客,都会说只需要找到libglog.dll和libglog.lib,再加上几个头文件,就是能用的glog库了。然鹅…
编译出来没有libglog.dll,也没有libglog.lib!
小白在此时真的觉得自己是不是还是应该把那几个没勾选的东西先折腾一下?!
那么我们来看看编译出来的文件里到底有什么…
编译出来的Release文件夹里,找到了glog.dll和glog.lib。
在编译出来的glog文件夹里,找到了疑似需要的几个头文件:
其中,logging.h是很多代码里直接包含的,那么是不是glog本身版本升级后,导出库的名称发生了变化?
干脆试一试。
小白建立的工程文件树是这样的:
├─3rdparty
│ └─glog-0.6.0
│ ├─bin
│ ├─include
│ │ └─glog
│ └─lib
├─build
│ └─x64
│ └─Release
├─log
├─source
└─windows
└─testGlog
其中,glog.dll放在 ./3rdparty/glog-0.6.0/bin/
目录下;
glog.lib放在./3rdparty/glog-0.6.0/lib/
目录下;
那几个头文件全部放在./3rdparty/glog-0.6.0/include/glog/
目录下;
此处要注意,由于glog中头文件的相互包含带有路径关系,一定要将头文件置于名为glog的目录下,且在工程文件中附加包含目录要包含glog的上级目录,在小白的工程中,即需要包含
./3rdparty/glog-0.6.0/include
路径
执行文件输出到./build/x64/Release/
目录下;
目标日志输出到./log/
目录下。
在source下新建一个源文件testMain.cpp:
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <glog/logging.h>
#include <iostream>
#include <vector>
#define WRITE_LOG(s) (LOG(INFO)<<s)
using namespace std;
int main(int argc, char * argv[])
{
FLAGS_log_dir = "../../../log";
google::SetLogFilenameExtension(".log");
google::InitGoogleLogging("AlgLog");
WRITE_LOG("google initial log");
google::FlushLogFiles(google::GLOG_INFO);
system("pause");
return EXIT_SUCCESS;
}
其中,GLOG_NO_ABBREVIATED_SEVERITIES
一开始没定义,报错。
点击生成----> 没通过!
看一下报错信息,似乎是少了头文件…
log_severity.h 和 platform.h?
小白突然灵机一闪,去看了一眼源文件的glog目录:
于是把这两个文件都拷到工程目录下的./3rdparty/glog-0.6.0/include/glog/
目录下。
生成通过!
运行得到的.exe文件,可以生成日志:
打开可以看到日志的内容:
至此我们完成了GLOG-0.6.0的编译安装初步使用。
至于以上代码的解释,我们在下一节中再进行讨论。
有两个问题:
- 小白的编译过程中,少勾选的那三个选项,是不是导致这两个头文件没有被迁移到编译好的文件中去的原因?
- glog.dll和glog.lib是否确实就是libglog.dll和libglog.lib的更名后文件?
对于第1个问题,小白暂时没有去验证,因为没有去安装未勾选的那几项。(后面估计还要去尝试
GTEST的安装和使用,等到那时候再返回来验证。暂时留坑)
对于第2个问题,小白尝试着去找一下github上的更新说明,并未找到实证。结合前人的博客来说,目前只能推测是0.4.0或0.5.0版本时,对导出库的名称进行了变更。这个问题也有待查证。
4 GLOG常用函数及示意
前面不管有没有存在一些问题,总算是能正常使用。
接下来把精力聚焦到GLOG的使用本身。
4.1 GLOG的四级日志
GLOG的日志按严重等级分为四级,由低到高分别是
- INFO(0级)
- WARNING(1级)
- ERROR(2级)
- FATAL(3级)
更高严重等级的日志,会出现在所有比它等级低的日志中。例如,FATAL的日志,不仅会出现在FATAL中,也会出现在ERROR、WARNING、INFO之中。
4.2 GLOG的常用标志位
GLOG中包含众多常用标志位,这些标志位的设置将决定了GLOG的输出行为。如果系统已经安装有GFLAGS库,那么可以使用命令行系统来开启这些常用标志位。例如如果想把--logtostderr
设置成1,则可以在命令行中如此开启:
./your-application --logtostderr=1
但小白前面编译的时候没有安装GFLAGS,所以只能使用环境变量的方式进行开启,这里每一个“环境变量”命名前都加有一个GLOG_
前缀。例如:
GLOG_logtostderr=1 ./your-application
常用的标志位包括:
logtostderr(bool, default=false)
该标志位为true时,将log内容输出到标准输出,对windows系统来说就是命令行。可以通过赋1
,true
或yes
来使能这个标志位,反之,通过赋0
,false
或no
来抑能这个标志位。注意,大小写不限。
stderrthreshold(int, default=2, which is ERROR)
除了向日志文件中复制之外,也将当前级别或以上的日志消息复制到标准输出。严重级别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)
使能后,显示所有小于等于m等级的VLOG(m)
信息。
vmodule(string, default="")
每个模块的详细级别。该参数必须包含逗号分隔的列表<模块名称>=<日志级别>.<模块名称>
是一种glob模式(例如,gfs*
用于名称以gfs
开头的所有模块),与文件名基数匹配(即,名称忽略.cc/.h/-inl.h
)。<日志级别>
将覆盖 --v
给出的任意值 。
logging.cc中还定义了其他一些标志。在源码中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";
4.3 GLOG的常用函数
4.3.1 条件/偶然日志
有时,只需要在特定条件下才输出一条日志信息,这称为条件日志,可以使用以下的Log_IF
宏来实现:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
只有当num_cookies
这个变量达到10以上时,才会在日志文件中输出一条"Got lots of cookies"信息。如果一行代码会执行多次(例如在循环中),则仅按特定的间隔记录信息可能会很有用。这种日志记录对于信息性消息最有用。
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
以上的这行代码使用了LOG_EVERY_N
宏,将会在第1、11、21…次执行时输出一条日志信息。注意上述中的google::COUNTER
值表示的是重复发生的次数,在实际代码中需要用实际能表示次数的变量替换。
也可以把条件和偶然日志通过以下的LOG_IF_EVERY_N
宏方式结合起来使用:
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER<< "th big cookie";
也可以用LOG_FIRST_N
宏限制前n次输出,而不是每n次都输出:
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
上述代码在前20次执行均会输出日志信息。
另外,也可以每间隔特定时间输出一条信息,使用LOG_EVERY_T
宏,以下例子每10ms记录一条日志信息:
LOG_EVERY_T(INFO, 0.01) << "Got a cookie";
或者每2.35s记录一条日志信息:
LOG_EVERY_T(INFO, 2.35) << "Got a cookie";
4.3.2 Debug模式
特殊的“调试模式”使用DLOG
宏,使日志仅在调试模式下起作用,并且在非调试模式编译时被编译为零。使用这些宏可以避免由于过多的日志记录而降低生产应用程序的速度。
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";
4.3.3 CHECK 宏
最好经常检查程序中的预期条件,以便迟早发现错误。CHECK
宏提供了在不满足条件时路上应用程序的功能,类似于标准C库中定义的断言。
如果条件不为真,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消息。这些值必须定义了运算符<<(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_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
接受第三个浮点参数,该参数指定可接受的误差幅度。
以上这段功能和另一个开源库gtest有很大的相似性,都是google出品,建议去补充阅读这些内容。
4.3.4 详细日志
当追查困难的错误时,全面的日志消息非常有用。但是,我们也希望忽略通常开发中过于冗长的消息。对于此类详细日志记录,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时,此宏返回true,用法如下:
if (VLOG_IS_ON(2)) {
// 做一些日志准备和日志,这些操作
// 直接用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;
VLOG_IF(1, (size > 1024))
这一句当size>1024
时,才能以--v=1
或更多来输出一条日志;
VLOG_EVERY_N(1, 10)
这一句每10次执行才输出一次;
VLOG_IF_EVERY_N(1, (size > 1024), 10)
这一句既需要size>1024
,还需要满足10次执行才输出一次。
4.3.5 日志前缀命名
glog支持通过接收用户提供的用于生成此类字符串的回调来更改附加到日志消息前缀的格式。该功能必须在编译时由WITH_CUSTOM_PREFIX
标志启用。
核对了一下,小白编译时这个勾是勾上的,所以小白编译出来的版本,这个功能应该是可以使用。
对于每个日志条目,将使用LogMessageInfo
结构调用回调,该结构包含事件的严重性、文件名、行号、线程ID和时间。它还将获得对输出流的引用,其内容将附加到最终日志行中的实际消息之前。
举例:
/* 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()
,需要在初始化时简单地给glog一个指针:InitGoogleLogging(argv[0], &CustomPrefix);
可选地,InitGoogleLogging()
可以在第三个参数,类型为void*
来在回调函数中传递。
小白试了一下,写了以下代码:
#define GLOG_NO_ABBREVIATED_SEVERITIES
#define GLOG_CUSTOM_PREFIX_SUPPORT
#define NOMINMAX
#include <glog/logging.h>
#include <iostream>
#include <vector>
#include <iomanip>
#define WRITE_LOG(s) (LOG(INFO)<<s)
using namespace std;
void CustomPrefix(std::ostream &s, const google::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 << "]";
}
int main(int argc, char * argv[])
{
FLAGS_log_dir = "../../../log";
google::SetLogFilenameExtension(".log");
//google::InitGoogleLogging("AlgLog");
google::InitGoogleLogging("AlgLog", &CustomPrefix);
WRITE_LOG("google initial log");
google::FlushLogFiles(google::GLOG_INFO);
LOG_EVERY_T(INFO, 0.10) << "haha";
for (int i = 0; i < 101; ++i)
{
LOG_IF(INFO, i > 90) << "it's " << i << "th loop.";
LOG_EVERY_N(INFO, 5) << "it's " << i << "th loop.";
LOG_IF_EVERY_N(INFO, i > 90, 3) << "it's " << i << "th loop.";
}
google::ShutdownGoogleLogging();
system("pause");
return EXIT_SUCCESS;
}
生成的日志前缀变化了:
对比一下之前的日志前缀
可以看到前缀中的年月日格式有所变化。
4.4 GLOG常用技巧
4.4.1 失败信号句柄
GLOG库提供一个方便的信号处理程序,当程序在某些信号(如SIGSEGV)上崩溃时,它将转储有用的信息。信号处理程序可以通过google::InstallFailureSiginalHandler()
安装。以下是信号处理程序的输出示例:
*** 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()自定义目标。
4.4.2 日志信息的性能
GLOG提供的条件日志记录宏(例如,CHECK
、LOG_IF
、VLOG
等)是精心实现的,并且在条件为假时不执行右侧表达式。因此,以下检查可能不会牺牲广泛应用程序的性能。
CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();
4.4.3 用户自定义失败函数
FATAL严重性级别消息或未满足CHECK
条件会终止程序,可以通过InstallFailureFunction
更改终止的行为。
void YourFailureFunction() {
// Reports something...
exit(EXIT_FAILURE);
}
int main(int argc, char* argv[]) {
google::InstallFailureFunction(&YourFailureFunction);
}
默认情况下,GLOG尝试转储堆栈跟踪,并使程序退出状态为1。仅当在GLOG支持堆栈跟踪的体系架构上运行程序时,才会生成堆栈跟踪。(截至2008年9月,GLOG支持x86和x86_64的堆栈跟踪)
4.4.4 原始日志
头文件<glog/raw_logging.h>
可用于线程安全日志记录,该日志记录不分配任何内存或获取任何锁。因此,此头文件中定义的宏可由低级内存分配和同步代码使用。有关详细信息,请查看src/glog/raw_logging.h.in
这个原始日志可以在多线程中安全使用。
4.4.5 剪枝日志语句
日志消息中使用的字符串可能会增加二进制文件的大小并引起隐私问题。因此,可以使用GOOGLE_STRIP_LOG
宏指示glog删除所有低于特定严重性级别的字符串:
只需要采用如下的应用程序:
#define GOOGLE_STRIP_LOG 1 // 要定义在 #include之前!
#include <glog/logging.h>
编译器将删除严重性小于指定整数值的日志消息。由于VLOG记录的严重性级别为INFO(数值0),因此将GOOGLE_STRIP_LOG
设置为1或更大会删除与VLOG关联的所有日志消息以及INFO日志语句。
4.4.6 自动移除旧日志
这个功能非常实用,小白也遇到过日志文件过大,造成旧日志严重冗余的情况。
若要使能日志清除器:
google::EnableLogCleaner(3); // 表示能保留3天的日志
然后,每当执行刷新时,glog都会检查是否有过期的日志。在此示例中,项目中上次修改时间大于3天的任何日志文件都将被取消链接。
如果已启用这个功能,则可以随时禁用此功能:
google::DisableLogCleaner();
4.4.7 Windows用户注意
GLOG定义了一个严重性级别ERROR,该级别也在windows.h中定义。可以通过在包含glog/logging.h之前定义GLOG_NO_ABBREVIATED_SEVERITIES
来使GLOG不定义INFO、WARNING、ERROR和FATAL。即使使用此宏,仍然可以像日志记录工具一样使用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中定义的函数使用INFO、WARNING、ERROR和FATAL,必须加前缀GLOG_
#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,则还有一些有时不起作用的解决方法:
#define WIN32_LEAN_AND_MEAN
orNOGDI
必须要在#include <windows.h>
之前#undef ERROR
必须在#include <windows.h>
之后
4.5 小白自己的代码示例
虽然GLOG很强大,有上面这么多的功能,但对于像小白这样的“初级程序员”来说还没有那么多的使用场景, 一次也消化不了这么多内容。暂且用一个HelloWorld级别难度的代码示例来作为学习的一个结束:
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include <glog/logging.h>
#include <iostream>
#include <vector>
#define WRITE_LOG(s) (LOG(INFO)<<s)
using namespace std;
int main(int argc, char * argv[])
{
FLAGS_log_dir = "../../../log";
google::SetLogFilenameExtension(".log");
google::InitGoogleLogging("AlgLog");
WRITE_LOG("google initial log");
google::FlushLogFiles(google::GLOG_INFO);
LOG_EVERY_T(INFO, 0.10) << "haha";
for (int i = 0; i < 101; ++i)
{
LOG_IF(INFO, i > 90) << "it's " << i << "th loop.";
LOG_EVERY_N(INFO, 5) << "it's " << i << "th loop.";
LOG_IF_EVERY_N(INFO, i > 90, 3) << "it's " << i << "th loop.";
}
google::ShutdownGoogleLogging();
system("pause");
return EXIT_SUCCESS;
}
运行后,日志输出的结果为
Log file created at: 2022/06/18 14:36:07
Running on machine: JACOBCHEN-PC
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20220618 14:36:07.086558 14432 testMain.cpp:14] google initial log
I20220618 14:36:07.088560 14432 testMain.cpp:20] it's 0th loop.
I20220618 14:36:07.088560 14432 testMain.cpp:20] it's 5th loop.
I20220618 14:36:07.088560 14432 testMain.cpp:20] it's 10th loop.
I20220618 14:36:07.088560 14432 testMain.cpp:20] it's 15th loop.
I20220618 14:36:07.088560 14432 testMain.cpp:20] it's 20th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 25th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 30th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 35th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 40th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 45th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 50th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 55th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 60th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 65th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 70th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 75th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 80th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 85th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 90th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 91th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:21] it's 91th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 92th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 93th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 94th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:21] it's 94th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 95th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 95th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 96th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 97th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:21] it's 97th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 98th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 99th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:19] it's 100th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:20] it's 100th loop.
I20220618 14:36:07.089558 14432 testMain.cpp:21] it's 100th loop.
LOG_EVERY_N(INFO, 5) << "it's " << i << "th loop.";
这一句,从第0次循环开始,每5个循环输出一条日志。
LOG_IF(INFO, i > 90) << "it's " << i << "th loop.";
这一句,当i>90
开始,每次循环输出一条日志,所以i为95和100各多出一条日志。
LOG_IF_EVERY_N(INFO, i > 90, 3) << "it's " << i << "th loop.";
这一句,当i>90
时,每3次循环输出一条日志,所以i为91 94 97 100时各多出一条日志。
5 结语
至此,耳东小白完成了C++工程领域的通用日志工具google/GLOG的初步入门学习。在Windows10平台下结合Visual Studio和CMake工具进行了编译和初步使用。这一过程,只能说是从入门到入门,但已能满足编程过程中的部分基础需求。
此日志工具还有很多特性有待学习和研究。这取决于今后在工程实践中的继续探索。
【水平所限,难免错漏】
【创作不易,轻喷勿骂】