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;

}
}
posted @ 2020-08-25 15:57  WoodInEast  阅读(254)  评论(0编辑  收藏  举报