020-第三代软件开发-日志模块
  XxAHIGK5wVKO 2023年11月02日 28 0


020-第三代软件开发-日志模块_sqlite

第三代软件开发-日志模块


文章目录

  • 第三代软件开发-日志模块
  • 项目介绍
  • 日志模块
  • 日志Demo
  • 第一代日志系统
  • 第二代日志系统



关键字:

Qt

Qml

日志

Log

SQLite

项目介绍

欢迎来到我们的 QML & C++ 项目!这个项目结合了 QML(Qt Meta-Object Language)和 C++ 的强大功能,旨在开发出色的用户界面和高性能的后端逻辑。

在项目中,我们利用 QML 的声明式语法和可视化设计能力创建出现代化的用户界面。通过直观的编码和可重用的组件,我们能够迅速开发出丰富多样的界面效果和动画效果。同时,我们利用 QML 强大的集成能力,轻松将 C++ 的底层逻辑和数据模型集成到前端界面中。

在后端方面,我们使用 C++ 编写高性能的算法、数据处理和计算逻辑。C++ 是一种强大的编程语言,能够提供卓越的性能和可扩展性。我们的团队致力于优化代码,减少资源消耗,以确保我们的项目在各种平台和设备上都能够高效运行。

无论您是对 QML 和 C++ 开发感兴趣,还是需要我们为您构建复杂的用户界面和后端逻辑,我们都随时准备为您提供支持。请随时联系我们,让我们一同打造现代化、高性能的 QML & C++ 项目!

重要说明☝

☀该专栏在第三代软开发更新完将涨价

日志模块

软件中日志的重要性是不可忽视的,以下是几个关键原因:

  1. 故障排除和调试:日志记录是排查和解决软件故障的重要工具。当软件出现问题时,日志记录可以帮助开发人员查看系统行为、错误和异常,以便进行故障排除和调试,快速定位并解决问题。
  2. 性能分析和优化:通过记录关键的性能指标和日志信息,开发人员可以分析系统的性能瓶颈和瓶颈所在,以便进行优化。日志记录可以揭示资源使用情况、响应时间、吞吐量等数据,从而帮助开发人员发现并改进性能问题。
  3. 安全审计和合规性:日志记录对于安全审计和合规性要求至关重要。通过记录关键事件和活动,可以跟踪系统的访问、操作和行为,以确保合规性要求得到满足,并提供审计追踪功能,帮助监测和检测潜在的安全威胁和异常行为。
  4. 用户行为分析:通过记录用户的操作和行为,可以了解用户的需求、偏好和行为模式,从而提供更好的用户体验和个性化服务。通过分析用户日志,可以改进产品功能、优化界面设计和提高用户满意度。
  5. 数据分析和决策支持:日志记录产生的数据可以用于进行数据分析,从中提取有价值的信息以支持决策制定。通过分析用户行为、系统运行情况和业务指标等日志数据,可以发现潜在的趋势、问题和机会,为业务决策提供依据。

综上所述,日志记录对于故障排除、性能优化、安全审计、用户分析和决策支持等方面都非常重要。它是软件开发和运维过程中必不可少的一部分,有助于提升系统的可靠性、性能和用户体验。

日志Demo

日志Demo老早就搞过了,最简单的日志记录,可以看这里QtApplets-MyLog

020-第三代软件开发-日志模块_log_02

第一代日志系统

如果我没有记错,Demo的日志是在主线程中直接运行的,那么,在程序开发后期,其实我们的的日志量也是不小的,所以呢,在正式项目中,我是把我的日式系统弄到了一个线程里面了,当然,实际我还没有遇到性能瓶颈,因为我们项目一直还是在功能开发阶段,日志还是比较少的,当下的拍错和解Bug大多还是依赖Debug模式,或者DGB调试。这里简单分享下我们第一代日志系统的代码:

头文件

#ifndef TURING_LOG_H
#define TURING_LOG_H
#include <QThread>
#include <QObject>
#include <QDir>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
#define LOGPATH "./T_log"

/**
 * 对外宏
 **/

#define LOCATION "$" << __FILE__ << "$" << __LINE__                                                         // 获取代码位置宏
#define CURRENTTHREADID "$" << QThread::currentThread()                                                     // 获取线程ID宏
#define LOGINFOR "$" << __FILE__ << "$" << __LINE__ << "$" << QThread::currentThread()                      // 加上日志内容

/**
 * @brief The LogType enum
 * 彩色日志控制台输出
 * *********************************************************************************************************************************************************
 */
