【Qt】DebugView and qInstallMsgHandler


【目的】

  • 將 qDebug() 導入 DebugView。
  • 以下提供使用官方 API (OutputDebugString) 配合 qInstallMsgHandler 輸出到 DebugView 的方式。

【環境】

【說明】

先由一些資料建立觀念

  • 使用 qDebug() 時可以將 DebugView 打開看看,有時輸出訊息會直接導到 DebugView。
    左圖紅色框第二個為DebugView打開前, Application Output還可以看到訊息。
    第三個是DebugView 打開後,只會看到下面訊息,表示訊息已被導到 DebugView。
    Cannot retrieve debugging output!
    image   image
  • OutputDebugString (基本程式 main.cpp)
    • 官方輸出到 DebugView
      在user-space就是呼叫 OutputDebugString ,
      而kernel-space就是呼叫DbgPrint。以下列出主要程式。
      #include <QtGui/QApplication>
      #include "dialog.h"
      #include <windows.h>
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
          OutputDebugString(L"Echo Message to DebugView");
          Dialog w;
          w.show();
          return a.exec();
      }
    • 結果一樣可以在 DebugView 可以看到
      image
  • qInstallMsgHandler
    • qInstallMsgHandler 的用法類似下面,可以自己指定輸出的方式,
      不過卻卻無法輸出到 DebugView。
      #include <QtGui/QApplication>
      #include "dialog.h"
      #include <qDebug>
      
      void myMessageOutput(QtMsgType type, const char *msg)
       {
           switch (type) {
           case QtDebugMsg:
               fprintf(stderr, "Debug: %s\n", msg);
               break;
           case QtWarningMsg:
               fprintf(stderr, "Warning: %s\n", msg);
               break;
           case QtCriticalMsg:
               fprintf(stderr, "Critical: %s\n", msg);
               break;
           case QtFatalMsg:
               fprintf(stderr, "Fatal: %s\n", msg);
               abort();
           }
       }
      
      int main(int argc, char *argv[])
      {
          qInstallMsgHandler(myMessageOutput);
          QApplication a(argc, argv);
          Dialog w;
          w.show();
          qDebug("Dialog is shown");
          return a.exec();
      }
    • 要解決這個問題,需要修改讓訊息可輸出到 DebugView
      #include <QtGui/QApplication>
      #include "dialog.h"
      #include <qDebug>
      #include <windows.h>
      
      void myMessageOutput(QtMsgType type, const char *msg)
       {
          char str[255];
          switch (type) {
           case QtDebugMsg:
               sprintf(str, "Debug: %s", msg);
               break;
           case QtWarningMsg:
               sprintf(str, "Warning: %s", msg);
               break;
           case QtCriticalMsg:
               sprintf(str, "Critical: %s", msg);
               break;
           case QtFatalMsg:
               sprintf(str, "Fatal: %s", msg);
               abort();
           default:
               sprintf(str, "Unknown: %s", msg);
           }
          OutputDebugStringA(str);
       }
      
      int main(int argc, char *argv[])
      {
          qInstallMsgHandler(myMessageOutput);
          QApplication a(argc, argv);
          Dialog w;
          w.show();
          qDebug("Dialog is shown");
          qWarning("Dialog is shown");
          qCritical("Dialog is shown");
          //qFatal("Dialog is shown");
          return a.exec();
      }
    • 目前問題
  • 延伸到 Linux 平台
    • 如果要將訊息輸出到 syslog,先看一下基本用法
      #incluse <syslog.h>
      main(int argc,char *argv[]){
        char *str = "test";
        openlog(argv[0],LOG_PID,LOG_USER);
        syslog(LOG_INFO,"%s\n",str);
        closelog();
      }
    • 通常會寫成一個巨集比較方便
      #ifndef NDEBUG
      #define printd(msg, args...) \
        do { \
          openlog("[Log]",LOG_PID,LOG_USER); \
          syslog(LOG_INFO, msg, ##args); \
          closelog(); \
        }while(0)
      #else
      #define printd(msg, args...)    do { } while(0)
      #endif
    • 所以 myMessageOutput 可以改成如此
      void myMessageOutput(QtMsgType type, const char *msg)
      {
          openlog(argv[0],LOG_PID,LOG_USER);
          switch (type) {
              case QtDebugMsg:
                  syslog(LOG_INFO,"Debug   : %s\n",str);
                  break;
              case QtWarningMsg:
                  syslog(LOG_INFO,"Warning : %s\n",str);
                  break;
              case QtCriticalMsg:
                  syslog(LOG_INFO,"Critical: %s\n",str);
                  break;
              case QtFatalMsg:
                  syslog(LOG_INFO,"Fatal   : %s\n",str);
                  //abort();
              }
          closelog();
      }
    • 和 Windows平台的DebugView 作整合,目前的想法是透過 ifdefine 控制, 目前程式碼先省略。
  • 嘗試修改讓訊息輸出到 檔案
    直接將 Debug message 輸出到檔案也是一個不錯的方式 myMessageOutput 可以寫成這樣。
    請注意 fclose() 要放在 if 裡面,表示開檔成功才作關檔動作。
    const char* logfile = "/var/log/log.txt";
    void myMessageOutput(QtMsgType type, const char *msg)
    {
        FILE *fp = fopen(logfile, "a");
        if (fp)
        {
            switch (type) {
            case QtDebugMsg:
                fprintf(fp, "Debug   : %s\n", msg);
                break;
            case QtWarningMsg:
                fprintf(fp, "Warning : %s\n", msg);
                break;
            case QtCriticalMsg:
                fprintf(fp, "Critical: %s\n", msg);
                break;
            case QtFatalMsg:
                fprintf(fp, "Fatal   : %s\n", msg);
                //abort();
            }
            fclose(fp);
        }
    }
  • 輸出到Kiwi Syslog Server
    如果使用 busybox 的話建議直接啟用將 –R 參數將訊息往遠端機器丟,
    除非特殊需求否則不需自己調用 socket 。
    [參考] BusyBox - The Swiss Army Knife of Embedded Linux
    http://www.busybox.net/downloads/BusyBox.html
    busybox syslogd -R 192.168.1.1:601

【想法】

  • 其實調用 qDebug/qWarning/qCritical/qFatal 對除錯而言,幫助並不大,要做的應該是將要做的事按照
    Init/Process/Close … 作分類會比較好。

【參考】

 

Ed32. Copyright 2008 All Rights Reserved Revolution Two Church theme by Brian Gardner Converted into Blogger Template by Bloganol dot com