项目中需要打log,当初看到glog,觉得google出品应该差不了,而且简单易用,库不是很大,就选择他了。

但是在使用中还真的发现一些不顺手和库设计上的问题,反正和我的使用习惯有点不一样。

  1. 设置log参数时有的用google::xx以函数的形式设置,有的以FLAG_xx的形式设置,而且有一些设置项两种方式都可以,但是源码中又走的不是相互封装的关系,看着有点乱,没理解glog为什么要这么设计,为什么不统一用一种方式。
  2. 在使用时我一直以为没有调用google::InitGoogleLogging是不会打印任何log的,直到有用户跟我反映说,他的程序里控制台一直会输出log信息,我明明把输出到控制台的选项都关闭了啊
    FLAGS_logtostderr = 0;	//是否打印到控制台
    FLAGS_alsologtostderr = 0; //打印到日志同时是否打印到控制台

      (这里也要吐槽下,不明白为什么要有FLAGS_alsologtostderr,输出到控制台和输出到日志文件分别有一个变量控制不行么,两个变量控制同一个状态真的好么)。后来看了glog源码才发现google::InitGoogleLogging根本不控制什么东西啊,

    void InitGoogleLogging(const char* argv0) {
    glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
    } void InitGoogleLoggingUtilities(const char* argv0) {
    CHECK(!IsGoogleLoggingInitialized())
    << "You called InitGoogleLogging() twice!";
    const char* slash = strrchr(argv0, '/');
    #ifdef OS_WINDOWS
    if (!slash) slash = strrchr(argv0, '\\');
    #endif
    g_program_invocation_short_name = slash ? slash + 1 : argv0;
    g_main_thread_id = pthread_self(); #ifdef HAVE_STACKTRACE
    InstallFailureFunction(&DumpStackTraceAndExit);
    #endif
    }

      查了一下g_program_invocation_short_name,g_main_thread_id这两个变量只是获取一下信息为了防止重复初始化和打印时输出线程ID,根本不控制是否可以输出。不信再看下面

    因为glog都是通过LOG(XXX)来打印log,所以找到源码

    #define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
    ----------------------------------------------------
    #if GOOGLE_STRIP_LOG == 0
    #define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \
    __FILE__, __LINE__)
    #define LOG_TO_STRING_INFO(message) google::LogMessage( \
    __FILE__, __LINE__, google::GLOG_INFO, message)
    #else
    #define COMPACT_GOOGLE_LOG_INFO google::NullStream()
    #define LOG_TO_STRING_INFO(message) google::NullStream()
    #endif
    ----------------------------------------------------
    LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
    int ctr, void (LogMessage::*send_method)())
    : allocated_(NULL) {
    Init(file, line, severity, send_method);
    data_->stream_.set_ctr(ctr);
    } ----------------------------------------------------
    void LogMessage::Init(const char* file,
    int line,
    LogSeverity severity,
    void (LogMessage::*send_method)()) {
    allocated_ = NULL;
    if (severity != GLOG_FATAL || !exit_on_dfatal) {
    allocated_ = new LogMessageData();
    data_ = allocated_;
    data_->first_fatal_ = false;
    } else {
    MutexLock l(&fatal_msg_lock);
    if (fatal_msg_exclusive) {
    fatal_msg_exclusive = false;
    data_ = &fatal_msg_data_exclusive;
    data_->first_fatal_ = true;
    } else {
    data_ = &fatal_msg_data_shared;
    data_->first_fatal_ = false;
    }
    } stream().fill('0');
    data_->preserved_errno_ = errno;
    data_->severity_ = severity;
    data_->line_ = 0;
    data_->send_method_ = send_method;
    data_->sink_ = NULL;
    data_->outvec_ = NULL;
    WallTime now = WallTime_Now();
    data_->timestamp_ = static_cast<time_t>(now);
    localtime_r(&data_->timestamp_, &data_->tm_time_);
    int usecs = static_cast<int>((now - data_->timestamp_) * 1000000);
    RawLog__SetLastTime(data_->tm_time_, usecs); data_->num_chars_to_log_ = 0;
    data_->num_chars_to_syslog_ = 0;
    data_->basename_ = const_basename("");
    data_->fullname_ = file;
    data_->has_been_flushed_ = false; // If specified, prepend a prefix to each line. For example:
    // I1018 160715 f5d4fbb0 logging.cc:1153]
    // (log level, GMT month, date, time, thread_id, file basename, line)
    // We exclude the thread_id for the default thread.
    if (FLAGS_log_prefix && (line != kNoLogPrefix)) {
    stream() << LogSeverityNames[severity][0]
    << setw(2) << 1+data_->tm_time_.tm_mon
    << setw(2) << data_->tm_time_.tm_mday
    << ' '
    << setw(2) << data_->tm_time_.tm_hour << ':'
    << setw(2) << data_->tm_time_.tm_min << ':'
    << setw(2) << data_->tm_time_.tm_sec << "."
    << setw(6) << usecs
    << ' '
    << setfill(' ') << setw(5)
    << static_cast<unsigned int>(GetTID()) << setfill('0')
    << ' '
    << data_->basename_ << ':' << data_->line_ << "] ";
    }
    data_->num_prefix_chars_ = data_->stream_.pcount(); if (!FLAGS_log_backtrace_at.empty()) {
    char fileline[128];
    snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line);
    #ifdef HAVE_STACKTRACE
    if (!strcmp(FLAGS_log_backtrace_at.c_str(), fileline)) {
    string stacktrace;
    DumpStackTraceToString(&stacktrace);
    stream() << " (stacktrace:\n" << stacktrace << ") ";
    }
    #endif
    }
    }

      看到没,其实都在LogMessage::Init中,执行了这么多代码你有看到g_program_invocation_short_name,g_main_thread_id控制是否输出么,也就是说就算你没调用google::InitGoogleLogging,你执行LOG(XXX)<<XX;也会执行这么多代码,在一个要求执行效率比较高的代码里,这会浪费很多cpu时间啊。

      到这我也明白了客户那边为什么会输出控制台log,因为我以为没调用google::InitGoogleLogging就不会执行任何glog函数,用户使用我的接口时没有调用google::InitGoogleLogging,但是接口里面直行到LOG(XXX)时还是会输出到控制台信息。

    但是还有一个问题,那就是为什么只输出到控制台,没有输出到log文件,那么接着来看

