Linux下glog编译与功能修改
【前言】
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(×tamp);//更新时间
localtime_r(×tamp, &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_成员定义先后顺序也要改变,否则会造成空指针异常
最后文件名如下: