Qt 软件调试(一) Log日志调试

news2024/11/16 5:53:57

终于这段时间闲下来了,可以系统的编写Qt软件调试的整个系列。前面零零星星的也有部分输出,但终究没有形成体系。借此机会,做一下系统的总结。慎独、精进~

日志是有效帮助我们快速定位,找到程序异常点的实用方法。但是好的日志才能提高问题排查的效率。在代码江湖里闯荡的这些年头了,见独篇写入、日积月累下体态无限臃肿的单日志文件;见过中英文混杂,查个日志还容易语言系统紊乱;见过没有时间节点,更没有文件名、API名的,更别提行号的,如果能反向从代码中找到输出字符的蛛丝马迹,就要谢天谢地的;当然也见过规整清爽、分类清晰的日志系统。日志系统的搭建不是本系列的重点,如果大家有兴趣,我们后面可以开一个系列专门聊聊和深入研究探讨下。

一、Qt下日志模块

基于日志系统的调试,首先必须要有日志才行。开源的日志项目,如glog、log4cpp等,这里不做过多分享。我们先简单说说Qt下的日志。下面先给个自定义Log的例子:

#ifndef CLOG_H
#define CLOG_H

#include <QString>
#include <QDate>
#include <QFile>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QMutexLocker>
#include <QObject>
#include <QMetaEnum>
#include <QMetaType>
#include <QFlag>

class CLog : public QThread
{
    Q_OBJECT
public:
    enum LogType
    {
        DEBUG,
        WARNING,
        Critical,
        Info,
        Fatal
    };
    Q_ENUM(LogType)
    Q_DECLARE_FLAGS(LogTypes, LogType)
    Q_FLAG(LogTypes)

public:
    static CLog* instance();
    ~CLog();

    bool init(const QString& strLogPath,const QString& logName = "");
    void uninit();
    bool add(const QString &strMsg, LogType eLogType);

protected:
    void run() override;
    void createNewLogFile();
private:
    QDate                       m_dateCurFile;
    QString						m_strLogPath;
    QString						m_strLogName;
    QFile						m_fileLog;
    QMutex						m_lock;
    QQueue<QString>				m_queData;
    volatile bool				m_bThreadRun;
    static CLog* instance_;
};

using LogType = typename CLog::LogType;
#define _ins_clog_  CLog::instance()

#endif
#include "CLog.h"
#include <QDir>
#include <QCoreApplication>
#include <iostream>
#include <memory>

CLog* CLog::instance_ = nullptr;

CLog *CLog::instance()
{
    static std::once_flag s_flag;
    std::call_once(s_flag, [&]() { instance_ = new CLog;});

    return instance_;
}

CLog::~CLog()
{
    uninit();
}


bool CLog::init(const QString &strLogPath, const QString& logName)
{
    m_strLogPath = strLogPath;
    if (strLogPath.isEmpty()) {
        QString strAppDirPath = QCoreApplication::applicationDirPath();
        m_strLogPath = QString("%1/log").arg(strAppDirPath);
    }

    m_strLogName = logName;
    if (m_strLogName.isEmpty()) {
        m_strLogName = QCoreApplication::applicationName();
    }

    QDir dir(m_strLogPath);
    if (!dir.exists()) {
        if(!dir.mkpath(m_strLogPath)) {
            return false;
        }
    }


    m_dateCurFile = QDate::currentDate();

    QString strFolder = QString("%1\\%2").arg(m_strLogPath).arg(m_dateCurFile.toString("yyyy-MM-dd"));
    if (!dir.exists(strFolder)) {
        if (!dir.mkpath(strFolder)) {
            return false;
        }
    }

    QString fileName = QString("%1\\%2\\%3_%4_%5.txt")
            .arg(m_strLogPath)
            .arg(m_dateCurFile.toString("yyyy-MM-dd"))
            .arg(m_dateCurFile.toString("yyyy-MM-dd"))
            .arg(QTime::currentTime().toString("HH"))
            .arg(m_strLogName);

    m_fileLog.setFileName(fileName);

    if (!m_fileLog.open(QIODevice::Append | QIODevice::Text | QIODevice::WriteOnly))
    {
        return false;
    }

    m_bThreadRun = true;
    start();

    return true;
}

void CLog::uninit()
{
    QMutexLocker locker(&m_lock);
    m_bThreadRun = false;
    m_queData.enqueue("");
}

bool CLog::add(const QString& strMsg, LogType eLogType)
{
    QMetaEnum m = QMetaEnum::fromType<LogTypes>();
    QString strLogInfo = QString("%1 %2 $%3:%4\n")
            .arg(QDate::currentDate().toString("yyyy-MM-dd"))
            .arg(QTime::currentTime().toString("HH:mm:ss.zzz"))
            .arg(m.valueToKey(eLogType))
            .arg(strMsg);

    QMutexLocker locker(&m_lock);
    m_queData.enqueue(strLogInfo);

    return true;
}

void CLog::createNewLogFile()
{
    QDate curDate = QDate::currentDate();
    if (curDate > m_dateCurFile)
    {
        m_dateCurFile = curDate;

        QDir dir;
        QString strFolder = QString("%1\\%2").arg(m_strLogPath).arg(m_dateCurFile.toString("yyyy-MM-dd"));
        if (!dir.exists(strFolder)) {
            if (!dir.mkpath(strFolder)) {
                return;
            }
        }

        QString fileName = QString("%1\\%2\\%3_%4_%5.txt")
                .arg(m_strLogPath)
                .arg(m_dateCurFile.toString("yyyy-MM-dd"))
                .arg(m_dateCurFile.toString("yyyy-MM-dd"))
                .arg(QTime::currentTime().toString("HH"))
                .arg(m_strLogName);

        if (m_fileLog.isOpen()) {
            m_fileLog.flush();
            m_fileLog.close();
        }

        m_fileLog.setFileName(fileName);
        m_fileLog.open(QIODevice::Append | QIODevice::Text | QIODevice::WriteOnly);
    }
}

void CLog::run()
{
    QString strData;
    while (m_bThreadRun) {
        strData.clear();

        if (m_queData.isEmpty()) {
            msleep(200);
            continue;
        }

        QMutexLocker locker(&m_lock);
        strData = m_queData.dequeue();

        createNewLogFile();

        if (!m_fileLog.isOpen()){
            continue;
        }
        m_fileLog.write(strData.toUtf8());
        m_fileLog.flush();
    }
}
#include "mainwindow.h"
#include "iapplication.h"
#include "CLog.h"
#include <QDateTime>
#include <QTime>


void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    LogType msgType;
    qint32 level = -1;

    switch (type)
    {
    case QtDebugMsg:
        msgType = LogType::DEBUG;
        level = 1;
        break;
    case QtWarningMsg:
        msgType = LogType::WARNING;
        level = 2;
        break;
    case QtCriticalMsg:
        msgType = LogType::Critical;
        level = 3;
        break;
    case QtFatalMsg:
        msgType = LogType::Fatal;
        level = 4;
        break;
    case QtInfoMsg:
        msgType = LogType::Info;
        level = 1;
        break;
    default:
        break;
    }

    QString addMsg = msg;
    _ins_clog_->add(addMsg, msgType);
}

void testMessageOutput()
{

    qint64 bt = QDateTime::currentMSecsSinceEpoch();
    for(int i =0; i < 10000;++i){
        _ins_clog_->add(QString("the %1 times output message.").arg(i),CLog::Info);
    }
    qint64 et = QDateTime::currentMSecsSinceEpoch();
    qDebug() << et -bt;
}


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    /// 设置日志
    qInstallMessageHandler(outputMessage); 
    QString path;
    _ins_clog_->init(path);

    testMessageOutput();

    return a.exec();
}

