Реклама

Qt/C++ - Урок 050. Логирование событий Qt приложения в текстовый файл

file, log, Logger, qDebug, qFatal, qInfo, qWarning, Логирование

Все Qt-разработчики используют qDebug(), когда отлаживают приложение, написанное на Qt, но также имеются макросы qInfo(), qWarning(), qCritical() и qFatal() (который на момент написания статьи был с ошибками и не работал).

С помощью этих событий, вы можете разделить ошибки по уровням значимости и применить фильтры, для разделения того, какие ошибки нужно выводить, а какие нет.

Для перенаправления сообщений об ошибках в текстовый файл, вам необходимо установить CallBack-функцию обработчик в приложение. Для этого используется функция qInstalMessageHandler .

Сигнатура обработчика должна выглядеть следующий образом:

void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);

Через эту функцию мы будем получать следующие данные:

  1. QtMsgType type - Тип сообщения
    1. QtInfoMsg
    2. QtDebugMsg
    3. QtWarningMsg
    4. QtCriticalMsg
    5. QtFatalMsg
  2. QMessageLogContext &context - контекст сообщения, самое полезное в котором - это категория сообщения. Это может быть полезным, когда необходимо определить местоположение сообщения в коде, то есть из каких компонентов мы получаем данные или к какому типу взаимодействия относится сообщение.
  3. 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

Установка обработчика

Для демонстрации работы обработчика воспользуемся графическим приложением, в котором будет четыре кнопки.

  • Debug
  • Info
  • Warning
  • Critical

В обработчике каждой из кнопок будут вызываться соответствующие макросы логирования событий и применяться соответствующие категории, перечисленные выше. Окно приложение создано через графический дизайнер и выглядит следующим образом.

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 требуется.

  • #
  • 28 октября 2017 г. 8:23

Как здесь кириллицу корректно использовать?

Q_LOGGING_CATEGORY(logWarning,  "Предупреждение")

Фух фух фух.... не люблю я для таких вещей кириллицу использовать ;-)

Но может Вам поможет вот этот топик .
Здесь скорее всего нужно правильно с кодировкой поколдовать.

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
  • falcon
  • 16 января 2018 г. 17:25

Qt - Тест 001. Сигналы и слоты

  • Результат 100 баллов
  • Очки рейтинга 10
  • falcon
  • 16 января 2018 г. 17:22

Qt - Тест 001. Сигналы и слоты

  • Результат 68 баллов
  • Очки рейтинга -1
  • falcon
  • 16 января 2018 г. 17:18

C++ - Тест 001. Первая программа и типы данных

  • Результат 73 баллов
  • Очки рейтинга 1
Последние комментарии

QML - Урок 021. Переключение между окнами в QML

Спасибо всем. Все получилось. Прикручиваю логику.

  • BlinCT
  • 14 января 2018 г. 19:28

Разработка на Qt под iOS

Вот честно, на сколько же муторно под огрызок что то делать. Куча проблем) А вод линь или под Андроид все просто и тривиально))

  • folax
  • 12 января 2018 г. 9:16

QML - Урок 021. Переключение между окнами в QML

Ничего сложного, делаете по тех заданию 3 файла qml, называете их как указанно в тех задании, потом из первого окна через Loader их переключаете, в окне 2 и 3 делаете сигналы которые при закры...

QML - Урок 021. Переключение между окнами в QML

Все верно, я и не говорил что этот кусок кода лично мое произведение. Это тоже верно: Это задание для прохождения на собеседование в одну из крупных украинских IT компаний. Логику ...

  • folax
  • 12 января 2018 г. 8:13

QML - Урок 021. Переключение между окнами в QML

int main(int argc, char *argv[]){ QApplication app(argc, argv); Logic logic; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("logic", &logic)...

Сейчас обсуждают на форуме

ChartView. Отображение метки данных точки серии при наведении курсора

Спасибо большущее за советы! Все получилось через ScatterSeries. Методы remove() как-то сходу не дались, удаляет в первый раз, а потом программа падает... Не стал тратить время и воспользовалс...

  • EVILEG
  • 16 января 2018 г. 14:23

Как проверить доступность сервера

Добрый день! Теоретически можно использовать QTcpSocket, у него есть метод connectToHost. Возможно, что проверка доступности через этот класс будет осуществляться несколько быстрее,...

QGraphicsScene

спасибо, за подробное объяснение строчки, а с зумом я разобрался, все работает

  • EVILEG
  • 15 января 2018 г. 17:21

Qt webgl

Насчёт проверки подключения клиента я не в курсе. Что касается экземпляров приложения, то из того, что я читал получается, что нет необходимости в нескольких экземплярах для нескольких кл...

  • EVILEG
  • 15 января 2018 г. 11:39

Проблема добавления #DEFINE при сборке CMak'ом

А Вы не пробовали сделать предкомпилированные библиотеки boost под свою систему, а потом уже подключать собранные библиотеки Boost`а? Просто один только boost может собираться на пару гиг...