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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 欢迎来到我们的 QML & C++ 项目!这个项目结合了 QML(Qt Meta-Object Language)和 C++ 的强大功能,旨在开发出色的用户界面和高性能的后端逻辑。在项目中,我们利用 QML 的声明式语法和可视化设计能力创建出现代化的用户界面。通过直观的编码和可重用的组件,我们能够迅速开发出丰富多样的界面效果和动画效果。同时,我们利用 QML 强大的集成能力,轻松将 C++ 的底层逻辑和数据模型集成到前端界面中。在后端方面,我们使用 C++ 编写高性能的算法、数据处理和计算逻辑。C++ 是一种强大的编程语言,能够提供卓越的性能和可扩展性。我们的团队致力于优化代码,减少资

头图

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

[toc]

关键字: 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 xxxxx_H
#define xxxxx_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 xxxxx class
 * 日志线程管理类
 */
class xxxxx : public QThread
{
public:
    explicit xxxxx(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 // xxxxx_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万公里,那个先到算那个。所以这样就就极大的保证了日志数据的完整性。至于代码,不用分享了,就加了一个定时器和事物,聪明的你一定可以,不会的话,留言,我教你。


博客签名2021

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6月前
【云备份|| 日志 day6】文件业务处理模块
【云备份|| 日志 day6】文件业务处理模块
|
2月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
47 3
Golang语言之Prometheus的日志模块使用案例
|
2月前
|
Shell Python
salt自定义模块内使用日志例子
salt自定义模块内使用日志例子
logging 日志 模块
logging 日志 模块
|
3月前
|
Go 开发者
【应用服务 App Service】App Service发生错误请求时,如何查看IIS Freb日志,从中得知错误所发生的模块,请求中所携带的Header信息
【应用服务 App Service】App Service发生错误请求时,如何查看IIS Freb日志,从中得知错误所发生的模块,请求中所携带的Header信息
|
3月前
|
数据挖掘 语音技术
3D-Speaker说话人任务的开源项目问题之语义说话人信息模块在说话人日志系统中的问题如何解决
3D-Speaker说话人任务的开源项目问题之语义说话人信息模块在说话人日志系统中的问题如何解决
|
3月前
|
存储 监控 Java
|
3月前
|
存储 安全 Python
[python]使用标准库logging实现多进程安全的日志模块
[python]使用标准库logging实现多进程安全的日志模块
|
4月前
|
存储 算法 开发工具
Etcd/Raft 原理问题之Etcd-Raft是什么
Etcd/Raft 原理问题之Etcd-Raft是什么
|
3月前
|
监控 Java API
如何将不同业务模块产生的日志 分多文件记录
如何将不同业务模块产生的日志 分多文件记录
51 0