Google glog 使用

1        简介

Googleglog 库实现了应用级的日志记录,提供了C++ 风格的流操作和各种助手宏。

代码示例:

#include <glog/logging.h>

int _tmain(int argc,_TCHAR* argv[])

{

google::InitGoogleLogging((const char *)argv[0]);

google::SetLogDestination(google::GLOG_INFO, "./myInfo");

LOG(WARNING) << "thisis the 1st warning!";

return 0;

}

“LOG”宏为日志输出关键字,“INFO”为严重性程度。

主要支持功能如下:

1) 参数设置,以命令行启动参数的方式设置标志参数来控制日志记录行为;

2) 严重性分级,根据日志严重性分级记录日志;

3) 可有条件地记录日志信息;

4) 条件中止程序。丰富的条件判定宏,可预设程序终止条件;

5) 异常信号处理。程序异常情况,可自定义异常处理过程;

6) 支持debug功能。可只用于debug模式;

7) 自定义等级日志信息;

8) 原始日志记录。无需加锁与动态分配内存的轻量级线程安全版本;

9) 系统日志记录;

10) google perror风格日志信息;

11) 日志信息移除。

glog的使用是比较简单的,几乎可以不用配置就直接使用了。在配置方式上,glog和一般的日志系统(如log4系列)是不太一样的,后者一般使 用配置文件, 而glog是在命令行参数中指定的。对比优缺点,配置文件做的配置可能更加强大一些, 不过命令行配置虽然简单但是也不失灵活。

2      严重性分级

glog可通过根据指定的严重性等级,来选择性记录日志。日志信息严重性等级按由低到高排列依次为:INFO,WARNING, ERROR, 和 FATAL四级。使用者可以在命令行中设置严重性等级门限值来控制日志的输出,详细见“参数设置”部分的“minloglevel”标志值的介绍。

其中FATAL等级的日志会在记录以后终止程序运行,要谨慎使用。

3      日志输出宏

这里我们以一条最简单的日至输出为例说明:

LOG(WARNING) << "Thisis a warning message";

这里LOG是一个宏,其定义如下(logging.hline 487):

#define LOG(severity) COMPACT_GOOGLE_LOG_ ##severity.stream()

这里根据LOG宏中的severity的不同有分别扩展成了另外四个宏,其中severity有四个预定义(log_severity.h  line 51-59),分别代表不同级别的日志输出,有INFO、WARNING、ERROR、FATAL,以WARNING为例,LOG(WARNING)被扩 展为COMPACT_GOOGLE_LOG_WARNING.stream()。其中COMPACT_GOOGLE_LOG_WARNING又是另外一个 宏(logging.hline 391):

#if GOOGLE_STRIP_LOG <= 1

#define COMPACT_GOOGLE_LOG_WARNINGgoogle::LogMessage( \

__FILE__, __LINE__, google::GLOG_WARNING)

#define LOG_TO_STRING_WARNING(message)google::LogMessage( \

__FILE__, __LINE__, google::GLOG_WARNING, message)

#else

#define COMPACT_GOOGLE_LOG_WARNINGgoogle::NullStream()

#define LOG_TO_STRING_WARNING(message)google::NullStream()

#endif

到这里基本就能看出门道了,google::LogMessage和google::NullStream都是类,根据 GOOGLE_STRIP_LOG的不同定义,COMPACT_GOOGLE_LOG_WARNING被定义为LogMessage或者 NullStream,NullStream比较简单,从名字上也能测到它就是一个无输出的流(仅仅重载了operator<<,但实际上并 不输出任何信息),用以实现某些level的日志信息不被显式输出)。这里主要看LogMessage。

此时根据文件名,行号, 日志级别构造一个LogMessage类对象(logging.ccline 1153):

LogMessage::LogMessage(const char* file, int line,LogSeverity severity) :allocated_(NULL)

{

Init(file, line,severity, &LogMessage::SendToLog);

}

LogMessage有很多重载构造,这里不再一一列举了。注意构造里的初始化函数Init,除了文件名,行号, 日志级别,还多了一个参数,Init声明如下:

void Init(const char* file, int line,LogSeverity severity, void (LogMessage::*send_method)());

即最后一个参数是一个函数指针,且可配置,用以设置真正的日志输出,比如输出到文件、控制台等,甚至有可能配置成输出到远程网络端。Init内部用以初始化日志输入的流缓冲区,初始化日志创建时间,格式,确定打印日志文件名等等。

此时一个完整的LogMessage的对象就创建并初始化完成了,回到LOG(severity)宏定义处,此时LOG宏可以表示成如下定义:

#define LOG(severity) google::LogMessage().stream()

也即是最终被展开为google::LogMessage类的成员函数stream()的返回值,stream()实现如下:

std::ostream&LogMessage::stream()

{

returndata_->stream_;

}

data_->stream_是一个LogStream对象,其定义如下:

class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream

{

public:

LogStream(char *buf, int len, int ctr);

//..............此处省略

private:

base_logging::LogStreamBufstreambuf_;

int ctr_;  // Counter hack (for theLOG_EVERY_X() macro)

LogStream *self_;  // Consistency check hack

};

上面所提及的google::NullStream即是继承自LogStream,所以也是一个std::ostream对象。

至此一个日志输出语句,

LOG(WARNING) << "Thisis a warning message";

即可以表示为:

google:: LogStream() << "Thisis a warning message";

到这里就会发现这个和我们熟悉的cout输出是一样的了:

std::cout << "Thisis a warning message";

一个google::LogStream对象和std::cout都是std::ostream对象。

从上面也可以看出,每一次输出一条日志信息都要创建一个google::LogMessage对象,在每次输出结束后释放LogMessage对象,在其析构函数中有如下代码:

LogMessage::~LogMessage() {

Flush();

deleteallocated_;

}

Flush成员函数即是刷新日志缓存区,相当于C++中流操作的flush或者C中文件操作的fflush。另外注意Flush实现里有如下代码:

//......

{

MutexLockl(&log_mutex);

(this->*(data_->send_method_))();

++num_messages_[static_cast<int>(data_->severity_)];

}

//......

这是为了保证多个日志同时向同一介质进行输出时到保持有序。注意锁的使用前后有{}包围。呵呵,这种用法其实我也偶尔使用,好处就是在一个比较大的 语句块中创建一个作用域更小的对象,这样能使该对象及早释放,避免和整个语句块使用同一作用域。比如上面代码中的在加锁时使用了一个更小的作用域,该作用 域结束后锁就会立刻释放,而不是等到Flush函数返回时才释放,这样就进一步提高了响应时间。

4      条件输出

LOG_IF(INFO,num_cookies > 10) << "Gotlots of cookies"; //当条件满足时输出日志

LOG_EVERY_N(INFO, 10) << "Gotthe " << google::COUNTER<< "thcookie"; //google::COUNTER记录该语句被执行次数,从1开始,在第一次运行输出日志之后,每隔 10 次再输出一次日志信息

LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Gotthe " << google::COUNTER<< "th bigcookie"; //上述两者的结合,不过要注意,是先每隔 10 次去判断条件是否满足,如果滞则输出日志;而不是当满足某条件的情况下,每隔 10 次输出一次日志信息。

LOG_FIRST_N(INFO, 20) << "Gotthe " << google::COUNTER<< "thcookie"; //当此语句执行的前 20 次都输出日志,然后不再输出

演示代码如下:

#include <glog/logging.h>

int main(int argc,char* argv[])

{

google::InitGoogleLogging(argv[0]);

FLAGS_stderrthreshold=google::INFO;

FLAGS_colorlogtostderr=true;

for(int i = 1; i <= 100;i++)

{

LOG_IF(INFO,i==100)<<"LOG_IF(INFO,i==100)  google::COUNTER="<<google::COUNTER<<"  i="<<i;

LOG_EVERY_N(INFO,10)<<"LOG_EVERY_N(INFO,10)  google::COUNTER="<<google::COUNTER<<"  i="<<i;

LOG_IF_EVERY_N(WARNING,(i>50),10)<<"LOG_IF_EVERY_N(INFO,(i>50),10)  google::COUNTER="<<google::COUNTER<<"  i="<<i;

LOG_FIRST_N(ERROR,5)<<"LOG_FIRST_N(INFO,5)  google::COUNTER="<<google::COUNTER<<"  i="<<i;

}

google::ShutdownGoogleLogging();

}

5      日志类型

