Alle Qt-Entwickler verwenden qDebug() beim Debuggen einer Qt-Anwendung, aber es gibt auch Makros qInfo(), qWarning(), qCritical() und qFatal() (die bei der zum Zeitpunkt des Schreibens war der Artikel fehlerhaft und hat nicht funktioniert).
Mit diesen Ereignissen können Sie Fehler nach Signifikanzniveau kategorisieren und Filter anwenden, um zu trennen, welche Fehler gemeldet werden sollten und welche nicht.
Um Fehlermeldungen in eine Textdatei umzuleiten, müssen Sie den Handler CallBack-Funktion in der Anwendung installieren. Dazu wird die Funktion qInstalMessageHandler verwendet.
Die Handler-Signatur sollte wie folgt aussehen:
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
Durch diese Funktion erhalten wir folgende Daten:
- QtMsgType-Typ – Nachrichtentyp
- QtInfoMsg
- QtDebugMsg
- QtWarningMsg
- QtCriticalMsg
- QtFatalMsg
- QMessageLogContext &context – der Kontext der Nachricht, der nützlichste davon ist die Kategorie der Nachricht. Dies kann nützlich sein, wenn es notwendig ist, den Ort der Nachricht im Code zu bestimmen, d. h. von welchen Komponenten wir Daten erhalten oder zu welcher Art von Interaktion die Nachricht gehört.
- QString &msg - Fehlermeldung übergeben.
Zusätzliche Kategorien – QLoggingCategory
Zusätzliche Kategorien sind Objekte der Klasse QLoggingCategory. Es kann mehrere Ansätze für die Arbeit mit Kategorien geben, zum Beispiel können wir eine Objektkategorie in einer Klasse erstellen und sie an die Fehlermeldung übergeben:
QLoggingCategory m_category("Test"); qDebug(m_category) << "Check it";
Wir erhalten folgende Ausgabe:
Test: Check it
Der Nachteil dieses Ansatzes ist, dass mehrere Klassen die gleichen Fehlerkategorien haben können, aber in jeder Klasse die gleichen Kategorien angelegt werden müssen. Daher werden wir einen anderen Ansatz verwenden. Nämlich durch Registrieren einer Kategorie durch ein Makro.
LoggingCategories.h
Wir werden vier Kategorien in dieser Header-Klasse deklarieren.
#ifndef LOGGER_H #define LOGGER_H #include <QLoggingCategory> Q_DECLARE_LOGGING_CATEGORY(logDebug) Q_DECLARE_LOGGING_CATEGORY(logInfo) Q_DECLARE_LOGGING_CATEGORY(logWarning) Q_DECLARE_LOGGING_CATEGORY(logCritical) #endif // LOGGER_H
LoggingCategories.cpp
Und weisen Sie diesen Kategorien Namen zu
#include "LoggingCategories.h" Q_LOGGING_CATEGORY(logDebug, "Debug") Q_LOGGING_CATEGORY(logInfo, "Info") Q_LOGGING_CATEGORY(logWarning, "Warning") Q_LOGGING_CATEGORY(logCritical, "Critical")
LoggingCategories verwenden
Um die Kategoriedaten anzuwenden, müssen Sie die Header-Datei in die Datei einschließen, in der die Kategoriedaten verwendet werden. Und dann benutze sie:
qDebug(logDebug()) << "Check it";
Wir erhalten folgende Ausgabe:
Debug: Check it
Handler installieren
Um zu demonstrieren, wie der Handler funktioniert, verwenden wir eine grafische Anwendung mit vier Schaltflächen.
- Debuggen
- Die Info
- Warnung
- Kritisch
Im Handler jeder der Schaltflächen werden die entsprechenden Ereignisprotokollierungsmakros aufgerufen und die entsprechenden oben aufgeführten Kategorien angewendet. Das Anwendungsfenster wird von einem Grafikdesigner erstellt und sieht folgendermaßen aus.
Hauptfenster.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_debugButton_clicked(); void on_infoButton_clicked(); void on_warningButton_clicked(); void on_criticalButton_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
Hauptfenster.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> #include "LoggingCategories.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_debugButton_clicked() { qDebug(logDebug()) << "Debug Button"; } void MainWindow::on_infoButton_clicked() { qInfo(logInfo()) << "Info Button"; } void MainWindow::on_warningButton_clicked() { qWarning(logWarning()) << "Warning Button"; } void MainWindow::on_criticalButton_clicked() { qCritical(logCritical()) << "Critical Button"; }
main.cpp
#include "mainwindow.h" #include <QApplication> #include <QFile> #include <QDir> #include <QScopedPointer> #include <QTextStream> #include <QDateTime> #include <QLoggingCategory> // Умный указатель на файл логирования QScopedPointer<QFile> m_logFile; // Объявляение обработчика void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); int main(int argc, char *argv[]) { QApplication a(argc, argv); // Устанавливаем файл логирования, // внимательно сверьтесь с тем, какой используете путь для файла m_logFile.reset(new QFile("C:/example/logFile.txt")); // Открываем файл логирования m_logFile.data()->open(QFile::Append | QFile::Text); // Устанавливаем обработчик qInstallMessageHandler(messageHandler); MainWindow w; w.show(); return a.exec(); } // Реализация обработчика void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { // Открываем поток записи в файл QTextStream out(m_logFile.data()); // Записываем дату записи out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); // По типу определяем, к какому уровню относится сообщение switch (type) { case QtInfoMsg: out << "INF "; break; case QtDebugMsg: out << "DBG "; break; case QtWarningMsg: out << "WRN "; break; case QtCriticalMsg: out << "CRT "; break; case QtFatalMsg: out << "FTL "; break; } // Записываем в вывод категорию сообщения и само сообщение out << context.category << ": " << msg << endl; out.flush(); // Очищаем буферизированные данные }
Insgesamt
Als Ergebnis der Ereignisprotokollierung wird die Datei C:\example\logFile.txt erstellt (Hinweis: Aber nur, wenn der Ordner C:\example bereits vor dem Start der Anwendung existierte).
Wenn Sie auf die Schaltflächen im Anwendungsfenster klicken, sieht der Inhalt der Datei wie folgt aus:
2016-06-04 17:05:57.439 DBG Debug: Debug Button 2016-06-04 17:05:57.903 INF Info: Info Button 2016-06-04 17:05:58.367 WRN Warning: Warning Button 2016-06-04 17:05:58.815 CRT Critical: Critical Button
Qt-Anwendung mit Protokollierung in eine Textdatei herunterladen
Добрый день. А как сделать, что бы ещё и в консоль выводилось?
Добрый день
Многого хотите ))) Установка обработчика же как раз для того, чтобы перенаправить вывод qDebug в файл.
Вывод здесь забирается и перенаправляется в файл. Но если так сильно хочется выводить в консоль, то используйте std::cout в обработчике сообщений.
Важно упомянуть, что QLoggingCategory в QT начиная с 5 версии.
А кто-то ещё использует Qt4? )))
Мало ли ) Мне для взаимодействия с библиотеками Octave приходится)
Это который GNU Octave? Сочувствую тогда... в Qt5 много полезный вкусностей.
Он самый
Я полистал информацию в интернетах, вроде как кто-то пытается подружить его с Qt5, но успешных результатов не нашёл. Да и на сайте как-то не заметно информации о том, что конкретно ему нужно, какая именно версия Qt требуется.
Как здесь кириллицу корректно использовать?
Фух фух фух.... не люблю я для таких вещей кириллицу использовать ;-)
А что мешает сохранить адрес дефолтного обработчика и после вывода в файл вызывать и его?
Хм.. Как-то даже не подумал ))
таки да, используется, причем активно
таки да. используется
Мне видимо нужно было добавить тег /sarcasm ?
Всем ясно, что legaci есть и будет, но это не значит, что я, например, добровольно полезу болото месить.
Вопрос- как перенести из файла .cpp
Эти записи?
Если их помещаю как private, то ошибка.
Или обработчик messageHandler должен именно вне класса быть? Хочу его методом класса сделать.
А так- работает.
Как только переношу в класс метод messageHandler, он подчеркнут ошибкой в конструкторе:
Сам метод в классе называется уже:
с расширение названия класса.
И не пойму как в класс его затулить.
Этот обработчик должен быть отдельной функцией.
Не стоит его пытаться запихать в класс.
Как бы скорее всего это возможно, но это тухлого яйца не стоит.
Отдельной ф-ей вне класса?
А если у меня еще класс будет, откуда я буду его использовать? То эту же ф-ю вызывать надо будет? Просто extern ее пробросить в то место, где вызываю?
Этот обработчик вызывается автоматически при вызове qDebug(), qWarning() и т.д.
Зачем вам вообще это куда-то пробрасывать?
Нет никакого смысла использовать класс. А даже если и будет класс, то метод должен быть статическим. Бессмысленная затея.
Это делаю в конструкторе класса. Если у меня два класса, откуда я хочу логгирование, то дважды вставляем в конструкторы инициализацию?
Или как правильней поступить?
И вопрос по приватной переменной
QLoggingCategory m_category;
где в конструкторе она инициализируется- с каждым классом так проделывать?
Это глобальный логгер, а не для отдельных классов. Он один раз устанавливается в main функции.
В статье уже есть ответ на этот вопрос
Эксперимент показал, что инициализация в конструкторе main достаточна. Дальше в конструкторе каждого класса делаю инициализацию приватной переменной m_category(" название класса:")
И уже в логгере отображается
2020-06-04 11:43:57.192 MainWindow: : Button Customers_clicked
Т.о. можно делать разделение по классам.
Разобрался, спасибо.
Отдельно отмечу, что ваш блог помогает разобраться с вопросами по Qt, отдельная благодарность)
"И присвоим имена данным категориям "
Я правильно понимаю, что такие имена документация присваивать не рекомендует?
"Avoid the category names: debug, info, warning, and critical."
Условно да, для примера не важно, но больше выглядит как набор рекомендаций, который говорит о том, что в итоге что-то может пойти не так и вы сами себе злобные Буратины.