linux glog编译及对部分功能修改

本文介绍了如何编译glog并进行功能修改,包括按天输出日志、禁止高级别log写入低级别log、自定义日志格式、更改默认日志路径以及按日期生成日志文件。详细步骤涵盖了从下载源码到修改源码的整个过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【前言】

glog是google的日志库,原生的glog库在细节上无法满足我们的需求,需要进行一些修改。本文是根据网上的一些资料,经过尝试(主要是网上资料不详细,坑比较多)后,详细介绍一些简单的格式、功能修改过程。

1、glog编译

1、下载
https://github.com/google/glog
2、解压
tar -zxvf glog-master.zip
3、编译
cd glog-master
./autogen.sh
./configure
make
make install

若编译失败,可能缺少依赖:autoconf automake libtool

2、功能修改

1)增加日志按天输出

PidHasChanged() 定义于 utilities.cc 文件中,可以加一个类似的 DayHasChanged() 函数(注意 utilities.h 文件中加上函数声明,且要与与PidHasChanged()在同一命名空间下):

static int32 g_main_day = 0;
bool DayHasChanged()
{
    time_t raw_time;
    struct tm tm_info;

    time(&raw_time);
    localtime_r(&raw_time,&tm_info);

    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() || DayHasChanged()) {

2)高级log不往低级log写

glog默认有四种log级别,高级别的log中会包含低级别的log,这个我们也是不需要的,我希望每一个分级一个文件。我增加了一个宏来控制这个开关,以免影响到原来的功能。
1、在logging.h中增加

DECLARE_bool(servitysinglelog);

该修改不会再/usr/loocal/include(安装路径)中,若头文件重这里导出则需手动添加
2、在logging.cc文件中增加

GLOG_DEFINE_bool(servitysinglelog, false,  "Prevents high-level logs from writing to low-level logs");

并将 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);
      }
  }
}

3)修改log输出格式(日志头)

在logging.cc文件中LogMessage::Init()函数中日志头格式

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_ << "] ";
  }

这个是源码的日志头格式,可对其进行随意修改

4)日志默认路径修改

logging.cc中修改const vector& GetLoggingDirectories()默认路径

const vector<string>& GetLoggingDirectories() {
  // Not strictly thread-safe but we're called early in InitGoogle().
  if (logging_directories_list == NULL) {
    logging_directories_list = new vector<string>;

    if ( !FLAGS_log_dir.empty() ) {
      // A dir was specified, we should use it
      logging_directories_list->push_back(FLAGS_log_dir.c_str());
    } else {
      GetTempDirectories(logging_directories_list);
#ifdef OS_WINDOWS
      char tmp[MAX_PATH];
      if (GetWindowsDirectoryA(tmp, MAX_PATH))
        logging_directories_list->push_back(tmp);
      logging_directories_list->push_back(".\\");
#else
      logging_directories_list->push_back("./log/");
#endif
    }
  }
  return *logging_directories_list;
}

注意,glog写文件时,文件路径需存在,否则写日志失败

5)修改日志名按日期生成日志

1、生成log每天生成日志的代码open的flag增加O_APPEND,并去除O_EXCL,否则无法创建新日志文件、追加数据

bool LogFileObject::CreateLogfile(const string& time_pid_string) {
  string string_filename = base_filename_ +filename_extension_+ time_pid_string;
  const char* filename = string_filename.c_str();
  int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, FLAGS_logfile_mode);
  if (fd == -1) return false;
#ifdef HAVE_FCNTL
  // Mark the file close-on-exec. We don't really care if this fails
  fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
  //fprintf(stderr, "base_filename_:%s nameExtension:%s time_pid_string:%s!\n",base_filename_.c_str(),filename_extension_.c_str(),time_pid_string.c_str());

  file_ = fdopen(fd, "a");  // Make a FILE*.
  if (file_ == NULL) {  // Man, we're screwed!
    close(fd);
    unlink(filename);  // Erase the half-baked evidence: an unusable log file
    return false;
  }

2、修改write中日志名,该内容仅供参考,可根据需求自行修改。g_logTooBigAddName为超出设定文件大小的文件标识,将其设为LogFileObject成员,否则多类型日志文件共用一个一个标识。