//enum class LogType {
//    Reset = 0,

//    Bold,
//    Unbold,

//    FrontBlack,
//    FrontRed,
//    FrontGreen,
//    FrontYellow,
//    FrontBlue,
//    FrontPurple,
//    FrontCyan,
//    FrontWhite,
//    BackBlack,
//    BackRed,
//    BackGreen,
//    BackYellow,
//    BackBlue,
//    BackPurple,
//    BackCyan,
//    BackWhite,

//    TypeCount
//};
//static const char* logCommands[] = {
//    "\033[0m",
//    "\033[1m",
//    "\033[2m",
//    "\033[30m",
//    "\033[31m",
//    "\033[32m",
//    "\033[33m",
//    "\033[34m",
//    "\033[35m",
//    "\033[36m",
//    "\033[37m",
//    "\033[40m",
//    "\033[41m",
//    "\033[42m",
//    "\033[43m",
//    "\033[44m",
//    "\033[45m",
//    "\033[46m",
//    "\033[47m",
//};

//template <typename EnumType, typename IntType = int>
//int enumToInt(EnumType enumValue);


/*

    彩色控制台日志输出 demo

int main(int argc, char *argv[])
{
    for (int i = enumToInt(LogType::Bold); i < enumToInt(LogType::TypeCount); ++i)
    {
        qInfo().nospace() << logCommands[i] << i << " Hello World" << logCommands[0];
    }
    qWarning() << logCommands[enumToInt(LogType::FrontBlue)]
               << logCommands[enumToInt(LogType::BackRed)]
               << u8"感谢大家对涛哥系列文章的支持,也"
                  "欢迎直接联系我寻求帮助" << logCommands[0];
    return 0;
}


 * **********************************************************************************************************************************************************/
/**
 * @brief The Log_Base class
 * 真实LOG日志处理线程
 */
class Log_Base : public QObject
{
    Q_OBJECT
public:
    explicit Log_Base(QObject *parent = nullptr);

    ~Log_Base();

    void log(QtMsgType type, const QMessageLogContext &context, const QString &msg);

    bool makeLogDir(QString &msg);

    void cleanOldLog(bool isClean = false);

    bool openLogDataBase(QString& mes);

public slots:
    void slot_IntiLog_Base();

private:
    QDir*                                       mLogDir = nullptr;                                                                              // 全局文件夹
    QString                                     mpath;                                                                                          // 数据库路径
    QSqlDatabase                                mTuringLogDB;                                                                                   // 日志数据库
    QSqlQuery                                   sql_query;                                                                                      // 日志执行器
    QString                                     sqlString = "NULL";                                                                             // sql语句
    QString                                     mThreadID = "NULL";                                                                             // 线程ID
    QString                                     mFileName = "NULL";                                                                             // 文件名称
    QString                                     mCurrentLine = "NULL";                                                                          // 代码行数
    QString                                     mInfor = "NULL";                                                                                // 信息
    QString                                     messageType = "";                                                                               // 消息类型
};

/**
 * @brief The Turing_Log class
 * 日志线程管理类
 */
class Turing_Log : public QThread
{
public:
    explicit Turing_Log(QObject *parent = nullptr);

    void log(QtMsgType type, const QMessageLogContext &context, const QString &msg);

    bool makeLogDir(QString &msg);

    void cleanOldLog(bool isClean = false);

    bool openLogDataBase(QString& mes);
private:

    Log_Base*                   mLog_Base = nullptr;                                                                            // 日志核心模块
    QThread*                    mLogThread = nullptr;                                                                           // 日志线程

};

#endif // TURING_LOG_H5

源文件

#include "xxxxx.h"
#include <QSettings>
#include <QSql>
#include <QCoreApplication>
#include <QSqlError>
#include <QSqlDriver>
#include <QDateTime>
#include <QString>

QString mMessage = "";                      //消息
xxxxx::xxxxx(QObject *parent)
    : QThread{parent}
{
    mLog_Base = new Log_Base;
    mLogThread = new QThread;
    mLog_Base->moveToThread(mLogThread);
    connect(mLogThread,&QThread::started,mLog_Base,&Log_Base::slot_IntiLog_Base);
    mLogThread->start();
}
/**
 * @brief xxxxx::log
 * @param type
 * @param context
 * @param msg
 * 解析劫持日志
 */
void xxxxx::log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    if(mLog_Base)
        mLog_Base->log(type,context,msg);
}
/**
 * @brief xxxxx::makeLogDir
 * @param msg
 * @return
 * 创建日志目录
 */
