Skip to content

FileLogger ​

c++

class FileLogger : public QThread
{
    Q_OBJECT
public:
    FileLogger();
    ~FileLogger();
    void SetRecordLevel(Level level);
    void OpenLog(const QString& path);
    void Log(Level level,QString message);
    void Log(Level level,QString message,const QVariantList& argslist);
    void StopLog();
signals:
    void SendLog(std::shared_ptr<LogTask> log);
private:
    void WriteLog(Level level,QString date, QString message);
    void WriteLog(Level level,QString date, QString message,const QVariantList& argslist);
    
    QVariant MontageStr(QVariant arg){
        return arg;
    }

    void Open(const QString& file_name);
    void ClearLog();
    void run() override;
    int GetTaskNum();
    bool ScanLogDir();
private:
     Level level_ = Level::DEBUG;
     QString file_path_;
     QString file_name_;
     QFile file_;
     QDateTime latest_time_;
     QTimer* clear_log_timer_;
     QStringList level_list_ = {"DEBUG","INFO","WARN","ERROR","FATAL"};
     std::mutex mtx_;
     bool stop_log_ = false;
     QQueue<std::shared_ptr<LogTask>> log_task_queue_;
     std::condition_variable queue_empty_cond_;

};
c++
#include "filelogger.h"
FileLogger::FileLogger() {
    //设置定时器 定时检查目录下是否有超时的日志  并且记录今天的时间

    clear_log_timer_ = new QTimer();
    clear_log_timer_->setInterval(2000); // 15*24*3600*1000设置为x毫秒触发一次
    connect(clear_log_timer_, &QTimer::timeout, this, &FileLogger::ClearLog);
    clear_log_timer_->start();
}

void FileLogger::ClearLog()
{

    QDir dir(file_path_);
    QStringList files = dir.entryList(QDir::Files);
    if(files.length() <=0){
        return;
    }
    for(auto file_name : files){
        //根据文件名转换成时间
        QDateTime fileCreateTime = QDateTime::fromString(file_name.split('.')[0],"yyyy_MM_dd");

        if(fileCreateTime.daysTo(QDateTime::currentDateTime()) > 15){
            QFile file(file_path_+"//"+file_name);
            file.remove();
            qDebug()<<"删除 "<<file.fileName();
        }
    }
}

void FileLogger::StopLog()
{
    //加锁关闭
    std::unique_lock<std::mutex> lock(mtx_);
    stop_log_ = true;
}

//在run里面一直循环检查QQueue里面有没有任务
void FileLogger::run()
{
    while(1){
        std::unique_lock<std::mutex> lock(mtx_);
        /*如果为空并且日志线程没关闭 释放mtx_锁 这样其他线程可以
        抢mtx_锁 可以让打印任务入队  并且日志线程会阻塞在这里 不占用CPU*/
        while(log_task_queue_.empty() && !stop_log_){
            /*释放锁以后它会阻塞在这里  只有在别的线程添加打印任务时候
            通过条件变量唤醒 */
            queue_empty_cond_.wait(lock);
        }

        //如果为真 日志线程退出
        //消费完所有任务 再退出
        if(stop_log_){
            while(!log_task_queue_.empty()){
                //取出一个任务
                std::shared_ptr<LogTask> printLogTask = log_task_queue_.dequeue();
                //消费一个任务
                this->WriteLog(printLogTask->level_,
                               printLogTask->date_,
                               printLogTask->message_,
                               printLogTask->args_list_);
            }
            //解锁
            lock.unlock();
            return;
        }
        //取出一个任务
        std::shared_ptr<LogTask> printLogTask = log_task_queue_.dequeue();
        //解锁
        lock.unlock();
        //消费一个任务
        this->WriteLog(printLogTask->level_,
                       printLogTask->date_,
                       printLogTask->message_,
                       printLogTask->args_list_);

    }
}

int FileLogger::GetTaskNum()
{
    return log_task_queue_.size();
}

FileLogger::~FileLogger(){
    delete clear_log_timer_;
}

void FileLogger::Open(const QString &file_name)
{
    file_name_ = file_name;
    file_.setFileName(file_path_+"//"+file_name);//创建文件存放位置
    file_.open(QIODevice::Append|QIODevice::Text); // 写入模式:追加
}

void FileLogger::Log(Level level,QString message){
    Log(level,message,{});
}

void FileLogger::Log(Level level,QString message,const QVariantList& args_list){
    if(level < level_){
        return;
    }
    //构建任务对象
    std::shared_ptr<LogTask> log_task = std::make_shared<LogTask>(level,message,args_list);
    log_task->date_ = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    //操作队列要上锁
    std::unique_lock<std::mutex> lock(mtx_);
    emit SendLog(log_task);

    log_task_queue_.enqueue(log_task);

    //加完任务唤醒日志线程 这时候它去取任务一定没问题
    //如果是1 说明之前队列是空  也说明消费者线程已经阻塞  就把它唤醒
    bool notify = (log_task_queue_.size() == 1)?true:false;
    lock.unlock();
    // 通知等待的线程有新的任务可处理
    if(notify){
         queue_empty_cond_.notify_one();
    }
}

void FileLogger::WriteLog(Level level,QString date, QString message)
{
    WriteLog(level,message,{});
}

// 往那个日志输出是打印线程的事情
void FileLogger::WriteLog(Level level,QString date, QString message, const QVariantList &args_list)
{
    if(level < level_){
        return;
    }

    //填入可变参数    !要是这个参数不能toString 会出问题!
    for(auto arg:args_list){
        message = QString(message).arg(arg.toString());
    }
    //得到日志等级对应的字符串
    QString s_level = level_list_[level];

    if(file_.isOpen()){
        file_.close();
    }

    //如果当前目录下的最新日志不是今天
    if(latest_time_.date() != QDateTime::currentDateTime().date()){
        //设置最新日期是今天
        latest_time_ = QDateTime::currentDateTime();
    }

    //打开今天的日志 如果不存在就创建
    this->Open(latest_time_.toString("yyyy_MM_dd")+".log");

    //写入日志
    QTextStream in(&file_);
    in.setCodec("UTF-8");
    in << date+"  ["+s_level+"]  "+message+"\n";
}

/* 现在假设目录里只有一个Log文件 有多个只找找到的第一个 */
void FileLogger::OpenLog(const QString &path)
{
    // 记录当前打开的目录
    file_path_ = path;
    // 清理一下过期日志
    ClearLog();
    // 关闭一下文件对象
    file_.close();
    ScanLogDir();
}

// 给latest_time_赋值该目录下最新的日志时间 如果目录为空返回false 不为空返回true
bool FileLogger::ScanLogDir()
{
    QDir dir(file_path_);
    QStringList files = dir.entryList(QDir::Files);
    if(files.length() <= 0){//如果目录下面没文件
       latest_time_ = QDateTime::currentDateTime();
       return false;
    }
    // 这样做Log文件夹里面不能有别的东西 只能是严格的yyyy-MM-dd  hh,mm,ss.Log 不然会出问题
    for(auto& file_name : files){
        //拿到目录下的Log文件名
        QString prefix = file_name.split('.')[0];

        // 把Log文件名转换成QDateTime
        QDateTime date_time = QDateTime::fromString(prefix,"yyyy-MM-dd  hh,mm,ss");
        if(latest_time_ < date_time){
            latest_time_ = date_time;
        }
    }
    return true;
}

void FileLogger::SetRecordLevel(Level level)
{
    level_ = level;
}

学 习 记 录