1. 程式人生 > 實用技巧 >muduo原始碼解析14-logfile類

muduo原始碼解析14-logfile類

logfile類:

class logfile:noncopyable
{
};

作用:

主要負責日誌寫入檔案的管理
內部提供append,rollFile,flush三個函式
append表示向檔案尾部追加資料,
rollFile表示需要更換一個日誌檔案來寫日誌
flush表示清空檔案讀寫緩衝區

注意append和flush都提供了有鎖/無鎖的實現,logfile建構函式需要傳入是否是執行緒安全的threadSafe引數
若是true需要建立一個互斥所mutexlock並且加鎖,否則不用加鎖.

logfile成員變數:

private:
    const string m_basename;        //
日誌檔名 const off_t m_rollSize; //已寫入的資料大於rollsize就生成一個新日誌檔案 const int m_flushInterval; //日誌寫入時間間隔,預設每3s寫一次 const int m_checkEveryN; //m_count>預設=1024時需要檢查是否回滾或寫入 int m_count; //計數器,每次append操作時,都需要讓m_count++更新 std::unique_ptr<mutexlock> m_mutex;//
鎖智慧指標 time_t m_startOfPeriod; //開始記錄日誌的時間 time_t m_lastRoll; //上一次滾動日誌檔案的時間 time_t m_lastFlush; //上一次日誌寫入檔案的時間 std::unique_ptr<fileutil::AppendFile> m_file; //檔案智慧指標 static const int m_kRollPerSeconds=60*60*24; //一天的秒數

logfile成員函式:

public:
    logfile(
const string& basename,off_t rollSize,bool threadSafe=true, int flushInterval=3,int checkEveryN=1024); ~logfile(); //寫檔案操作,有鎖/無鎖兩種實現 void append(const char* logline,int len); //清空檔案讀寫緩衝區,有鎖/無鎖兩種實現 void flush(); //回滾檔案,其實就是當前檔案不能寫日誌了,需要更換下一個日誌檔案來寫日誌 bool rollFile(); private: //append的實現函式 void append_unlocked(const char* logline,int len); //根據當前時間獲得一個新的日誌檔名字 static string getLogFileName(const string& basename,time_t* now);

logfile.h

#ifndef LOGFILE_H
#define LOGFILE_H

#include"base/mutex.h"
#include"base/types.h"

#include<memory>

namespace mymuduo {

namespace fileutil{
    class AppendFile;
}

class logfile:noncopyable
{
public:
    logfile(const string& basename,off_t rollSize,bool threadSafe=true,
            int flushInterval=3,int checkEveryN=1024);
    ~logfile();

    //寫檔案操作,有鎖/無鎖兩種實現
    void append(const char* logline,int len);
    //清空檔案讀寫緩衝區,有鎖/無鎖兩種實現
    void flush();
    //回滾檔案,其實就是當前檔案不能寫日誌了,需要更換下一個日誌檔案來寫日誌
    bool rollFile();


private:
    //append的實現函式
    void append_unlocked(const char* logline,int len);
    //根據當前時間獲得一個新的日誌檔名字
    static string getLogFileName(const string& basename,time_t* now);

    const string m_basename;        //日誌檔名
    const off_t m_rollSize;         //已寫入的資料大於rollsize就生成一個新日誌檔案
    const int m_flushInterval;      //日誌寫入時間間隔,預設每3s寫一次
    const int m_checkEveryN;        //m_count>預設=1024時需要檢查是否回滾或寫入
    int m_count;                    //計數器,每次append操作時,都需要讓m_count++更新

    std::unique_ptr<mutexlock> m_mutex;//鎖智慧指標
    time_t m_startOfPeriod;         //開始記錄日誌的時間
    time_t m_lastRoll;              //上一次滾動日誌檔案的時間
    time_t m_lastFlush;             //上一次日誌寫入檔案的時間

    std::unique_ptr<fileutil::AppendFile> m_file;   //檔案智慧指標
    static const int m_kRollPerSeconds=60*60*24;    //一天的秒數
};


}//namespace mymuduo


#endif // LOGFILE_H

logfile.cpp

#include "logfile.h"

#include"base/fileutil.h"
#include"base/processinfo.h"

#include<assert.h>
#include<stdio.h>
#include<time.h>