LOG    //内置日志

VLOG    //自定义日志

DLOG    //DEBUG模式可输出的日志

DVLOG    //DEBUG模式可输出的自定义日志

SYSLOG    //系统日志,同时通过 syslog() 函数写入到/var/log/message 文件

PLOG    //perror风格日志,设置errno状态并输出到日志中

RAW_LOG       //线程安全的日志,需要#include <glog/raw_logging.h>

  前六种的日志使用方法完全相同(包括条件日志输出),而RAW_LOG 使用方法比较特殊,且不支持条件日志输出,另外不接受colorlogtostderr 的颜色设置。自定义日志也不接受 colorlogtostderr的颜色设置,另外其日志严重级别也为自定义数字,且与默认日志严重级别相反,数字越小 严重级别越高。如:

#include <glog/logging.h>

#include <glog/raw_logging.h>

class GLogHelper

{

public:

GLogHelper(char*program)

{

google::InitGoogleLogging(program);

FLAGS_stderrthreshold = google::INFO;

FLAGS_colorlogtostderr = true;

FLAGS_v = 3;

}

~GLogHelper()

{

google::ShutdownGoogleLogging();

}

};

int main(int argc,char* argv[])

{

GLogHelpergh(argv[0]);

LOG(ERROR) <<"LOG";

VLOG(3) <<"VLOG";

DLOG(ERROR) <<"DLOG";

DVLOG(3) <<"DVLOG";

SYSLOG(ERROR) <<"SYSLOG";

PLOG(ERROR) <<"PLOG";

RAW_LOG(ERROR,"RAW_LOG");

}

6      CHECK 宏

当通过该宏指定的条件不成立的时候,程序会中止,并且记录对应的日志信息。功能类似于ASSERT,区别是 CHECK 宏不受 NDEBUG 约束,在 release 版中同样有效。

我个人感觉这类CHECK_XX宏比上面的LOG宏实现的还要隐晦难懂,当然设计的还是很巧妙的,值得学习一下,我尝试做个分析。

在测试工程的logging_unittest.cc文件line535的TestCHECK()函数中有如下代码:

CHECK_NE(1, 2);

CHECK_GE(1, 1);

CHECK_GE(2, 1);

CHECK_LE(1, 1);

CHECK_LE(1, 2);

定义如下:

#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==,val1, val2)

#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=,val1, val2)

#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=,val1, val2)

#define CHECK_LT(val1, val2) CHECK_OP(_LT, < ,val1, val2)

#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=,val1, val2)

#define CHECK_GT(val1, val2) CHECK_OP(_GT, > ,val1, val2)

其中CHECK_OP宏定义如下:

#define CHECK_OP(name, op, val1, val2) \

CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal)

而CHECK_OP_LOG宏定义如下:

typedef std::string_Check_string;

#define CHECK_OP_LOG(name, op, val1, val2,log)          \

while(google::_Check_string* _result =               \

google::Check##name##Impl(                      \

google::GetReferenceableValue(val1),        \

google::GetReferenceableValue(val2),        \

#val1 " " #op " " #val2))                   \

log(__FILE__, __LINE__,                              \

google::CheckOpString(_result)).stream()

接下来我们以CHECK_LE(1,2);为例,将其逐步扩展:

CHECK_LE(1, 2)

------>CHECK_OP(_LE, <=, 1, 2)

------>CHECK_OP_LOG(_LE, <=, 1, 2,google::LogMessageFatal)

------>#define CHECK_OP_LOG(_LE, <=, 1, 2,google::LogMessageFatal)                         \

while (std::string* _result =  \

google::Check_LEImpl(    \

1,                   \

2,                   \

"1<= 2"))           \

log(__FILE__,__LINE__,       \

google::CheckOpString(_result)).stream()

其中google::Check_LEImpl也是通过宏预先实现的,这个宏就是DEFINE_CHECK_OP_IMPL(Check_LE,<=):

#define DEFINE_CHECK_OP_IMPL(name, op) \

template<typename T1, typename T2>  \

inlinestd::string* name##Impl(const T1& v1, const T2& v2,    \

const char*exprtext) {  \

if(GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \

elsereturn MakeCheckOpString(v1, v2, exprtext); \

} \

inlinestd::string* name##Impl(int v1, int v2, const char* exprtext) { \

returnname##Impl<int, int>(v1, v2, exprtext); \

}

