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模块安装和基本使用(ubuntu)环境

    1,如何安装 1 Git clone https://github.com/google/glog.git 2 cd glog 3 ./autogen.sh 4 ./configure --prefi ...

  2. google glog 使用方法

    #include <glog/logging.h> int main(int argc,char* argv[]) { google::ParseCommandLineFlags(& ...

  3. google::Glog

    windows下使用google的Glog库 下载glog-.tar.gz,解压. vs2013打开工程, 有四个项目 libglog libglog_static logging_unittest ...

  4. Google glog error LNK2001: unresolved external symbol "__declspec(dllimport) int fLI::FLAGS_XXXX 错误的解决。

    想在 windows 下使用 glog,使用类似 FLAGS_max_log_size 来设置参数,结果编译报错. 解决办法是在 项目属性 -> C/C++ -> Preprocessor ...

  5. windows和linux环境下使用google的glog日志库

    一.概述 glog是google推出的一款轻量级c++开源日志框架,源码在github上,目前最新release版本是v0.3.5. githut地址:https://github.com/googl ...

  6. glog使用

    How To Use Google Logging Library Glog 的基本使用方法在google code上有介绍:How To Use Google Logging Library ;最好 ...

  7. glog 使用

    glog 使用 来源:http://www.cnblogs.com/tianyajuanke/archive/2013/02/22/2921850.html 一.安装配置 1.简介 google 出的 ...

  8. GLOG使用Demo

    GLOG使用Demo GLOG是Google开源的一个精简的日志系统,博主简单学习了一下并记录常见用法,以备日常查询 一.安装 照例是编译安装,不过没有使用cmake git clone https: ...

  9. glog日志库使用笔记

    日志能方便地诊断程序原因.统计程序运行数据,是大型软件系统必不可少的组件之一.glog 是google的开源日志系统,相比较log4系列的日志系统,它更加轻巧灵活. 在Github上下载glog,解压 ...

随机推荐

  1. leetcode 141. Linked List Cycle ----- java

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  2. leetcode 103 Binary Tree Zigzag Level Order Traversal ----- java

    Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to ...

  3. Ci分开配置网站前台后台的方法

    CodeIgniter 是一个简单快速的PHP MVC框架.EllisLab 的工作人员发布了 CodeIgniter.许多企业尝试体验过所有 PHP MVC 框架之后,CodeIgniter 都成为 ...

  4. mysql5.6启动占用内存很大的解决方法

    vps的内存为512M,安装好nginx,php等启动起来,mysql死活启动不起来看了日志只看到对应pid被结束了,后跟踪看发现是内存不足被killed; 调整my.cnf 参数,重新配置(系统默认 ...

  5. 关于正则表达式处理textarea里的换行

    将textarea里的内容存入数据库时,会自动将回车换行符过滤成空格,也会将多个空格转换成一个空格,即:将\n等换成 “  ”存入数据库 因此为了将内容从数据库中按照原来格式读出写入到html 就必须 ...

  6. linux内核启动流程[转]

    启动流程一览 既然启动是很严肃的一件事,那我们就来了解一下整个启动的过程吧! 好让大家比较容易发现启动过程里面可能会发生问题的地方,以及出现问题后的解决之道! 不过,由於启动的过程中,那个启动管理程序 ...

  7. shell下的作业管理[转]

    作业管理 举例来说,我们在登陆 bash 后, 想要一边复制文件.一边进行数据搜寻.一边进行编译,还可以一边进行 vi 程序撰写! 当然我们可以重复登陆那六个文字介面的终端机环境中,不过,能不能在一个 ...

  8. thinkphp3.2 学习

    http://www.tuicool.com/articles/nQFnQrR 1,sublime text 增强插件 右键可以打开文件目录 http://www.w3cfuns.com/notes/ ...

  9. JSBinding + SharpKit / 原理篇:内存管理与垃圾回收

    C# 和 JS 都有垃圾回收机制,需要保证 2 者能够分工协作. 类对象 类在C#中是引用类型.我们在 C# 中维护了2个map,保存 C# 对象和 JS 对象的一一对应关系. 举一个例子,看以下代码 ...

  10. Python-正则零宽断言及命名捕获(类PHP)

    (一)零宽断言 说明:本文的例子使用python描述      首先说明一下什么是零宽断言,所谓零宽断言就是并不去真正的匹配字符串文本,而仅仅是匹配对应的位置.      正则表达式中有很多这样的断言 ...