logger(三):其他log库的介绍(&glog的用法)

Glog等一些log库

 

在写代码的过程中,打log肯定是少不了的,毕竟不能总靠调试来发现问题。我们可以试着使用自己写一些log库,也可以使用现成的log库,成熟的log库非常多,log4cpp、log4cxx、poco.log、boost.log、glogsyslog等等.

log4cpp: 开源的C++类库:提供在C++中使用日志和跟踪调试功能,使用log4cpp,可以很便利地将日志或者跟踪调试信息写入字符流、内存字符串队列、文件、回滚文件、调试器、Windows日志、本地syslog和远程syslog服务器中。Log4cpp是个基于LGPL的开源项目,移植自Java的日志处理跟踪项目log4j,并保持了API上的一致。其类似的支持库还包括Java(log4j)C++(log4cpplog4cplus)C(log4c)python(log4p)等。

参考网址:http://blog.csdn.net/kingskyleader/article/details/7320826

 

Log4cxx:log4cxx是Java社区著名的log4j的c++移植版,用于为C++程序提供日志功能,以便开发者对目标程序进行调试和审计,log4cxx是apache软件基金会的开源项目,基于APR实现跨平台支持。

参考网址:http://blog.csdn.net/fhxpp_27/article/details/8284650

 

Poco.log: 可以参考一下poco框架,还是挺屌的 网址:http://www.libpoco.com/site/feature/中说日志部分:可扩展的日志框架,可拔插的日志通道和消息格式化;日志可输出到终端,日志文件,syslog,远程syslog,Windows事件服务

参考网址:http://blog.csdn.net/jmppok/article/details/25598483

 

Boost.log:参考boost库中的log部分,基本上用法和上面大同小异。

       参考网址:http://blog.csdn.net/flyfish1986/article/details/40832447

 

Glog:来自于google,google提供给大家不少好东西,所以功能和质量上都是可以保证的。而且很轻量,用起来很灵活。

glog项目地址:https://code.google.com/p/google-glog/

glog的文档:英文 http://google-glog.googlecode.com/svn/trunk/doc/glog.html

 

一:概述:

      Google glog是一个基于程序级记录日志信息的c++库,编程使用方式与c++的stream操作类似,例:

       LOG(INFO) << "Found " << num_cookies << " cookies";

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

主要支持功能:

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

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

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

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

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

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

7, 自定义日志信息;

8, 线程安全日志记录方式;

9, 系统级日志记录;

10, google perror风格日志信息;

11, 精简日志字符串信息。

 

.   功能及使用方法

   1.1  参数设置

       可通过命令行方式设置glog的标志参数,用来控制日志记录行为。

命令格式如下:

      标志名1 = 标志值 标志名2=标志值 …… 标志值n=标志值        ./程序名

所有标志名需添加统一前缀—“GLOG_”,不同标志语句之间以空格相隔;

例如:

-- GLOG_vmodule=mapreduce=2,file=1,gfs*=3  -- GLOG_v=0 ./application.exe

常用标志参数类型及其作用说明:

标志参数

类型

作用

logtostderr

bool

值为true的时候,日志信息输出到stderr,并非文件。默认值为 false。

stderrthreshold

int

严重性级别在该门限值以上的日志信息除了写入日志文件以外,还要输出到stderr。各严重性级别对应的数值:INFO—0,WARNING—1,ERROR—2,FATAL—3

默认值为2.

minloglevel

int

严重性级别在该门限值以上的日志信息才进行记录。

默认值为0.

log_dir

string

日志信息记录路径。默认为空,如果没有指定信息输出到stderr,则信息保存在"/tmp/<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>"文件中。 (e.g., "/tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474").

v

int

对于使用“ VLOG(m)”(m为int型)表达式进行输出的日志信息,只在m的值小于该标志的值的时候,才进行输出。另外, 该设置可能被 vmodule标志给覆盖.默认为0.

vmodule

string

分模块(文件)设置VLOG(m)日志信息的输出基本。命令格式为以逗号分开的“<module name>=<log level>”表达式组成。其中<module name> 是“glob pattern”,支持通配符,<module name>不包括文件的扩展名(.h,.cc等)。

1标志说明

还有其他的标志参数定义在logging.cc,可在文件中搜索“DEFINE_”来进行查看。

 

1.2     严重性分级记录信息

glog可通过根据指定的严重性等级,来选择性记录日志。日志信息严重性等级按由低到高排列依次为:INFO,WARNING, ERROR, 

和 FATAL四级。使用者可以在命令行中设置严重性等级门限值来控制日志的输出,详细见“参数设置”部分的“minloglevel”标志值的介绍。

 

1.3     有条件地记录日志信息