随便找一个函数看看
google::SetLogFilenameExtension(Extra_Name);
-------------------------------------------------------
void SetLogFilenameExtension(const char* ext) {
LogDestination::SetLogFilenameExtension(ext);
} 可以知道google::xxx都是通过LogDestination类来设置的,但是LogDestination类在哪里初始化呢,找找new LogDestination吧
------------------------------------------------------
inline LogDestination* LogDestination::log_destination(LogSeverity severity) {
assert(severity >= && severity < NUM_SEVERITIES);
if (!log_destinations_[severity]) {
log_destinations_[severity] = new LogDestination(severity, NULL);
}
return log_destinations_[severity];
}
可以看到,它的逻辑是使用severity等级来定义对象,每个等级对应一个LogDestination对象,有一个LogDestination的数组。其他函数也都一样,每次都是从这个数组中取出对象来设置。
-------------------------------------------------------

因为我把log的初始化封装到一个函数里,用户没有初始化,所以没有调用任何google::xx,或者FLAGS_XX代码,所以根据glog库的默认值,是不会输出到glog的,但是问题还是,我的接口中cpu还是会执行LOG(XXX),所以其实还是执行了很多glog库里的代码。

所以如果真的想不输出log,不能通过glog函数来控制,需要自己再封装一下,在自己的代码中控制,这个glog实在太差劲了,这太坑。

这里也证明了,有人问FLAGS_XX,google::xx,应该在google::InitGoogleLogging之前还是之后执行,回答是没关系,他俩根本不存在先后顺序。

3.    用户还提出一个需求,就是不希望log中显示文件名和行号,我一开始以为这太容易了,肯定会有个设置项设置的,后来发现我想的太美了,哪有这个设置。还是老老实实看源码吧

LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
int ctr, void (LogMessage::*send_method)())
: allocated_(NULL) {
Init(file, line, severity, send_method);
data_->stream_.set_ctr(ctr);
}

前面我们知道了,输出log其实归根结底都是调用LogMessage,而且这两个参数const char* file, int line在从对外的接口函数里一路传下来,想不打印文件名和行号,只能自己改源码再重新编译库dll了,因为LogMessage是必经之路,所以我就是在LogMessage改动,把file,line给个固定值,算是解决了问题。

可是如果每次有点问题就需要改源码重新编译库文件,那也是够够的了,改来改去的,万一将来有一天需要升级最新的glog版本,难道我要再拿最新的源码把这些改动再改一边么,想想就够了。

所以啊,这第三方库的选择就跟去饭店吃饭一样,冷清的饭店肯定是有问题的,要么菜不好吃,要么服务不行,要么有什么其他问题,还是随大流比较好,小众的东西还是谨慎尝试,需要不拍踩坑才行。