namespace mymuduo {

//建構函式,負責初始化檔名,回滾大小,是否執行緒安全(判斷是否需要建立互斥鎖),
//寫入日誌檔案的時間間隔等資訊
logfile::logfile(const string& basename,off_t rollSize,bool threadSafe,
                 int flushInterval,int checkEveryN)
    :m_basename(basename),m_rollSize(rollSize),m_flushInterval(flushInterval),
      m_checkEveryN(checkEveryN),m_count(0),
      m_mutex(threadSafe?new mutexlock:NULL),
      m_startOfPeriod(0),m_lastRoll(0),m_lastFlush(0)
{
    //保證basename中沒有 / , 也就是這裡basename是檔名而不是檔案路徑
    assert(basename.find('/')==string::npos);
    rollFile();
}
logfile::~logfile()=default;

//兩種方式:有鎖/無鎖,內部都是append_unlocked()函式實現
void logfile::append(const char* logline,int len)
{
    if(!m_mutex)
        append_unlocked(logline,len);
    else
    {
        mutexlockguard mlg(*m_mutex);
        append_unlocked(logline,len);
    }
}

//兩種方式:有鎖/無鎖,內部都是AppendFile::flush()實現
void logfile::flush()
{
    if(!m_mutex)
        m_file->flush();
    else
    {
        mutexlockguard mlg(*m_mutex);
        m_file->flush();
    }
}

//回滾日誌檔案,功能是根據當前時間now設定m_lastRoll,m_lastFlush,m_startOfPeriod
//並把m_file重新指向一個新的AppendFile(完整日誌檔名字)
bool logfile::rollFile()
{
    time_t now = 0;
    //filename格式 test.txt.20200825-064633.master.22895.log
    string filename = getLogFileName(m_basename, &now);
    time_t start = now / m_kRollPerSeconds * m_kRollPerSeconds;//啥意思?

    //這裡考慮一下now<=m_lastRoll,要不然就是getLogFileName返回錯誤的now
    //要不然就是時間靜止/倒退,肯定不正確,只考慮now>m_lastRoll
    if (now > m_lastRoll)
    {
        m_lastRoll = now;
        m_lastFlush = now;
        m_startOfPeriod = start;
        //m_file重新指向一個新的AppendFile物件,開啟的檔案為filename
        //即完整的日誌檔名字,如果檔案不存在則新建立一個檔案
        m_file.reset(new fileutil::AppendFile(filename));
        return true;
    }
    return false;
}

//利用AppendFile類完成向檔案中寫操作
void logfile::append_unlocked(const char* logline,int len)
{
    //向檔案中寫入長度為len的logline字串
    m_file->append(logline,len);
    //判斷已寫入的位元組數是否超出了回滾大小,若超出了只能再次回滾檔案:
    //根據當前時間建立一個新的日誌檔案,並且把m_file指向這個新的AppendFile
    if(m_file->writtenBytes()>m_rollSize)
        rollFile();
    else
    {
        //不需要回滾檔案時候,對當前檔案進行操作即可.
        m_count++;
        if(m_count>=m_checkEveryN)
        {
            //此時重置m_count,再次回滾檔案.
            m_count=0;
            time_t now=::time(NULL);
            time_t thisPeriod=now/m_kRollPerSeconds*m_kRollPerSeconds;
            if(thisPeriod!=m_startOfPeriod) //比較是否相等,不相等 說明到了第二天0點,就滾動檔案
                rollFile();
            else if(now-m_lastFlush>m_flushInterval)//判斷是否超過flush時間間隔
            {
                m_lastFlush=now;
                m_file->flush();
            }
        }
    }
}

//得到完整的日誌檔名字,例如basename=test.txt時,filename=如下
//test.txt.20200825-064633.master.22895.log
//basename.時間.hostname.pid.log     //就是一個完整的日誌檔名字
string logfile::getLogFileName(const string& basename,time_t* now)
{
    string filename;
    filename.reserve(basename.size()+64);
    //新增basename
    filename=basename;
    char timebuf[32];
    struct tm tm;
    *now=::time(NULL);
    gmtime_r(now,&tm);
    strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm);
    filename += timebuf;   //新增time
    filename+=processinfo::hostname();//新增hostname
    char pidbuf[32];
    snprintf(pidbuf, sizeof pidbuf, ".%d", processinfo::pid());
    filename += pidbuf;//新增pid
    filename += ".log";//新增字尾名

    return filename;

}
}