Барлық Qt әзірлеушілері Qt қолданбасын жөндеу кезінде qDebug() пайдаланады, бірақ сонымен қатар qInfo(), qWarning(), qCritical() және qFatal() макростары бар (олар мақаланы жазу уақыты қателермен болды және жұмыс істемеді).
Осы оқиғалардың көмегімен қателерді маңыздылық деңгейлері бойынша санаттауға және қандай қателер туралы хабарлану керек және қайсысы болмауы керек екенін бөлу үшін сүзгілерді қолдануға болады.
Қате туралы хабарларды мәтіндік файлға қайта бағыттау үшін қолданбада CallBack-функциясы өңдеушісін орнату керек. Ол үшін qInstalMessageHandler функциясы пайдаланылады.
Өңдеуші қолтаңбасы келесідей болуы керек:
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
Бұл функция арқылы біз келесі деректерді аламыз:
- QtMsgType түрі - Хабар түрі
- QtInfoMsg
- QtDebugMsg
- QtWarningMsg
- QtCriticalMsg
- QtFatalMsg
- QMessageLogContext &context - хабарламаның контексі, оның ішінде ең пайдалысы хабар категориясы. Бұл кодтағы хабарламаның орнын анықтау қажет болғанда пайдалы болуы мүмкін, яғни біз деректерді қай құрамдас бөліктерден аламыз немесе хабарлама қандай өзара әрекеттестік түріне жатады.
- QString &msg - жіберілген қате туралы хабар.
Қосымша санаттар - QLoggingCategory
Қосымша санаттар QLoggingCategory класының объектілері болып табылады. Категориялармен жұмыс істеудің бірнеше тәсілдері болуы мүмкін, мысалы, кейбір сыныпта объект санатын жасап, оны қате туралы хабарға бере аламыз:
QLoggingCategory m_category("Test"); qDebug(m_category) << "Check it";
Біз келесі нәтижені аламыз:
Test: Check it
Бұл тәсілдің кемшілігі бірнеше сыныптарда бірдей қате санаттары болуы мүмкін, бірақ әр сыныпта бірдей санаттарды жасау керек. Сондықтан біз басқа тәсілді қолданамыз. Атап айтқанда, макрос арқылы санатты тіркеу арқылы.
LoggingCategories.h
Біз осы тақырып сыныбында төрт санатты жариялаймыз.
#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
Және осы санаттарға атау беріңіз
#include "LoggingCategories.h" Q_LOGGING_CATEGORY(logDebug, "Debug") Q_LOGGING_CATEGORY(logInfo, "Info") Q_LOGGING_CATEGORY(logWarning, "Warning") Q_LOGGING_CATEGORY(logCritical, "Critical")
LoggingCategories пайдалану
Санат деректерін қолдану үшін санат деректері пайдаланылатын файлға тақырып файлын қосу керек. Содан кейін оларды пайдаланыңыз:
qDebug(logDebug()) << "Check it";
Біз келесі нәтижені аламыз:
Debug: Check it
Өңдеушіні орнату
Өңдеуші қалай жұмыс істейтінін көрсету үшін төрт түймесі бар графикалық қолданбаны қолданайық.
- Түзету
- Ақпарат
- Ескерту
- Критикалық
Түймелердің әрқайсысының өңдегішінде сәйкес оқиғаларды тіркеу макростары шақырылады және жоғарыда аталған сәйкес санаттар қолданылады. Қолданба терезесі графикалық дизайнер арқылы жасалады және келесідей көрінеді.
mainwindow.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
mainwindow.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(); // Очищаем буферизированные данные }
Барлығы
Оқиғаларды тіркеу нәтижесінде C:\example\logFile.txt файлы жасалады (Ескертпе: Бірақ C:\example қалтасы қолданба іске қосылғанға дейін бұрыннан бар болса ғана).
Бағдарлама терезесіндегі түймелерді басқан кезде файлдың мазмұны келесідей болады:
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 қолданбасын жүктеп алу
Добрый день. А как сделать, что бы ещё и в консоль выводилось?
Добрый день
Многого хотите ))) Установка обработчика же как раз для того, чтобы перенаправить вывод 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."
Условно да, для примера не важно, но больше выглядит как набор рекомендаций, который говорит о том, что в итоге что-то может пойти не так и вы сами себе злобные Буратины.