关于glog使用中遇到的问题的更多相关文章

  1. ORA-02020 : 过多的数据库链接在使用中-Windows环境解决步骤

    一.现象 编译存储过程时报ORA-02020错误. 错误详细信息:ORA-04052在查找远程对象 xx@yy时出错 ORA-00604 : 递归 SQL 级别 1 出现错误 ORA-02020 : ...

  2. Subsonic使用中

    使用中,遇到各种奇葩问题,依依汇总. 1.引用了Subsonic层后,一运行就开始报错,提示未能找到文件!!    //引用后,目标框架可能会被改变,subsonic的默认框架是2.0,请检查框架是否 ...

  3. mysqldump 备份命令使用中的一些经验总结

    mysqldump的一个小坑(自测) 正文:经常使用接触mysql复制功能的朋友应该对mysqldump命令不陌生吧,鄙人最近也在研究学习这一块的内容,经过几天的测试,发现mysqldump使用中容易 ...

  4. <总结>delphi WebBrowser控件的使用中出现的bug

    Delphi WebBrowser控件的使用中出现的bug:  1.WebBrowser.Visible=false:Visible属性不能使WebBrowser控件不可见,暂时用 WebBrowse ...

  5. SSH框架使用中存在的诡异异常

    背景 相信大多数人目前都在使用Spring + Struts2/SpringMVC + Hibernate来构建项目的整体架构,但是在使用中经藏会遇到一些诡异的问题,不知道如果解决,今天我遇到了一个非 ...

  6. VM出现该虚拟机正在使用中的提示,让获取所有权限解决办法

    今天打开虚拟机正要学习,结果说是虚拟机似乎正在使用中,让我重新获取权限.解决办法:打开提示的配置文件的位置,将一个以.lck结尾的文件夹删除或者保存为另外的文件名称,再打开虚拟机就OK了.

  7. cordova + ionic 使用中碰到的一些问题

    cordova + ionic 使用中碰到的一些问题     No Content-Security-Policy meta tag found. Please add one when using ...

  8. VMware“该虚拟机似乎正在使用中”问题

    在用VMware虚拟机的时候,有时会发现打开虚拟机时提示“该虚拟机似乎正在使用中.如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所有权.否则,请按“取消(C)”按钮以防损坏.配置文件: D ...

  9. 项目使用中Linq使用总结

    项目使用中Linq使用总结 本文旨在和网友分享Linq在项目中的实践,曾经我参与过的项目都能看见Linq的影子.(LinqTosql.LinqToString.LinqToXML.LinqToEnti ...

随机推荐

  1. JAVA笔记12-接口interface

    1.概念:接口是抽象方法和常量值得定义的集合.本质上,接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义,而没有变量和方法的实现. 接口定义举例: 2.接口特性: (1)接口可以多重实现:(接口 ...

  2. os.system 的坑,'C:\Program' 不是内部或外部命令,也不是可运行的程序 或批处理文件

    首先对os.system()是执行一些系统命令,参数是以字符串的形式进行传递,如果有多个参数时,用空格隔开 例子1:cd 和 D:用空格间隔开来,代表两个参数 但一些情况空格只是字符串里面组成部分,不 ...

  3. qtmaind.lib(qtmain_win.obj) : error LNK2019: 无法解析的外部符号 __imp_CommandLineToArgvW,该符号在函数 WinMain 中被引用

    报错:qtmaind.lib(qtmain_win.obj) : error LNK2019: 无法解析的外部符号 __imp_CommandLineToArgvW,该符号在函数 WinMain 中被 ...

  4. 我在做评论功能时学到的js一些思路

    在提交评论的时候,如何判断是一级评论还是二级评论(因为都是通过一个文本域提交评论),思路:声明一个全局变量,如果是回复(二级评论)那么会触发点击回复事件,在这个事件的回调函数里给全局变量设置为true ...

  5. 手动升级 Confluence - 开始升级之前

    在本指南中,我们将会帮助你使用 zip / tar.gz 文件将你的 Confluence 安装实例在 Windows 或者 Linux 版本中升级到最新的版本. 升级到任何最新的版本都是免费的,如果 ...

  6. 序列模式挖掘--SPADE算法

  7. Struts2笔记(学struts2只需要这一篇文章)

    1.如何将struts2框架引入到web项目中      1.把struts2相关jar包引入到项目中 2.把struts2的配置文件直接放到src下面,名字要叫做struts.xml.(运行时配置文 ...

  8. fiddler(一)、下载及安装

    fiddler 官网地址:https://www.telerik.com/fiddler 进入页面后点击 Free download 进入下载页面,填写用途,邮箱和国家等信息后,点击Download ...

  9. selenium java 自动化测试 基于火狐浏览器/谷歌浏览器

    :环境 java1.8+ieda 直接上代码 pom.xml <?xml version="1.0" encoding="UTF-8"?> < ...

  10. php mysql替换数据库中出现过的所有域名实现办法 (原)

    2019-10-12备注: 数据量稍微有些大且前期数据库建设相当完善的可以看一下这边的方法,数据量小或者数据库建设不完善的可以参考这篇文章,前两天看的,没自己试,有需要可以试试  https://ww ...