前言
最近一直用qt开发安卓app,一直无法用真机调试,可能是缺什么东西。但是如果通过Qt Creator在真机上运行,可以在电脑控制台看打印(安卓本身的日志、qDebug之类的打印),所以我是通过打印猜测问题所在,这样凑合着用。
最近换了个测试机,控制台就不打印了,包括qDebug的一些都没有,这就很难受了。
所以,就学习了一下安卓的日志工具logcat,查资料时,发现还可以与Qt的一些打印(qDebug、qInfo等)结合,都显示在命令行工具界面中,方便查看。
知识储备
Logcat 命令行工具
安卓的日志分有优先级:
- V:详细(最低优先级)
- D:调试
- I:信息
- W:警告
- E:错误
- F:严重错误
- S:静默(最高优先级,绝不会输出任何内容)
如要将日志输出降低到可管理的水平,可使用过滤表达式限制日志输出。
过滤表达式采用 tag:priority ... 格式,其中 tag 表示您感兴趣的标记,priority 表示可针对该标记报告的最低优先级。不低于指定优先级的标记的消息会写入日志。在一个过滤表达式中提供任意数量的 tag:priority 规范。一系列规范使用空格分隔。
以下是一个过滤表达式的示例,该表达式会抑制除标记为“ActivityManager”、优先级不低于“信息”的日志消息,以及标记为“MyApp”、优先级不低于“调试”的日志消息以外的所有其他日志消息:
adb logcat ActivityManager:I MyApp:D *:S也可控制日志输出格式:使用 -v 选项,并指定下列某一受支持的输出格式:
- brief:显示优先级、标记以及发出消息的进程的 PID。
- long:显示所有元数据字段,并使用空白行分隔消息。
- process:仅显示 PID。
- raw:显示不包含其他元数据字段的原始日志消息。
- tag:仅显示优先级和标记。
- thread::旧版格式,显示优先级、PID 以及发出消息的线程的 TID。
- threadtime(默认值):显示日期、调用时间、优先级、标记、PID 以及发出消息的线程的 TID。
- time:显示日期、调用时间、优先级、标记以及发出消息的进程的 PID。
如:
adb logcat -v thread常用的命令有:
- adb logcat ,查看输出的全部日志
- adb logcat -v time ,带日期的日志
- adb logcat -v time -s Tag 。仅显示指定标签的日志信息,同时带有日期
详细使用说明可看官网介绍:
Logcat 命令行工具 | Android Studio | Android Developers (google.cn)
qInstallMessageHandler
Message Handler用于打印出调试消息、警告、严重和致命错误消息。也就是说 qDebug(), qInfo(), qWarning(), qCritical(), qFatal()都是通过这个消息句柄打印出来的。
Qt提供了一个函数qInstallMessageHandler 用于安装消息处理函数,也就是可用自定义的Message Handler 替代之前默认的。
具体可见帮助文档说明
<QtGlobal> - Global Qt Declarations | Qt Core 5.15.16
我们可以使用自定义的Message Handler将打印消息收集起来写入文件,作为日志。
当然,也可以做其他处理 ,比如此次是将打印信息写入安卓的日志。
代码
上面关于logcat的帮助文档中有关于写日志的描述
日志记录系统的 C/C++ 主接口是共享库
liblog及其头文件<android/log.h>。所有语言特定的日志记录工具(包括android.util.Log)最终都会调用函数__android_log_write。默认情况下,它会调用函数__android_log_logd_logger,该函数使用套接字将日志条目发送到logd。从 API 级别 30 开始,可通过调用__android_set_log_writer更改日志记录函数
int __android_log_write(
  int prio,
  const char *tag,
  const char *text
)Writes the constant string text to the log, with priority prio and tag tag.
下面的代码来自网络,亲测,可用:
#ifndef QDEBUG2LOGCAT_H
#define QDEBUG2LOGCAT_H
#include <QtMsgHandler>
#ifdef ANDROID
void installLogcatMessageHandler(const char *TAG);
#else
#define installLogcatMessageHandler(TAG)
#endif
#endif // QDEBUG2LOGCAT_H
#if defined(ANDROID)
#include "qDebug2Logcat.h"
#include <android/log.h>
#include <QDebug>
#include <QByteArray>
static const char *g_TAG = 0;
static void messageOutput2Logcat(QtMsgType type,
                                 const QMessageLogContext &context,
                                 const QString &msg)
{
    int prio = ANDROID_LOG_VERBOSE;
    QByteArray localMsg = msg.toLocal8Bit();
    switch (type) {
    case QtDebugMsg:
        prio = ANDROID_LOG_DEBUG;
        break;
    case QtWarningMsg:
        prio = ANDROID_LOG_WARN;
        break;
    case QtCriticalMsg:
        prio = ANDROID_LOG_ERROR;
        break;
    case QtFatalMsg:
        prio = ANDROID_LOG_FATAL;
        break;
    case QtInfoMsg:
        prio = ANDROID_LOG_INFO;
        break;
    default:
        break;
    }
    __android_log_write(prio, g_TAG, localMsg.data());
}
void installLogcatMessageHandler(const char *TAG)
{
    g_TAG = (TAG == 0 ? "QDebug" : TAG);
    qInstallMessageHandler(messageOutput2Logcat);
}
#endif
int main(int argc, char *argv[])
{
    //注册自定义的消息处理函数
    installLogcatMessageHandler("CustomTag");
  ...
}运行
在电脑上通过adb连接安卓设备后,调用命令行查看日志,
比如 标签为“onboardTrainingLog”的带日期等信息的日志:
adb logcat -v -time -s onboardTrainingLog显示如下

附
关于通过qInstallMessageHandler实现各个平台日志,可参考下面的博客:
Qt 在Windows/Mac/Android下使用日志——————附赠完整代码和示例-CSDN博客
结束语
有了日志,感觉就有了底气。



















