1.使用方法


流方式输出

不用像std::out,需要在最后添加endl,使用更方便,支持常见类型直接输出,还支持Qt内置的数据类型输出,如QMap,QList,QVaraint等等,qt调试利器

QString strText = "hello";
bool bOk = true;
qDebug()<< strText << bOk;
占位方式输出

使用方式完全和printf一样,因为内部实现就是printf

qDebug("%s","hello");

Qt5源码

#define Q_ATTRIBUTE_FORMAT_PRINTF(A, B) \
         __attribute__((format(printf, (A), (B))))
         
void debug(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3);

2.让qDebug支持输出自定义的结构体


在项目中自定义结构体非常常见,但是打印自定义结构体很麻烦,每次使用都需要逐个成员依次打印,输出起来就很麻烦。

普通实现

struct StInfo{
    int id;
    QString dev_id;
};

普通用法

StInfo stInfo;
qDebug()<< stInfo.id << stInfo.dev_id;

也有个简单的做法就是实现一个toString()方法,需要输出结构体就调用一下toString()。

升级实现

struct StInfo{
    int id;
    QString dev_id;
    QString toString()
    {
        return QString("StInfo(%1 %2)").arg(id).arg(dev_id);    
    }
};

升级用法

StInfo stInfo;
qDebug()<< stInfo.toString();

不过我们可以通过友元函数的方式重载qDebug,来支持自定义类型输出。并且还可以在结构体前后或中间随意的添加标记,诸如结构体前缀,成员变量标记,更明显的看出来这是哪个结构体的信息。

终极实现

struct StInfo{
    int id;
    QString dev_id;

    friend inline QDebug operator<<(QDebug out, const StInfo& info)
    {
        out <<" StInfo("<<info.id<<info.dev_id<<") ";
        return out;
    }
};

终极用法

StInfo info;
qDebug()<< info;

3.重定向qDebug的输出


qDebug用的正舒服,此时需要输出debug信息到文件,可通过如下方式
//声明一个全局的回调函数
void outputMsg(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    ...
    //打开文件,组织字符串写入
    ...
}

int main()
{
    ...
    //安装回调函数
    qInstallMessageHandler(outputMsg);
    ...
}

为了调试方便,我们还可以将日志通过信号输出到窗口、文件、socket、控制台中等等。实现方式均可自定义。

注意

1.该回调函数内的实现不可使用qDebug,否则会造成死递归,如需打印调试使用printf和std::out均可。

2.注意加锁以支持多线程qDebug();

调用qInstallMessageHandler,传入回调函数指针,qDebug最后的输出将会送入这个回调函数,QMessageLogContext中包含了文件名,函数名,行号等信息。msg则是输出的信息。次数实现写入文件即可。

Qt5源码中的声明

class QMessageLogContext
{
    ...
    int version;
    int line;
    const char *file;
    const char *function;
    const char *category;
    ...
};

4.指定qDebug的输出格式

一个简单的使用示例

int main()
{
    //设置qDebug的输出格式
    qSetMessagePattern("[%{type}] %{time yyyy-MM-dd hh:mm:ss.zzz}  %{function}:%{line} %{message}");

    qDebug()<<"hello";
}

控制台输出

[warning] 2021-08-19 10:10:59.886  main:32 hello
输出支持的格式
支持字段 描述
%{appname} QCoreApplication::applicationName()
%{category} 日志类别
%{file} 源文件路径
%{function} 函数名
%{line} 在源文件中的行号
%{message} 实际的信息
%{pid} QCoreApplication::applicationPid()
%{threadid} 线程Id
%{qthreadptr} 当前线程指针
%{type} “debug”, “warning”, “critical” or “fatal”
%{time process} 该进程启动以来的时间
%{time boot} 自软件运行以来的时间
%{time [format]} 当前时间,支持自定义时间戳格式
%{backtrace [depth=N] [separator=“…”]} 程序异常时的堆栈信息
默认的输出格式为
“%{if-category}%{category}: %{endif}%{message}”

设置输出格式的方式支持两种,分别是环境变量和函数调用,同时使用时环境变量优先。
指定格式的时候还可以使用类型条件判断,判断条件包含在%{}中%{endif}表示该判断的逻辑结束,可以通过此功能指定不同类型消息的输出标记,以及在控制台,控制不同类型输出不同颜色。

 QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t}
 %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W
 %{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"

控制台颜色输出代码示例

不同类型的日志前缀不同的颜色码,并且该条日志输出之后,需要恢复颜色到原始状态(\033[0m),否则后续第三方库调用printf的输出会一直保持这个颜色,造成颜色显示错乱。

//CLogEvent 为作者自定义的结构体 包含日志类型(debug info...)和函数名、行号等等信息,重点关注不同类型颜色的输出实现
void CConsoleAppender::append(CLogEvent &event)
{
    if(event.level < m_enmLevel)
        return ;

    static QString arrColor[] = {"","\033[32m","\033[33m","\033[31m","\033[35m"};// 默认 绿 黄 红 紫

    fprintf(stdout,"%s %s\n\033[0m",arrColor[event.level].toStdString().c_str(),m_fmtter.fmt(event).toLocal8Bit().constData());
    fflush(stdout);
}

5.pro文件中相关配置

#release模式默认不会显示函数名,行号等信息,添加以下即可显示
DEFINES += QT_MESSAGELOGCONTEXT

#可通过环境变量配置日志格式
QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t}
  %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W
  %{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"

推荐关注

欢迎关注《每周Qt》,学习最新的Qt知识。
在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