glog可以控制日志信息在指定条件下进行记录。具体使用如下:

1, LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";

上面的语句表示,只有当num_cookies > 10条件成立时,“Got lots of cookies”日志信息才被记录。

2, LOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie";

上面的语句表示,在程序中周期性的记录日志信息,在该语句第1、11、21……次被执行的时候,记录日志信息。COUNTER变量表示该语句被执行的次数。

3, LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER<< "th big cookie";

上面的语句为1,2项功能的合并,size>1024的条件连续成立10次的时候记录日志信息。COUNTER变量表示该条件成立的次数。

4, LOG_FIRST_N(INFO, 20) << "Got the " << COUNTER << "th cookie";

上面的语句表示,当该语句只在首次执行了20次以后记录日志信息, COUNTER变量表示该语句被执行的次数。

 

1.4     有条件地中止程序

       glog提供了CHECK宏,用于在调试地时候中止程序,及早发现程序错误。当通过该宏指定的条件不成立的时候,程序会中止,并且记录对应的日志信息。功能类似于ASSERT,区别是CHECK宏不受NDEBUG约束,在release版中同样有效。具体使用如下:

 CHECK(fp->Write(x) == 4) << "Write failed!";

    fp->Write(x) == 4成立时,记录“Write failed!”日志信息,并且中止程序,其中fp->Write(x) == 4为判定条件,日志信息以c++的stream操作形式生成。

glog提供了多个便利的宏来处理特定关系的判定。具体有:

1,判定大小关系

     CHECK_EQ, CHECK_NE, CHECK_LE, CHECK_LT, CHECK_GE, CHECK_GT,使用这些宏需要注意类型一致,如果出现类型不一致的,可使用static_cast转换。

2,判定指针是否为空

        CHECK_NOTNULL(some_ptr),可用于对象初始化的时候。

3,判定字符串是否相等

       CHECK_STREQ, CHECK_STRNE, CHECK_STRCASEEQ, CHECK_STRCASENE。可进行大小写敏感或不敏感字符串来分别判定。

4,     判定浮点是否相等或相近

CHECK_DOUBLE_EQ,CHECK_NEAR。这两个宏都需要指定一个可容忍的偏差上限。

当这些宏判定条件不成立时,glog会生成一个FATAL级别的日志信息,该信息包含比较的两个值和stream方式传入的字符串,然后中止程序。

 

1.5     异常信号处理

      glog提供了比较方便的程序异常处理机制。例如,当程序出现SIGSEGV异常信号时,glog的默认异常处理过程会导出非常有用的异常信息。异常处理过程可以通过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)

默认情况下,异常信息是输出到stderr,通过InstallFailureWriter()可以改变输出目标。

 

1.6     支持debug功能

      glog提供特定的宏只在debug模式下生效。以下分别对应LOG、LOG_IF、DLOG_EVERY_N操作的专用宏。

   DLOG(INFO) << "Found cookies";

   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";

   DLOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie";

 

1.7     自定义日志信息

        glog提供VLOG宏,让用户自定义分级信息,该分级与LOG宏对应的严重性分级是独立管理,在命令行参数设置中独立设置“v”或“vmodule”参数来控制,具体见“参数设置”部分标志说明。VLOG宏便于用户调试、查找完问题以后,屏蔽日志信息,减轻负担。具体使用如下:

  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";

       上面的语句,只有在size>1024成立时且命令行参数v的值不小于1,才记录日志信息。

  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 " << COUNTER;

       上面的语句,只有在命令行参数v的值不小于1时,才会每执行10次记录一次日志信息。

  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 " << COUNTER;

       上面的语句,只有在命令行参数v的值不小于1时,若size>1024条件连续成立10次,记录一次日志信息。

 

1.8     线程安全日志记录

      glog提供了线程安全的日志记录方式。在<glog/raw_logging.h>文件中提供了相关的宏,如,RAW_CHECK,RAW_LOG等。这些宏的功能与CHECK,LOG等一致,除此以外支持线程安全,不需要为其分配任何内存和提供额外的锁(lock)机制。

 

1.9     系统级日志记录

  glog除了提供了普通的日志记录宏,还提供SYSLOG, SYSLOG_IF,和 SYSLOG_EVERY_N宏,这些宏将日志信息通过syslog()函数记录到系统日志。这个貌似只支持linux,windows 编译的时候无法通过,报的错的是找不到 syslog()函数,而且使用到的syslog.h也找不到;

 

1.10   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]

 

1.11   精简日志字符串信息

      日志信息的字符串会占用比较大的内存空间,另外还带来隐私泄露的问题。glog提供了GOOGLE_STRIP_LOG宏在编译时候去除日志的字符串信息。

 

 

