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


    头图

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


    关键字: QtQml日志LogSQLite

    项目介绍

    欢迎来到我们的 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

    image-20230725214812949

    第一代日志系统

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

    头文件

    #ifndef TURING_LOG_H
    #define TURING_LOG_H
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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 
    //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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    源文件

    #include "xxxxx.h"
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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
    //int enumToInt(EnumType enumValue)
    //{
    
    //    static_assert (std::is_enum::value, "EnumType must be enum");
    //    return static_cast(enumValue);
    //}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325

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

    第二代日志系统

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


    博客签名2021
  • 相关阅读:
    2024免费的苹果电脑杀毒软件cleanmymac X
    与面试相关的redis
    单片机学习笔记之点亮led 灯
    Express 路由
    产品经理的效率利器:揭秘提升工作效率的10大神器!
    快速排序和归并排序的非递归形式
    【Hadoop】关于Yarn的一些学习笔记(Hadoop权威指南读书笔记等)
    初试占比70%,计算机招生近200人,安徽理工大学考情分析
    gin+gorm+mysql
    手部关键点检测1:手部关键点(手部姿势估计)数据集(含下载链接)
  • 原文地址:https://blog.csdn.net/z609932088/article/details/133935637