void LogFileObject::Write(bool force_flush,
                          time_t timestamp,
                          const char* message,
                          int message_len) {
  MutexLock l(&lock_);

  // We don't log if the base_name_ is "" (which means "don't write")
  if (base_filename_selected_ && base_filename_.empty()) {
    return;
  }

  bool logTooBig = false;
  bool logChangeDay = DayHasChanged();

  if ((logTooBig = (static_cast<int>(file_length_ >> 20) >= MaxLogSize())) /*||
      PidHasChanged() */|| logChangeDay) {
      if (logTooBig)
      {
          g_logTooBigAddName++;
      }
      if (logChangeDay)
      {
          g_logTooBigAddName = 1;
      }

    if (file_ != NULL) fclose(file_);
    file_ = NULL;
    file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
    rollover_attempt_ = kRolloverAttemptFrequency-1;
  }

  // If there's no destination file, make one before outputting
  if (file_ == NULL) {
    // Try to rollover the log file every 32 log messages.  The only time
    // this could matter would be when we have trouble creating the log
    // file.  If that happens, we'll lose lots of log messages, of course!
    if (++rollover_attempt_ != kRolloverAttemptFrequency) return;
    rollover_attempt_ = 0;

    struct ::tm tm_time;
    time(&timestamp);//更新时间
    localtime_r(&timestamp, &tm_time);

    // The logfile's filename will have the date/time & pid in it
    ostringstream time_pid_stream;
    time_pid_stream.fill('0');
    /*
    time_pid_stream << 1900+tm_time.tm_year
                    << setw(2) << 1+tm_time.tm_mon
                    << setw(2) << tm_time.tm_mday
                    << '-'
                    << setw(2) << tm_time.tm_hour
                    << setw(2) << tm_time.tm_min
                    << setw(2) << tm_time.tm_sec
                    << '.'
                    << GetMainThreadPid();
     */
    time_pid_stream << 1900+tm_time.tm_year
                    << setw(2) << 1+tm_time.tm_mon
                    << setw(2) << tm_time.tm_mday;


    while (true)
   {
       char szAddName[16] = ".log";
       if (g_logTooBigAddName != 1)
       {
           sprintf(szAddName,"(%d).log",g_logTooBigAddName);
       }
//       string strfilename = base_filename_+filename_extension_+ time_pid_stream.str()+string(szAddName);
       string strfilename = base_filename_ + time_pid_stream.str()+string(szAddName);
       struct stat statbuf;
       statbuf.st_size = 0;
       stat(strfilename.c_str(),&statbuf);
       if ( statbuf.st_size < MaxLogSize()*1024*1024 )
       {
           file_length_ += statbuf.st_size;
           bytes_since_flush_ += statbuf.st_size;
           break;
       }
       g_logTooBigAddName++;
   }

   if (g_logTooBigAddName != 1)
   {
       time_pid_stream << '(' << g_logTooBigAddName << ')';
   }
    time_pid_stream <<".log";
    const string& time_pid_string = time_pid_stream.str();
    if (base_filename_selected_) {
      if (!CreateLogfile(time_pid_string)) {
        perror("Could not create log file");
        fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n",
                time_pid_string.c_str());
        return;
      }
    } else {
      // If no base filename for logs of this severity has been set, use a
      // default base filename of
      // "<program name>.<hostname>.<user name>.log.<severity level>.".  So
      // logfiles will have names like
      // webserver.examplehost.root.log.INFO.19990817-150000.4354, where
      // 19990817 is a date (1999 August 17), 150000 is a time (15:00:00),
      // and 4354 is the pid of the logging process.  The date & time reflect
      // when the file was created for output.
      //
      // Where does the file get put?  Successively try the directories
      // "/tmp", and "."
      string stripped_filename(
          glog_internal_namespace_::ProgramInvocationShortName());
      string hostname;
      GetHostName(&hostname);

      string uidname = MyUserName();
      // We should not call CHECK() here because this function can be
      // called after holding on to log_mutex. We don't want to
      // attempt to hold on to the same mutex, and get into a
      // deadlock. Simply use a name like invalid-user.
      if (uidname.empty()) uidname = "invalid-user";

//      stripped_filename = stripped_filename+'.'+hostname+'.'
//                          +uidname+".log."
//                          +LogSeverityNames[severity_]+'.';
      stripped_filename = stripped_filename+'.'+LogSeverityNames[severity_]+'.';
      // We're going to (potentially) try to put logs in several different dirs
      const vector<string> & log_dirs = GetLoggingDirectories();

      // Go through the list of dirs, and try to create the log file in each
      // until we succeed or run out of options
      bool success = false;
      for (vector<string>::const_iterator dir = log_dirs.begin();
           dir != log_dirs.end();
           ++dir) {
        base_filename_ = *dir + "/" + stripped_filename;
        if ( CreateLogfile(time_pid_string) ) {
          success = true;
          break;
        }
      }
      // If we never succeeded, we have to give up
      if ( success == false ) {
        perror("Could not create logging file");
        fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!",
                time_pid_string.c_str());
        return;
      }
    }

    // Write a header message into the log file
    ostringstream file_header_stream;
    file_header_stream.fill('0');
    file_header_stream << "Log file created at: "
                       << 1900+tm_time.tm_year << '/'
                       << setw(2) << 1+tm_time.tm_mon << '/'
                       << setw(2) << tm_time.tm_mday
                       << ' '
                       << setw(2) << tm_time.tm_hour << ':'
                       << setw(2) << tm_time.tm_min << ':'
                       << setw(2) << tm_time.tm_sec << '\n'
                       << "Running on machine: "
                       << LogDestination::hostname() << '\n'
                       << "Log line format: [IWEF YY-mm-dd hh:mm:ss.uuuuuu "
                       << "file:line] msg" << '\n';