.   其他说明

1, windows平台使用注意

因为glog的严重性级别中使用了ERROR宏,与<windows.h>文件中冲突,可通过以下两种方式避免:

     a,在包含<windows.h>文件之前,定义宏WIN32_LEAN_AND_MEAN 或者NOGDI。

     b,在包含<windows.h>文件之后,undef掉ERROR定义。

2,FLAGS_V 编译的时候报错error LNK2001: unresolved external symbol

需要包含这两个头文件

#include "config_for_unittests.h"

#include "utilities.h"

 

.基本代码

using namespace GOOGLE_NAMESPACE;       //必须包含

 

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

{

google::InitGoogleLogging(argv[0]);    //初始化log,参数argv[0]就是程序名

 

// 像下面对INFOERRORWARNING 三个级别的错误日志都指明了存放位置,如果是同一个位置的话三个文件会合在一起成为一个文件;假设我们只指定INFO文件位置的话,在指定位置的日志里我们可以看到文件中包含的日志也有ERRORWARNING,但是在windows TEMP 目录下也会生成两个文件,一个是WARING,一个是ERROR
google::SetLogDestination(google::INFO,installPath.c_str()); //INFO级别的日志都存放到installPath目录下
google::SetLogDestination(google::ERROR,installPath.c_str());//ERROR级别的日志都存放到installPath目录下
google::SetLogDestination(google::WARNING,installPath.c_str());//WARNING级别的日志都存放到installPath目录下

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

 

FLAGS_logbufsecs  = 0;  //日志实时输出
FLAGS_max_log_size = 1024; // max log size is 1024M

return 0;

}

 

初始化日志模块的时候设置一些以下的参数在头文件src/glog/logging.h

// Set whether log messages go to stderr instead of logfiles
DECLARE_bool(logtostderr);
// Set whether log messages go to stderr in addition to logfiles.
DECLARE_bool(alsologtostderr);
// Set color messages logged to stderr (if supported by terminal).
DECLARE_bool(colorlogtostderr);
// Log messages at a level >= this flag are automatically sent to
// stderr in addition to log files.
DECLARE_int32(stderrthreshold);
// Set whether the log prefix should be prepended to each line of output.
DECLARE_bool(log_prefix);
// Log messages at a level <= this flag are buffered.
// Log messages at a higher level are flushed immediately.
DECLARE_int32(logbuflevel);
// Sets the maximum number of seconds which logs may be buffered for.
DECLARE_int32(logbufsecs);
// Log suppression level: messages logged at a lower level than this
// are suppressed.
DECLARE_int32(minloglevel);
// If specified, logfiles are written into this directory instead of the
// default logging directory.
DECLARE_string(log_dir);
// Sets the path of the directory into which to put additional links
// to the log files.
DECLARE_string(log_link);
DECLARE_int32(v);  // in vlog_is_on.cc
// Sets the maximum log file size (in MB).
DECLARE_int32(max_log_size);
// Sets whether to avoid logging to the disk if the disk is full.
DECLARE_bool(stop_logging_if_full_disk);

 

 

int64 base_num_infos   = LogMessage::num_messages(GLOG_INFO); //INFO 级别日志数量
int64 base_num_warning = LogMessage::num_messages(GLOG_WARNING); //WARNING级别日志数量
int64 base_num_errors  = LogMessage::num_messages(GLOG_ERROR); //ERROR级别日志数量

 

 

  for ( int i = 0; i < 10; ++i ) {
    int old_errno = errno;
    errno = i;
    PLOG_EVERY_N(ERROR, 2) << "Plog every 2, iteration " << COUNTER; 
    errno = old_errno;

    LOG_EVERY_N(ERROR, 3) << "Log every 3, iteration " << COUNTER << endl;
    LOG_EVERY_N(ERROR, 4) << "Log every 4, iteration " << COUNTER << endl;

    LOG_IF_EVERY_N(WARNING, true, 5) << "Log if every 5, iteration " << COUNTER;
    LOG_IF_EVERY_N(WARNING, false, 3)
        << "Log if every 3, iteration " << COUNTER;
    LOG_IF_EVERY_N(INFO, true, 1) << "Log if every 1, iteration " << COUNTER;
    LOG_IF_EVERY_N(ERROR, (i < 3), 2)
        << "Log if less than 3 every 2, iteration " << COUNTER;
  }
  
LOG_IF(WARNING, true) << "log_if this";
LOG_IF(WARNING, false) << "don't log_if this";

