LogerThread.h
cpp
#ifndef LOGERTHREAD_H
#define LOGERTHREAD_H
#include <QThread>
#include <QFile>
#include <QStringList>
#include <QMutexLocker>
class LogerThread : public QThread
{
Q_OBJECT
private:
explicit LogerThread(QObject *parent = nullptr);
public:
~LogerThread();
void log(QString msg); // 日志信息设置
void run();
// 创建单例
static LogerThread* instance()
{
if(!_pInstance)
{
QMutexLocker mutexLocker(&_mutex);
// if(!_pInstance)
// {
LogerThread *pInstance = new LogerThread();
_pInstance = pInstance;
// }
}
return _pInstance;
}
private:
static LogerThread *_pInstance;
static QMutex _mutex;
bool _isRun; // 线程是否运行标志
QFile *_pLogFile; // 日志文件
QString _logPath; // 日志文件存储路径
QStringList _msgList; // 日志缓冲的list
};
#endif // LOGERTHREAD_H
LogerThread.cpp
cpp
#include "LogerThread.h"
#include <QDir>
#include <QFileInfoList>
#include <QDateTime>
#include <QApplication>
#include <QString>
LogerThread * LogerThread::_pInstance = nullptr;
QMutex LogerThread::_mutex;
LogerThread::LogerThread(QObject *parent) :
QThread(parent), _isRun(true), _pLogFile(nullptr)
{
_logPath = QString("%1/log").arg(qApp->applicationDirPath());
this->start();
}
LogerThread::~LogerThread()
{
_isRun = false;
this->terminate();
this->wait();
}
void LogerThread::run()
{
QDir logDir(_logPath);
if (!logDir.exists())
{
logDir.mkpath(_logPath);
}
// 限制日志文件最大10M
qint64 maxLogSize = 1024*1024*10;
// 通过时间戳来对文件命名
QString logPath = QString("%1/%2.log").arg(_logPath).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss_zzz"));
_pLogFile = new QFile(logPath);
{
while (_isRun)
{
// 超过最大限制则重新创建新文件
if (_pLogFile->isOpen() && _pLogFile->size() >= maxLogSize)
{
_pLogFile->close();
}
if (!_pLogFile->isOpen())
{
_pLogFile->setFileName(logPath);
// 注意写入时需要以追加形式打开文件
if (!_pLogFile->open(QFile::WriteOnly | QFile::Append))
this->msleep(1000);
continue;
}
QByteArray logText;
while (_msgList.length() > 0 && _isRun)
{
logText = _msgList.first().toLocal8Bit();
logText.append("\n");
if (_pLogFile->write(logText) == -1)
break;
_msgList.removeFirst();
}
this->msleep(500);
}
_pLogFile->close();
}
}
void LogerThread::log(QString msg)
{
if(msg.length() > 0)
{
_msgList.append(msg);
}
}
main.cpp
cpp
#include "LogerThread.h"
static void prepareLog()
{
QDateTime time = QDateTime::currentDateTime();
int curTime = int(time.toTime_t());
// 设置日志多少天删除
int deleteTime = 3 * 24 * 60 * 60;
// 删除log文件夹里面超过7天的文件
QDir dir(QApplication::applicationDirPath()+"/log");
if (dir.exists())
{
qDebug()<<"delete log file-----------------------";
dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
QFileInfoList fileList = dir.entryInfoList();
foreach (QFileInfo file, fileList)
{
if(file.isFile())
{
int modifyTime = int(file.lastModified().toTime_t());
if(curTime - modifyTime > deleteTime)
{
file.dir().remove(file.fileName());
}
}
}
}
}
void logOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
static QMutex mutex;
mutex.lock();
QString text;
text.append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
QFileInfo fileName(QString(context.file));
switch(int(type))
{
case QtDebugMsg:
text.append("[D] ");
break;
case QtWarningMsg:
text.append("[W] ");
break;
case QtCriticalMsg:
text.append("[C] ");
break;
case QtFatalMsg:
text.append("[F] ");
break;
}
text.append(fileName.fileName() + "-");
text.append(msg);
mutex.unlock();
// 是否输出日志到日志文件
// 创建单例线程来实时获取记录程序输出的各种日志信息
LogerThread::instance()->log(text);
}
int main(int argc, char *argv[])
{
// 删除超时的日志文件
prepareLog();
#define CONSOLE_ENABLE
#if defined (CONSOLE_ENABLE)
qInstallMessageHandler(logOutput);
#endif
QApplication a(argc, argv);
static QTranslator translator;
a.installTranslator(&translator);
Widget *w = new Widget;
w->setMinimumHeight(520);
w->setMinimumWidth(1020);
w->show();
return a.exec();
}
目前不知道为啥QString::fromUtf8(context.function) 和 QString(context.line)的打印信息有问题
限制在qml界面显示100条日志信息,支持日志信息的增删
LogMessages.h
cpp
#pragma once
#ifndef LOGMESSAGES_H
#define LOGMESSAGES_H
#include <QObject>
#include <QAbstractListModel>
class LogData;
class LogMessages : public QAbstractListModel
{
Q_OBJECT
public:
/** 条目中的角色类型
* @param[TypeRole]对应LogData的(QString)type;[SizeRole1]对应LogData的(QString)size
* @return null
*/
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole1
};
explicit LogMessages(QObject *parent = nullptr);
~LogMessages();
public: // QAbstractItemModel interface
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/** 根据索引和角色获取条目的数据
* @param[index]索引;[role]数据角色
* @return null
*/
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
/** 根据索引和角色获取条目的数据
* @param[index]索引;[role]数据角色
* @return null
*/
Q_INVOKABLE void append(const LogData &data);
/** 增加条目
* @param[index]索引
* @return null
*/
Q_INVOKABLE void insert(int index, const LogData &data);
/** 根据索引删除条目
* @param[index]索引
* @return null
*/
Q_INVOKABLE void remove(int index);
/** 增加条目
* @param[map]数据
* @return null
*/
Q_INVOKABLE void append(const QVariantMap map);
bool modelData(int index, LogData &modelData);
signals:
void countChanged(int arg);
private:
int count() const;
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QList<LogData> m_data;
};
class LogData
{
public:
LogData(const QString &type, const QString &size):
m_type(type), m_size(size)
{
}
QString type() const {return m_type;}
QString size() const {return m_size;}
private:
QString m_type;
QString m_size;
};
#endif // LOGMESSAGES_H
LogMessages.cpp
cpp
#include "LogMessages.h"
#include <QDebug>
LogMessages::LogMessages(QObject *parent):
QAbstractListModel(parent)
{
}
LogMessages::~LogMessages()
{
}
int LogMessages::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.count();
}
QVariant LogMessages::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_data.count())
return QVariant();
const LogData &data = m_data[index.row()];
if (role == TypeRole)
return data.type();
else if (role == SizeRole1)
return data.size();
return QVariant();
}
void LogMessages::insert(int index, const LogData &data)
{
if(index < 0 || index > m_data.count()) {
return;
}
beginInsertRows(QModelIndex(), index, index);
m_data.insert(index, data);
endInsertRows();
emit countChanged(m_data.count());
}
void LogMessages::append(const LogData &data)
{
m_data.insert(count(), data);
emit countChanged(m_data.count());
}
void LogMessages::remove(int index)
{
if(index < 0 || index >= m_data.count()) {
return;
}
beginRemoveRows(QModelIndex(), index, index);
m_data.removeAt( index );
endRemoveRows();
emit countChanged(m_data.count());
}
void LogMessages::append(const QVariantMap map)
{
QString type = map["type"].toString();
QString size = map["size"].toString();
LogData data(type, size);
insert(count(), data);
}
bool LogMessages::modelData(int index, LogData &modelData)
{
if(index < 0 || index >= m_data.count()) {
return false;
}
modelData = m_data.at(index);
return true;
}
int LogMessages::count() const
{
return rowCount(QModelIndex());
}
QHash<int, QByteArray> LogMessages::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole1] = "size";
return roles;
}
在创建以上类的基础上执行以下操作:
1.创建main中全局静态对象:
static LogMessages logModel;
2.同时创建上下文属性modelManager用于qml显示:
_qmlAppEngine->rootContext()->setContextProperty("modelManager", &logModel);
3.在main文件的logoutput函数增加对text文本的append
cpp
if(logModel.rowCount() >= 100)// 限制最多显示100条 多的在log文件夹下通过日志看
{
logModel.remove(logModel.rowCount()-1);
}
logModel.append(LogData(text,""));
QModelIndex modelIndex;
modelIndex = logModel.index(logModel.rowCount()-1,0,QModelIndex());
qDebug()<<logModel.rowCount()<<logModel.data(modelIndex,LogMessages::TypeRole);
4.qml中调用
javascript
//import 包
Rectangle {
id: logView
color: "transparent"
ScrollView
{
id: myLessFlow
anchors.fill: parent
anchors.topMargin: 40
anchors.leftMargin: 24
anchors.bottomMargin: 40
anchors.rightMargin: 24
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
clip: true
ListView
{
id: appInformationView
anchors.fill: parent
currentIndex: 0
highlightMoveDuration: 50
model: modelManager
delegate: gridLessDelegate
}
}
Component
{
id: gridLessDelegate
Rectangle
{
width: logView.width
height: innerText.contentHeight+20
color: (index%2 === 0) ? "#333333" :"#222222"
Text
{
id: innerText
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
text: type
wrapMode: Text.WrapAnywhere
font.pixelSize: 14
color: "#FFFFFF"
}
}
}
}