bool xxxxx::makeLogDir(QString& msg)
{
    if(mLog_Base)
        return mLog_Base->makeLogDir(msg);
    else
        return false;
}
/**
 * @brief xxxxx::cleanOldLog
 * @param isClean
 * 清理旧的日志
 */
void xxxxx::cleanOldLog(bool isClean)
{
    if(mLog_Base)
        mLog_Base->cleanOldLog(isClean);
}
/**
 * @brief xxxxx::openLogDataBase
 * @param mes
 * @return
 * 打开数据库
 */
bool xxxxx::openLogDataBase(QString &mes)
{
    if(mLog_Base)
        return mLog_Base->openLogDataBase(mes);
    else
        return false;
}
/**
 * @brief Log_Base::log
 * @param type
 * @param context
 * @param msg
 * 日志线程
 */
Log_Base::Log_Base(QObject *parent)
{
    Q_UNUSED(parent)
}
/**
 * @brief Log_Base::~Log_Base
 * 析构函数,关闭数据库
 */
Log_Base::~Log_Base()
{
    if(mTuringLogDB.isOpen())
        mTuringLogDB.close();
}
/**
 * @brief Log_Base::log
 * @param type
 * @param context
 * @param msg
 * 日志函数
 * 目前这里会保存所有的日志记录,后期需要加入标志,仅仅保留需要的数据
 *
 *
 *
 */
void Log_Base::log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(context);
    mMessage = msg;
    switch(type)
    {
    default:
        break;
    case QtDebugMsg:
        messageType = "输出";
        break;
    case QtInfoMsg:
        messageType = "消息";
        break;
    case QtWarningMsg:
        messageType = "警告";
        break;
    case QtCriticalMsg:
        messageType = "严重";
        break;
    case QtFatalMsg:
        messageType = "致命";
        break;
    }

    while (!mMessage.isEmpty()) {
        if(mMessage.indexOf("$") > 0)
        {
            mInfor = mMessage.mid(0,mMessage.indexOf("$"));
            mMessage = mMessage.mid(mMessage.indexOf("$")+1);
            if(mMessage.indexOf("$") > 0)
            {
                mFileName = mMessage.mid(0,mMessage.indexOf("$"));
                mMessage = mMessage.mid(mMessage.indexOf("$")+1);
                if(mMessage.indexOf("$") > 0)
                {
                    mCurrentLine = mMessage.mid(0,mMessage.indexOf("$"));
                    mMessage = mMessage.mid(mMessage.indexOf("$")+1);
                    if(mMessage.length() > 1)
                    {
                        mThreadID = mMessage;
                    }
                    else
                        mThreadID = "NULL";
                }
                else
                {
                    mCurrentLine = mMessage;
                    mThreadID = "NULL";
                }
            }
            else
            {
                mFileName = mMessage;
                mCurrentLine = "NULL";
                mThreadID = "NULL";
            }
        }
        else
        {
            mInfor = mMessage;
            mFileName = "NULL";
            mCurrentLine = "NULL";
            mThreadID = "NULL";
        }

        if(mThreadID != "NULL")                             //这里需要做下条件判断,只有给了线程ID的日志信息才被记录
        {
            sqlString = QString("insert into day%1(Date,Level,Info,File,Line,ThreadId) values ('%2', '%3', '%4','%5','%6','%7')")
                    .arg(QDateTime::currentDateTime().toString("dd"),
                         QDateTime::currentDateTime().toString("hh:mm:ss"),
                         messageType,
                         mInfor,
                         mFileName,
                         mCurrentLine,
                         mThreadID);
            sql_query.exec(sqlString);
        }
        mMessage.clear();
    }
}
/**
 * @brief Log_Base::makeLogDir
 * @param msg
 * @return
 * 创建日志文件夹
 */