DLOG(INFO)<<"ONLY PRINT FOR DEBUG COMPILE";
DLOG_IF(INFO,true)<<"ONLY PRINT FOR DEBUG COMPILE";
DLOG_EVERY_N(INFO,1)<<"ONLY PRINT FOR DEBUG COMPILE";
DLOG_IF_EVERY_N(INFO,true,1)<<"ONLY PRINT FOR DEBUG COMPILE";

void GoogleVLogTest(int v)
{
fLI::FLAGS_v = v;
VLOG(1)<<"if FLAGS_v >1, printf info";
VLOG(3)<<"IF FLAGS_v > 3,printf info";

//VLOG_IF
//VLOG_EVERY_N
//VLOG_IF_EVERY_N
//DVLOG
//DVLOG_IF
}

 

 

.特别实现

 

1.增加文件按天区分

glog是根据进程ID来区分文件的,如果你重新启动了程序,则log文件的名字就会变,这样似乎不太满足我的需求,我还要求它可以每天生成文件,方便我整理和分析log,其实也有办法,自己增加一个按天滚文件的函数就可以了。

在utilities.cc中增加函数如下,(因为PidHasChanged()在这个文件里)

static int32 g_main_day = 0;bool DayHasChanged(){

    time_t raw_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;

}

在logging.cc的LogFileObject::Write函数中将

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

    PidHasChanged()) {

改成

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

 PidHasChanged() || DayHasChanged()) {

这样就会按照天来输出log了。

 

2.glog默认有四种log级别,高级别的log中会包含低级别的log,这个我也是不需要的,我希望每一个分级一个文件。

我增加了一个宏来控制这个开关,以免影响到原来的功能。

在glog/src/windows/glog/logging.h (我在windows上用的,linux的请自己查找)

DECLARE_bool(servitysinglelog);

然后修改 logging.cc中的LogDestination::LogToAllLogfiles为如下

inline void LogDestination::LogToAllLogfiles(LogSeverity severity,

 time_t timestamp,

 const char* message,

 size_t len) {

     if ( FLAGS_logtostderr ) { // global flag: never log to file

         ColoredWriteToStderr(severity, message, len);

     } else {         if (FLAGS_servitysinglelog)

         {

             LogDestination::MaybeLogToLogfile(severity, timestamp, message, len);

         }

         else

         {

             for (int i = severity; i >= 0; --i)

                 LogDestination::MaybeLogToLogfile(i, timestamp, message, len);

         }

    }

 }

我整体的初始化函数如下:

bool Init(){

    google::InitGoogleLogging("");

    #ifdef DEBUG_MODE

        google::SetStderrLogging(google::GLOG_INFO); //设置级别高于 google::INFO 的日志同时输出到屏幕

    #else

        google::SetStderrLogging(google::GLOG_FATAL);//设置级别高于 google::FATAL 的日志同时输出到屏幕

    #endif

    FLAGS_colorlogtostderr = true; //设置输出到屏幕的日志显示相应颜色

    FLAGS_servitysinglelog = true;// 用来按照等级区分log文件

    google::SetLogDestination(google::GLOG_FATAL, "./log/log_fatal_"); // 设置 google::FATAL 级别的日志存储路径和文件名前缀

    google::SetLogDestination(google::GLOG_ERROR, "./log/log_error_"); //设置 google::ERROR 级别的日志存储路径和文件名前缀

    google::SetLogDestination(google::GLOG_WARNING, "./log/log_warning_"); //设置 google::WARNING 级别的日志存储路径和文件名前缀

    google::SetLogDestination(google::GLOG_INFO, "./log/log_info_"); //设置 google::INFO 级别的日志存储路径和文件名前缀

    FLAGS_logbufsecs = 0; //缓冲日志输出,默认为30秒,此处改为立即输出

    FLAGS_max_log_size = 100; //最大日志大小为 100MB

    FLAGS_stop_logging_if_full_disk = true; //当磁盘被写满时,停止日志输出    //google::SetLogFilenameExtension("91_"); //设置文件名扩展,如平台?或其它需要区分的信息

    //google::InstallFailureSignalHandler(); //捕捉 core dumped (linux)

    //google::InstallFailureWriter(&Log); //默认捕捉 SIGSEGV 信号信息输出会输出到 stderr (linux)

 

    return true;}

这些改动之后我的功能基本就满足了,修改下log输出的格式,原来的log格式大致是这样的 I1234 02/11 12:20:50.123456 xxx.cpp 12] 你的log内容,是 级别的首字母 线程ID 月/日 时分秒微秒 文件名 行号],我将它修改成为  [级别名 线程ID 年/月/日 时分秒毫秒 文件名 行号],在logging.cc的LogMessage::Init函数中修改

再加上一个log(severity_level, format, …)函数就可以打印log了。

 

 

你可能感兴趣的:(Logger,log4cpp,log4cxx,glog用法,boost.log)