展开后就实现了google::Check_LEImpl函数(其他与此类似,这里只以“<=”为例说明):

CHECK_LE(1, 2) ------>

while (std::string* _result =google::Check_LEImpl(1, 2, "1<= 2"))

log(__FILE__,__LINE__,google::CheckOpString(_result)).stream()

其中google::Check_LEImpl又调用了模板实现的Check_LEImpl,该函数根据两个参数v1、v2和操作符op决定了要么返回NULL,要么返回一个string*,如果返回NULL,则不再执行下面的输出,否则则输出日志信息。

至此,就完成了CHECK_LE(1,2)的扩展,如果检测为true,则返回NULL,否则就会返回一个有明确提示信息的字符串指针,并输出该信息,然后是程序宕掉。

7      core dumped

通过 google::InstallFailureSignalHandler();即可注册,将 coredumped 信息输出到 stderr,如:

#include <glog/logging.h>

#include <string>

#include <fstream>

//将信息输出到单独的文件和LOG(ERROR)

void SignalHandle(const char* data, int size)

{

std::ofstreamfs("glog_dump.log",std::ios::app);

std::stringstr = std::string(data,size);

fs<<str;

fs.close();

LOG(ERROR)<<str;

}

class GLogHelper

{

public:

GLogHelper(char*program)

{

google::InitGoogleLogging(program);

FLAGS_colorlogtostderr=true;

google::InstallFailureSignalHandler();

//默认捕捉 SIGSEGV 信号信息输出会输出到stderr,可以通过下面的方法自定义输出方式:

google::InstallFailureWriter(&SignalHandle);

}

~GLogHelper()

{

google::ShutdownGoogleLogging();

}

};

void fun()

{

int* pi = new int;

delete pi;

pi = 0;

int j = *pi;

}

int main(int argc,char* argv[])

{

GLogHelpergh(argv[0]);

fun();

}

  如果不使用 google::InstallFailureSignalHandler();  则只会输出“段错误” 三个字,难于排查。

8      其它常用配置

google::SetLogDestination(google::ERROR,"log/prefix_");   //第一个参数为日志级别,第二个参数表示输出目录及日志文件名前缀。

google::SetStderrLogging(google::INFO); //输出到标准输出的时候大于 INFO级别的都输出;等同于 FLAGS_stderrthreshold=google::INFO;

FLAGS_logbufsecs =0; //实时输出日志

FLAGS_max_log_size =100; //最大日志大小(MB)

#define GOOGLE_STRIP_LOG 3    // 小于此级别的日志语句将在编译时清除,以减小编译后的文件大小,必须放在#include 前面才有效。

9      日志文件说明

  如果可执行文件名为"test",则将日志输出到文件后,还会生成 test.ERROR,test.WARNING,test.INFO三个链接文件,分别链接到对应级别的日志文件。如果日志输出超过 FLAGS_max_log_size 设置的大小,则会分为多个文件存储,链接文件就会指向其中最新的对应级别的日志文件。所以当日志文件较多时,查看链接文件来查看最新日志挺方便的。

10 增加日志按天输出

glog默认是根据进程ID是否改变和文件大小是否超过预定值来确定是否需要新建日志文件的,此处可以参考glog源码logging.cc 文件中的 voidLogFileObject::Write 函数中