二、只有打印信息,没有日志输出如何排查

经典神器 Dbgview上场。
在这里插入图片描述
使用比较简单,这里不做概述。

三、关于日志或打印信息排查问题的一些总结和思考

1、日志通常只能作为业务逻辑的辅助排查。当程序由于逻辑上执行异常时,我们可以通过判断打印信息去推断可能产生问题的原因。
2、通过日志排查问题对相关人员有比较高的要求,对于业务逻辑需要比较熟悉才能快速定位
3、软件开发人员在编写打印输出的日志信息时,需要统一输出格式,对问题点输出可靠、可读性强的提示;否则输出过多无关紧要的信息,反而不利于问题的排查和分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1246875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React + BraftEditor 实现富文本编辑

Braft Editor 是一个基于 React 和 Draft-js 开发的富文本编辑器&#xff0c;提供了丰富的基础功能&#xff0c;如基本文本格式化、列表、链接、图片上传、视频插入等&#xff0c;并且还支持扩展。 首先&#xff0c;确保你已经在项目中安装了 Braft Editor 和它的依赖项&#x…

量子计算突破云渲染资源调度!真机测试完整报告公开!

​摘要&#xff1a;在影视领域中&#xff0c;经常会涉及大量的视频图像渲染工作&#xff0c;而往往在这种大规模、动态渲染场景下&#xff0c;普遍存在着冗余渲染现象。究其原因在于大规模的图像渲染通常要求在短时间内做出渲染任务的算力分配决策&#xff0c;而经典计算机无法…

基于springboot实现班级综合测评管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现班级综合测评管理系统演示 摘要 随着互联网技术的高速发展&#xff0c;人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理&#xff0c;交易等&#xff0c;而且过程简单、快捷。同样的&#x…

【shell】脚本实现将开发机user1账户下的abc文件夹复制到user2~4账户下

1 主要内容 可以使用Shell脚本来实现将开发机&#xff08;Linux&#xff09;上user1账户下的abc文件夹复制到user2、user3和user4账户下。 #!/bin/bash# 数组赋值&#xff0c;目标用户列表 # target_users(user2 user3 user4) # 定义数组 target_users()# 生成user数字的数组…

渲染农场渲染一分钟动画需要多少钱?需要渲染多少时间?

现在很公司都开始使用渲染农场渲染动画&#xff0c;但是还是有很多人不知道渲染农场渲染动画需要多少钱&#xff0c;需要渲染多少时间。在这篇文章中我们将为你一一解答&#xff0c;为你提供一个清晰的参考。 渲染农场的收费通常是按照渲染的使用时间收费&#xff0c;渲染十分…

【C/C++】素数专题

素数专题 1.判断素数模板2.求范围内的素数&#xff08;101-200&#xff09;3.判断素数与分解 1.判断素数模板 #include<stdio.h> #include<math.h>int prism(int n){if(n1) return 0;for(int i2;i<sqrt(n);i){if(n%i0) return 0;}return 1; }int main() {int n…

销售管理系统怎么选择?

销售管理系统能够帮助企业更好的了解客户、洞察市场趋势是企业管理中常见的工具&#xff0c;如何选择销售管理系统&#xff1f;可以考察销售管理系统功能是否完整、稳定性以及CRM系统是否易上手。 1.功能是否完整 功能完整是销售管理系统的首要条件&#xff0c;例如缺少营销自…

MacM1(ARM)安装Protocol Buffers

MacM1(ARM)安装Protocol Buffers 本文目录 MacM1(ARM)安装Protocol Buffers3.21之前版本安装使用configure3.22之后版本安装使用cmake使用编译后的版本 protobuf下载地址&#xff1a;https://github.com/protocolbuffers/protobuf/releases 在运行./autogen.sh或./configure命…

vue2中使用echarts

1,安装echarts npm install --save echarts 2&#xff0c;具体页面 <template><div class"app-container"><div class"aa" id"main" style"width: 500px; height: 400px;"></div></div> </te…

