#include "logworker.h" // qt #include #include #include // cpp #include // goodies #include "singleton.h" #define REGISTER_QT_MSG_TYPE(type) \ {QtMsgType::type, QString(#type).replace("Qt", "").replace("Msg", "")} namespace goodies { // TODO: to avoid errors replace me via magic_enum lib or any other compile-time // stuff when qt-distributed mingw will support it static QHash qtMsgTypes = { REGISTER_QT_MSG_TYPE(QtDebugMsg), REGISTER_QT_MSG_TYPE(QtWarningMsg), REGISTER_QT_MSG_TYPE(QtCriticalMsg), REGISTER_QT_MSG_TYPE(QtFatalMsg), REGISTER_QT_MSG_TYPE(QtInfoMsg) }; QString appLogFileName() { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + "logs"; QDir dir(path); if (!dir.exists()) { if (!dir.mkpath(path)) { // we'll get recursion here by using qWarning or some other // QMessageLogger std::cerr << "cannot create dir: " << path.toStdString() << std::endl; } } QString filename = QDateTime::currentDateTime().toString("yyyy.MM.dd_hh.mm.ss.zzz") + ".log"; return path + QDir::separator() + filename; } void myMessageHandler( QtMsgType type, const QMessageLogContext& context, const QString& msg ) { goodies::defaultMessageHandler(type, context, msg); // std::cout << msg.toStdString() << std::endl; SINGLETON(LogWorker)->writeMessage(type, context, msg); } LogMsg::LogMsg( QDateTime dateTime, MessageType messageType, QString message, QObject* parent ) : G_Object(parent) , INIT_FIELD(dateTime) , INIT_FIELD(messageType) , INIT_FIELD(message) { } LogWorker::LogWorker(const QString filepath) : QAbstractListModel(nullptr) , INIT_FIELD(filepath) , m_file(filepath) , m_fileStream(&m_file) { QMutexLocker l(&m_mtx); auto onIsLoggingEnabledChanged = [&]() { if (get_isLoggingEnabled()) { if (!m_file.open(QFile::ReadWrite | QFile::Append)) { qWarning() << tr("cannot open log file for reading and writing: ") << m_file.fileName(); set_isLoggingEnabled(false); return; } qDebug() << "log file opened reading and writing"; } else { if (!m_file.open(QFile::ReadOnly)) { qWarning() << tr("cannot open log file for reading: ") << m_file.fileName(); } qDebug() << "log file opened for reading"; } }; connect( this, qOverload<>(&LogWorker::isLoggingEnabledChanged), onIsLoggingEnabledChanged ); onIsLoggingEnabledChanged(); } LogWorker::~LogWorker() { QMutexLocker l(&m_mtx); } QString LogWorker::filepath() const { return m_filepath; } void LogWorker::initialize(QApplication& app) { // LogWorker uses app info to open log file Q_UNUSED(app) SINGLETON(LogWorker); goodies::defaultMessageHandler = qInstallMessageHandler(myMessageHandler); } // LogWorker &LogWorker::operator<<(const QString &msg) //{ // QMutexLocker l(&m_mtx); // if(!m_file.isOpen()) // return *this; // auto date = // QDateTime::currentDateTime().toString("yyyy.MM.dd_hh.mm.ss.zzz"); // auto m = date + ": " + msg; // m_fileStream << m << Qt::endl << Qt::flush; // beginInsertRows(QModelIndex(), m_messages.count(), m_messages.count()); // m_messages << m; // endInsertRows(); // auto delta = m_messages.count() - get_maxLinesCount(); // if(get_maxLinesCount() > 0 && delta > 0) // { // beginRemoveRows(QModelIndex(), 0, delta - 1); // m_messages.erase(m_messages.begin(), // m_messages.begin() + delta); // endRemoveRows(); // } // return *this; //} // void LogWorker::writeMessage(QString msg) //{ // (*this) << msg; // } void LogWorker::writeMessage( QtMsgType type, const QMessageLogContext& context, const QString& msg ) { QMutexLocker l(&m_mtx); // extract filename from filepath (not tested on windows \\\\\\\ ) const char* file = context.file ? FILENAME(context.file) : ""; // convert QtSomethingMsg to Something // auto typeName = // QString::fromStdString(std::string(magic_enum::enum_name(type))) // .replace("Qt", "").replace("Msg", ""); auto typeName = qtMsgTypes[type]; auto newLineOrSpace = msg.contains("\n") ? "\n" : " "; if (!m_file.isOpen()) return; // auto date = // QDateTime::currentDateTime().toString("yyyy.MM.dd_hh.mm.ss.zzz"); auto dt = QDateTime::currentDateTime(); auto m = QString("%1:%2: %3: %4%5") .arg(file) .arg(context.line) .arg(typeName) .arg(newLineOrSpace) .arg(msg); auto full_m = dt.toString(Qt::ISODateWithMs) + ": " + m; m_fileStream << full_m << Qt::endl << Qt::flush; beginInsertRows(QModelIndex(), m_messages.count(), m_messages.count()); m_messages << LogMsg(dt, LogMsg::MessageType(type), msg); endInsertRows(); auto delta = m_messages.count() - get_maxLinesCount(); if (get_maxLinesCount() > 0 && delta > 0) { beginRemoveRows(QModelIndex(), 0, delta - 1); m_messages.erase(m_messages.begin(), m_messages.begin() + delta); endRemoveRows(); } // writeMessage(QString("%1:%2: %3:%4%5") // .arg(file) // .arg(context.line) // .arg(typeName) // .arg(newLineOrSpace) // .arg(msg)); } int LogWorker::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent) return m_messages.count(); } QVariant LogWorker::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); // std::cout << Q_FUNC_INFO << std::endl; if (role >= Qt::UserRole) { auto m = m_messages.at(index.row()); switch (role) { case Date: return m.get_dateTime().toUTC(); break; case MsgType: return m.get_messageType(); break; case Msg: return m.get_message(); break; default: break; } return m_messages.at(index.row()).get_message(); } return QVariant(); } LogWorker::LogWorker(const LogWorker& other) { Q_ASSERT_X(false, Q_FUNC_INFO, "don't call me"); } } // namespace goodies