bool Log_Base::makeLogDir(QString &msg)
{
    if(mLogDir->exists(LOGPATH))                                                                    //检查日志文件加是否存在
    {
        mpath = LOGPATH + QString("/") +QDateTime::currentDateTime().toString("yyyy");
        if(mLogDir->exists(mpath))                                                                  //检查对应年份日志文件夹是否旬在
            return true;
        else
            if(mLogDir->mkdir(mpath))
                return true;
            else
            {
                msg = "创建xxxxx文件夹失败";
                return false;
            }
    }
    else
    {
        if(mLogDir->mkdir(LOGPATH))
        {
            mpath = LOGPATH + QString("/") +QDateTime::currentDateTime().toString("yyyy");
            if(mLogDir->exists(mpath))
                return true;
            else
            {
                if(mLogDir->mkdir(mpath))
                    return true;
                else
                {
                    msg = QString("创建%1文件夹失败").arg(QDateTime::currentDateTime().toString("yyyy"));
                    return false;
                }
            }
        }
        else
        {
            msg = "创建xxxxx文件夹失败";
            return false;
        }
    }
    return true;
}
/**
 * @brief Log_Base::cleanOldLog
 * @param isClean
 * 清理旧的日志
 */
void Log_Base::cleanOldLog(bool isClean)
{
    if(isClean)
    {
        mLogDir->setPath(LOGPATH);
        mLogDir->setFilter(QDir::Dirs);
        QFileInfoList list = mLogDir->entryInfoList();
        int i = 0;
        do{
            QFileInfo fileInfo = list.at(i);
            if((fileInfo.fileName() != ".") &&
                    (fileInfo.fileName() != "..") &&
                    (fileInfo.fileName() != QDateTime::currentDateTime().toString("yyyy")))
            {
                mLogDir->setPath(LOGPATH + QString("/") +fileInfo.fileName());
                mLogDir->removeRecursively();
            }
            ++i;
        }while (i<list.size());
    }
}
/**
 * @brief Log_Base::openLogDataBase
 * @param mes
 * @return
 * 打开数据库
 */
bool Log_Base::openLogDataBase(QString &mes)
{
    if(QSqlDatabase::contains("xxxxx_database"))
        mTuringLogDB = QSqlDatabase::database("xxxxx_database");
    else
    {
        mpath = LOGPATH + QString("/") +QDateTime::currentDateTime().toString("yyyy")+QString("/")+ QDateTime::currentDateTime().toString("MM") + ".db";
        mTuringLogDB = QSqlDatabase::addDatabase("QSQLITE","xxxxx_database");
        if(!mTuringLogDB.isValid())
        {
            mes = "数据库驱动无效";
            return false;
        }
        mTuringLogDB.setDatabaseName(mpath);
        mTuringLogDB.setUserName("Root");
        mTuringLogDB.setPassword("Root");
        if(mTuringLogDB.open())
        {
            sql_query = QSqlQuery(mTuringLogDB);
            sqlString = QString("select count(*) from sqlite_master where type='table' and name='day%1'").arg(QDateTime::currentDateTime().toString("dd"));
            sql_query.exec(sqlString);
            if(sql_query.next())
            {
                if(sql_query.value(0).toUInt() == 0)
                {
                    sqlString = QString("create table day%1 (Id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                        "Date varchar(30),"
                                        "Level varchar(30),"
                                        "Info varchar(300),"
                                        "File varchar(300),"
                                        "Line int,"
                                        "ThreadId int)").arg(QDateTime::currentDateTime().toString("dd"));
                    if(!sql_query.exec(sqlString))
                    {
                        mes = "数据库建表";
                        return false;
                    }
                }
            }
        }
        else
        {
            mes = "数据库打开失败";
            return false;
        }
    }
    return true;
}
/**
 * @brief Log_Base::slot_IntiLog_Base
 * 初始化日志核心
 */
void Log_Base::slot_IntiLog_Base()
{
    mLogDir = new QDir;
    QCoreApplication::addLibraryPath("./plugins");
}

//template<typename EnumType, typename IntType>
//int enumToInt(EnumType enumValue)
//{

//    static_assert (std::is_enum<EnumType>::value, "EnumType must be enum");
//    return static_cast<IntType>(enumValue);
//}

在第一代日志系统中,是存在一定问题的,细心的你有发现吗?欢迎在评论去留言。

第二代日志系统

第二代日志系统,也是在第一代日志系统上的改良,就是在数据库存入的时候,不再是单独一条一条的IO存入,而是采用了事物模式,到达一应数量后批量存入数据库,但是这就会触发一个风险,就是如果我们的日志数量没有达到数量级,那就会有丢日志的风险,所以加入了定时器。哎嗨,这里是不是发现一点套路了,没错,咱的灵感就是来源于汽车的那个 3年8万公里,那个先到算那个。所以这样就就极大的保证了日志数据的完整性。至于代码,不用分享了,就加了一个定时器和事物,聪明的你一定可以,不会的话,留言,我教你。


020-第三代软件开发-日志模块_qml_03


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
XxAHIGK5wVKO