if (static_cast<int>(file_length_>> 20) >=MaxLogSize() ||

PidHasChanged()) {

我们只需要在此处加一个日期判断就可以了,PidHasChanged()定义于utilities.cc 文件中,可以加一个类似的DayHasChanged() 函数(注意 utilities.h文件中加上函数声明):

static int32 g_main_day = 0;

bool DayHasChanged()

{

time_traw_time;

struct tm*tm_info;

time(&raw_time);

tm_info =localtime(&raw_time);

if (tm_info->tm_mday!= g_main_day)

{

g_main_day = tm_info->tm_mday;

return true;

}

return false;

}

再修改 voidLogFileObject::Write 函数中的判断条件即可:

if (static_cast<int>(file_length_>> 20) >=MaxLogSize() ||

PidHasChanged() ||DayHasChanged()) {

Google glog 使用相关推荐

  1. 【C++】google glog详解

    0.简介 glog是google的日志管理系统,配合gflags库,通过命令行参数管理日志. 源码下载:https://github.com/google/glog ubuntu安装: sudo ap ...

  2. Google Glog使用

    Glog是Google的一个C++开源日志系统,轻巧灵活,入门简单,而且功能也比较完善. 安装 以下是官方的安装方法 git clone https://github.com/google/glog. ...

  3. 在windows下基于visual studio2017和CMake的安装Google glog

    这里简单记录安装google glog在windows下基于visual studio2017的安装过程. 下载 https://github.com/google/glog 使用cmake编译,编译 ...

  4. google glog使用指南

    Google Logging (glog) 是一个 C++98 库,用于实现应用程序级日志记录.该库提供了基于 C++ 风格流和各种帮助宏的日志 API. 入门 您可以通过简单地将内容流式传输到LOG ...

  5. Google glog

    本文是根据自己的理解翻译组织了glog的manual,鉴于自身的理解能力和英语水平,可能存在谬误,欢迎大家指出!英文原文见 http://google-glog.googlecode.com/svn/ ...

  6. Google/glog

    glog简介: google 出的一个C++轻量级日志库,支持以下功能: ◆ 参数设置,以命令行参数的方式设置标志参数来控制日志记录行为: ◆ 严重性分级,根据日志严重性分级记录日志: ◆ 可有条件地 ...

  7. caffe中LOG(INFO) DLOG(INFO)介绍(统称Google glog)

    Original url: http://blog.51cto.com/mengjh/546766    本文是根据自己的理解翻译组织了glog的manual,鉴于自身的理解能力和英语水平,可能存在谬 ...

  8. google glog简介

    本文是根据自己的理解翻译组织了glog的manual,鉴于自身的理解能力和英语水平,可能存在谬误,欢迎大家指出!英文原文见 http://google-glog.googlecode.com/svn/ ...

  9. google/glog 使用

    1 安装 $sudo apt-get install libgoogle-glog-dev 2 使用 //将信息输出到单独的文件和 LOG(ERROR) #include <glog/loggi ...

最新文章

  1. python编程案例教程答案-Python基础案例教程
  2. C# 部署失败--上传文件缺失
  3. b区计算机复试国家线,考研国家线/自主划线/a区b区线/专业线这些考研复试分数线你能分清吗?...
  4. Python 装饰器工作原理解析
  5. SAP云平台上的Low Code Development(低代码开发)解决方案
  6. 为什么用python画图_为什么使用Matplotlib绘图太慢?
  7. TP框架多上传域上传图片
  8. L1-047 装睡-PAT团体程序设计天梯赛GPLT
  9. access如何保存小数点后_如何设置石材雕刻机原点及断电后怎么保存原点坐标?...
  10. kali linux系统介绍
  11. C#之Dispose
  12. 计算机上的无线网络开关怎么打开,联想笔记本无线网络开关怎么打开
  13. OpenCV入门学习笔记之常用的图像处理操作
  14. 多条ADSL线路合并叠加效果测试,多WAN口聚合
  15. 微信公众号的简单常识
  16. 微信小程序 java音乐播放器系统python php
  17. 防浪涌启动电路,防止插入12V电源有火花
  18. 郭盛华公司211万收购AI域名, 只为抢占人工智能市场
  19. 托福备考建议以及资料推荐
  20. 若腾网络大站协议邮件群发软件不换ip不用小号

热门文章

  1. 计算机未来发展8百字征文,计算机和人类作文800字
  2. 12星座攻心策----白羊女
  3. Wi-Fi列表总有TP-LINK?这个神秘品牌到底啥来路
  4. 微信云开发使用教程,实现世界排行榜等功能
  5. 数仓(三)简析阿里、美团、网易、恒丰银行、马蜂窝5家数仓分层架构
  6. Java初级工程师必读的书籍
  7. mt6765和骁龙665哪个好_骁龙450和联发科P22和哪个好?高通骁龙450与MT6762区别对比[多图]...
  8. Camera中的拉风箱问题是什么?
  9. 柠檬班python自动化百度云_带柠字好听的名字大全
  10. 七大设计原则与设计模式(创建型模式、结构型模式、行为型模式)