GLOG从入门到入门

GLOG从入门到入门google 日志系统 glog 库的说明 glog

大家好,欢迎来到IT知识分享网。

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.

2 用CMake编译GLOG-0.6.0

3 在C++项目中引入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文件,可以生成日志:
在这里插入图片描述


有两个问题:

  1. 小白的编译过程中,少勾选的那三个选项,是不是导致这两个头文件没有被迁移到编译好的文件中去的原因?
  2. glog.dll和glog.lib是否确实就是libglog.dll和libglog.lib的更名后文件?

4 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 

stderrthreshold(int, default=2, which is ERROR)
除了向日志文件中复制之外,也将当前级别或以上的日志消息复制到标准输出。严重级别INFOWARNINGERRORFATAL的值分别是0、1、2、3。

minloglevel(int, default=0, which is INFO)
输出此级别或更高级别的日志信息。严重级别INFOWARNINGERRORFATAL的值分别是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"; 
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_EQCHECK_NECHECK_LECHECK_LTCHECK_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_STREQCHECK_STRNECHECK_STRCASEEQCHECK_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==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 

这句控制代码将可以:

  1. mapreduce.{h,cc}文件中将打印VLOG(2)和更低级别的消息;
  2. file.{h,cc}文件中将打印VLOG(1)和更低级别的消息;
  3. 前缀带有”gfs”的文件中将打印VLOG(3)和更低级别的消息;
  4. 其他文件中打印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)) << "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  (unix time) try "date -d @" if you are using GNU date * * SIGSEGV (@0x0) received by PID 17711 (TID 0x7fa6f0) 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提供的条件日志记录宏(例如,CHECKLOG_IFVLOG等)是精心实现的,并且在条件为假时不执行右侧表达式。因此,以下检查可能不会牺牲广泛应用程序的性能。

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天的日志 
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 or NOGDI 必须要在#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 I 14:36:07.086558 14432 testMain.cpp:14] google initial log I 14:36:07.088560 14432 testMain.cpp:20] it's 0th loop. I 14:36:07.088560 14432 testMain.cpp:20] it's 5th loop. I 14:36:07.088560 14432 testMain.cpp:20] it's 10th loop. I 14:36:07.088560 14432 testMain.cpp:20] it's 15th loop. I 14:36:07.088560 14432 testMain.cpp:20] it's 20th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 25th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 30th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 35th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 40th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 45th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 50th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 55th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 60th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 65th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 70th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 75th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 80th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 85th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 90th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 91th loop. I 14:36:07.089558 14432 testMain.cpp:21] it's 91th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 92th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 93th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 94th loop. I 14:36:07.089558 14432 testMain.cpp:21] it's 94th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 95th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 95th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 96th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 97th loop. I 14:36:07.089558 14432 testMain.cpp:21] it's 97th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 98th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 99th loop. I 14:36:07.089558 14432 testMain.cpp:19] it's 100th loop. I 14:36:07.089558 14432 testMain.cpp:20] it's 100th loop. I 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 结语

在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/135944.html

(0)
上一篇 2025-06-30 22:20
下一篇 2025-06-30 22:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信