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

终于这段时间闲下来了,可以系统的编写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、软件开发人员在编写打印输出的日志信息时,需要统一输出格式,对问题点输出可靠、可读性强的提示;否则输出过多无关紧要的信息,反而不利于问题的排查和分析。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【周报2023-11-24】

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

【转载】如何在Macbook上把Ubuntu安装到移动硬盘里

我的设备系统版本、遇到的问题和解决&#xff1a; Mac&#xff1a;macOS Ventura 13.3 Ubuntu&#xff1a;22.04.3 问题&#xff1a; 按照这个教程在【3.3.10】修改完启动项后&#xff0c;Mac系统无法启动&#xff0c;Ubuntu可以正常启动。 原因&#xff1a; Mac找不到启动引导…

【Vue】浏览器安装vue插件

首先看一下安装之后的效果&#xff0c;再考虑一下要不要安装 安装完之后&#xff0c;打开浏览器控制台&#xff08;ctrl shift j) <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</t…

多回路交流三相单相电压电流电量监测开口式互感器适用多种环境用电能耗监控

1 产品概述 多回路交流无线电压电流传感器/电量采集监测仪搭配多路开口式互感器&#xff0c;可以监控采集三相电压、电流、功率和电量等信息&#xff0c;可用于能耗采集监控。支持RS485和4G网络接口&#xff0c;数据可以对接客户指定的第三方云平台。本产品可实现单相/三相用电…

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

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

讲概念谈愿景AI Agent名不副实?看实在智能RPA Agent智能体如何落地!

OpenAI在首届开发者大会上推出了GPTs和Assitant API&#xff0c;不仅改写了AI Agent的构建范式&#xff0c;也把AI智能体的应用推向一个新高潮。GPTs和GPT商店&#xff0c;使得用户无需编码通过自然语言就能创建并拥有多个专属私人助理&#xff0c;且可以如在苹果应用商店一样在…

yarn:无法加载文件 C:\Users\***\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本

原因&#xff1a;PowerShell 脚本的执行有着严格的安全策略限制&#xff01; 解决方案&#xff1a;管理员身份启动Windows PowerShell 在命令行中输入set-ExecutionPolicy RemoteSigned 再使用yarn就可以了

JavaScript实现动态背景颜色

JavaScript实现动态背景颜色 前言实现过程HTML实现过程CSS实现过程JS实现过程全部源码 前言 本文主要讲解JavaScript如何实现动态背景颜色&#xff0c;可以根据颜色选择器选择的颜色而实时更新到背景中&#xff0c;如下图所示。 当我们在颜色选择器中改变颜色时&#xff0c;会…

抖音电商品牌力不足咋办?如何升级或强开旗舰店、官方旗舰店?我们有妙招!

随着抖音电商的发展&#xff0c;越来越多的商家蜂拥而至&#xff0c;入驻经营抖音小店... 然而我们在开店的时候&#xff0c;选择开通官方旗舰店、旗舰店、专营店或专卖店&#xff0c;却被系统提示为你的商标品牌力不足&#xff0c;无法开通官方旗舰店、旗舰店、专营店、专卖店…

windows事件查看器日志

Windows 事件查看器&#xff08;Event Viewer&#xff09;是 Windows 操作系统提供的一个内置工具&#xff0c;它用于管理和查看系统、应用程序和安全事件日志。在 Windows 系统中&#xff0c;各种活动和错误都会被记录到事件日志中&#xff0c;包括系统启动、应用程序崩溃、安…

linux如何查看文件的hash数值

在Linux系统中&#xff0c;你可以使用各种工具来查看文件的哈希值。下面是一些常见的方法&#xff1a; md5sum命令&#xff1a; md5sum 文件名例如&#xff1a; md5sum example.txtsha1sum命令&#xff1a; sha1sum 文件名例如&#xff1a; sha1sum example.txtsha256sum命令&a…

PSP - 蛋白质真实长序列查找 PDB 结构短序列的算法

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134599076 在蛋白质结构预测的过程中&#xff0c;输入一般是蛋白质序列(长序列)&#xff0c;预测出 PDB 三维结构&#xff0c;再和 Ground Truth …

麒麟linux离线安装dotnet core

1. 下载 dotnet core,以3.1为例 下载地址: 下载 .NET Core 3.1 (Linux、macOS 和 Windows) 查看linux cpu类型,然后根据类型下载 uname -m #结果是: aarch64 2. 放到指定目录,比如:/usr/dotnet 3. 解压dotnet-sdk-3.1.426-linux-arm64.tar.gz cd /usr/dotnet tar –zxvf a…

数字图像处理(实践篇)一 将图像中的指定目标用bBox框起来吧!

目录 一 实现方法 二 涉及的OpenCV函数 三 代码 四 效果图 一 实现方法 ①利用OTSU方法将前景与背景分割。 ②使用连通区域分析可以将具有相同像素值且位置相邻的前景像素点组成的图像区域识别。 ③画bbox。 ④显示结果。 二 涉及的OpenCV函数 ① OpenCV提供了cv2.th…

Android 打包aar包含第三方aar 解决方案

Android 打包aar包含第三方aar 因项目需要&#xff0c;打包aar包含第三方aar&#xff0c;如果直接对module进行打包会产生一些问题。 * What went wrong: Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken be…

金风玉露一相逢|实在智能联手浪潮信息合力致新生成式AI产业生态

近日&#xff0c;实在智能正式加入浪潮信息元脑生态AIStore。 实在智能是一家基于AGI大模型超自动化技术&#xff0c;领跑人机协同时代的人工智能科技公司&#xff0c;以其自研垂直的“TARS&#xff08;塔斯&#xff09;大语言模型”技术、实在RPA Agent智能体数字员工产品和超…

土壤教学经典用图30张

一、土壤分布 二、土壤形成与气候 三、土壤形成与地形 四、土壤形成与成土母质 五、成土过程示意图 六、土壤剖面实景图 七、土壤剖面示意图 八、土壤质地 以上图片多来源于 人教、湘教、鲁教、中图、沪教 五套新教材及地图册

unity Terrain 性能问题

在实践过程中unity发生进入场景GPU爆显存的情况&#xff0c;经过调查发现是使用Terrain造成的问题&#xff0c;这个问题在使用一个Terrain的时候并不会发生&#xff0c;但是在使用多个时会发生。 似乎在使用过程中Terrain会直接把Terrain的整个地图加载&#xff0c;造成移动设…

Duplicate 模型中的 ROLLUP(十六)

因为 Duplicate 模型没有聚合的语意。所以该模型中的 ROLLUP&#xff0c;已经失去了“上卷”这一层含义。而仅仅是作为调整列顺序&#xff0c;以命中前缀索引的作用。下面详细介绍前缀索引&#xff0c;以及如何使用 ROLLUP 改变前缀索引&#xff0c;以获得更好的查询效率。 前…

【广州华锐互动】Web3D云展编辑器能为展览行业带来哪些便利?

在数字时代中&#xff0c;传统的展览方式正在被全新的技术和工具所颠覆。其中&#xff0c;最具有革新意义的就是Web3D云展编辑器。这种编辑器以其强大的功能和灵活的应用&#xff0c;正在为展览设计带来革命性的变化。 广州华锐互动开发的Web3D云展编辑器是一种专门用于创建、编…