北邮22级信通院数电:Verilog-FPGA(11)第十一周实验(1)用JK触发器实现8421码十进制计数器

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.代码部分 1.1 JK_8421.v 1.2 JK_ff.v …

Rt-Thread 移植8--定时器的实现(KF32)

1 思路 之前线程需要延时的时候&#xff0c;就初始化remaining_tick&#xff0c;只是将线程就绪优先级组中对应的位清0&#xff0c;并不会将线程从就绪列表中删除。每次systick中断&#xff0c;就remaining_tick递减&#xff0c;如果为0表示延时时间到&#xff0c;将该线程就绪…

SSM教材管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 教材管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开…

如何为你的味蕾选择最完美的白葡萄酒

每一种白葡萄酒都有独特的味道和气味&#xff0c;如何选择一款你喜欢的口味的葡萄酒呢&#xff1f;每一种独特的白葡萄酒产品都使用了非常不同的发酵过程和葡萄种类&#xff0c;以达到它们所熟知的独特味道和风味。 来自云仓酒庄品牌雷盛红酒分享长相思是最具绿色口感的白葡萄…

NX二次开发UF_CSYS_create_temp_csys 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_create_temp_csys Defined in: uf_csys.h int UF_CSYS_create_temp_csys(const double csys_origin [ 3 ] , tag_t matrix_id, tag_t * csys_id ) overview 概述 Creates …

VMware 虚拟机设置静态IP

1.桥接模式&#xff1a;无线网卡虚拟机可以桥接的&#xff0c;Vmware0是虚拟机默认进入的虚拟网络&#xff0c;打开虚拟网络编辑器把Vmware0桥接到具体的无线网卡上&#xff0c;再打开网卡设置选择桥接模式即可。 2、.NAT模式下 &#xff1a;window下VMnet8: IPv4 地址 . . . …

ArcMap针对正射影像图生成切片操作

1.导入图层jpg文件 2.添加地图坐标系 右键点击地图 --》数据框属性 坐标系选项设置地图的坐标系 地图应该有对应的坐标文件 3.地理配准选项 --》去除自动校正&#xff0c; 4.选择参考坐标 在图中选三个定位坐标保存 5.地理配准选项 --》更新地理位置配准 6.管理工具下 --》…

绿色能源守护者:光伏运维无人机

随着我国太阳能光伏产业被纳入战略性新兴产业&#xff0c;光伏发电成为实现“双碳”目标的关键之一。在政策支持下&#xff0c;光伏产业维持高速发展&#xff0c;为迎接“碳达峰、碳中和”大势注入了强大动力。在这一背景下&#xff0c;复亚智能与安徽一家光伏企业合作&#xf…

【周报2023-11-24】

周报2023-11-24 本周主要工作下周工作计划 本周主要工作 本周的话一个主要工作有&#xff1a; 前后端进行联调接口&#xff1a; 那么目前为止的话&#xff0c;已经调通的接口 可以使用的是个人中心 历史生成的接口 选择新模板 新模板详情 ps: 下周工作计划 主要的话就是将…

spark数据倾斜的解决思路

数据倾斜是&#xff1a;多个分区中&#xff0c;某个分区的数据比其他分区的数据多的多 数据倾斜导致的问题&#xff1a; 导致某个spark任务耗时较长&#xff0c;导致整个任务耗时增加&#xff0c;甚至出现OOM运行速度慢&#xff1a;主要发生在shuffle阶段&#xff0c;同样的k…

水厂桶装水订水小程序搭建,提高用户订水的便捷性。

桶装水订水小程序&#xff0c;轻松实现在线预订&#xff0c;统计数量&#xff0c;告别繁琐电话预订。微信水预订小程序&#xff0c;不仅方便用户在线预订&#xff0c;还助力水店提升互联网管理效率。 一、微信订水小程序的作用 1. 简化预订流程&#xff0c;提高服务效率&#…