//                       << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu "
//                       << "threadid file:line] msg" << '\n';
    const string& file_header_string = file_header_stream.str();

    const int header_len = file_header_string.size();
    fwrite(file_header_string.data(), 1, header_len, file_);
    file_length_ += header_len;
    bytes_since_flush_ += header_len;
  }

  // Write to LOG file
  if ( !stop_writing ) {
    // fwrite() doesn't return an error when the disk is full, for
    // messages that are less than 4096 bytes. When the disk is full,
    // it returns the message length for messages that are less than
    // 4096 bytes. fwrite() returns 4096 for message lengths that are
    // greater than 4096, thereby indicating an error.
    errno = 0;
    fwrite(message, 1, message_len, file_);
    if ( FLAGS_stop_logging_if_full_disk &&
         errno == ENOSPC ) {  // disk full, stop writing to disk
      stop_writing = true;  // until the disk is
      return;
    } else {
      file_length_ += message_len;
      bytes_since_flush_ += message_len;
    }
  } else {
    if ( CycleClock_Now() >= next_flush_time_ )
      stop_writing = false;  // check to see if disk has free space.
    return;  // no need to flush
  }

  // See important msgs *now*.  Also, flush logs at least every 10^6 chars,
  // or every "FLAGS_logbufsecs" seconds.
  if ( force_flush ||
       (bytes_since_flush_ >= 1000000) ||
       (CycleClock_Now() >= next_flush_time_) ) {
    FlushUnlocked();
#ifdef OS_LINUX
    // Only consider files >= 3MiB
    if (FLAGS_drop_log_memory && file_length_ >= (3 << 20)) {
      // Don't evict the most recent 1-2MiB so as not to impact a tailer
      // of the log file and to avoid page rounding issue on linux < 4.7
      uint32 total_drop_length = (file_length_ & ~((1 << 20) - 1)) - (1 << 20);
      uint32 this_drop_length = total_drop_length - dropped_mem_length_;
      if (this_drop_length >= (2 << 20)) {
        // Only advise when >= 2MiB to drop
        posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length,
                      POSIX_FADV_DONTNEED);
        dropped_mem_length_ = total_drop_length;
      }
    }
#endif
  }
}

源码中存在一处可能出现异常的地方,LogFileObject::LogFileObject()函数中base_filename=="“的话,base_filename_selected_=true,base_filename_=”";故需进行修改:

LogFileObject::LogFileObject(LogSeverity severity,
                             const char* base_filename)
    : base_filename_((base_filename != NULL) ? base_filename : ""),
    base_filename_selected_(!base_filename_.empty()),

修改后,LogFileObject类中base_filename_和base_filename_selected_成员定义先后顺序也要改变,否则会造成空指针异常

最后文件名如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值