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;
}