Evgenii Legotckoi
Evgenii Legotckoi05 червня 2016 р. 08:28

Qt/C++ - Урок 050. Реєстрація подій програми Qt у текстовий файл

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

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

Для перенаправлення повідомлень про помилки в текстовий файл, вам необхідно встановити CallBack-функцію обробник у програму. Для цього використовується функція qInstalMessageHandler .

Сигнатура обробника має виглядати так:

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

Через цю функцію ми будемо отримувати такі дані:

  1. QtMsgType type - Тип повідомлення
  2. QtInfoMsg
  3. QtDebugMsg
  4. QtWarningMsg
  5. QtCriticalMsg
  6. QtFatalMsg
  7. QMessageLogContext &context - контекст повідомлення, найкорисніше в якому це категорія повідомлення. Це може бути корисним, коли необхідно визначити розташування повідомлення в коді, тобто з яких компонентів ми отримуємо дані або до якого типу взаємодії відноситься повідомлення.
  8. 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 програму з логуванням у текстовий файл

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

C
  • 21 січня 2017 р. 05:51

Добрый день. А как сделать, что бы ещё и в консоль выводилось?

Evgenii Legotckoi
  • 21 січня 2017 р. 08:35

Добрый день

Многого хотите ))) Установка обработчика же как раз для того, чтобы перенаправить вывод qDebug в файл.
Вывод здесь забирается и перенаправляется в файл. Но если так сильно хочется выводить в консоль, то используйте std::cout в обработчике сообщений.

И
  • 15 серпня 2017 р. 07:08

Важно упомянуть, что QLoggingCategory в QT начиная с 5 версии.

Evgenii Legotckoi
  • 15 серпня 2017 р. 07:10

А кто-то ещё использует Qt4? )))

И
  • 15 серпня 2017 р. 07:14

Мало ли ) Мне для взаимодействия с библиотеками Octave приходится)

Evgenii Legotckoi
  • 15 серпня 2017 р. 07:23

Это который GNU Octave? Сочувствую тогда... в Qt5 много полезный вкусностей.

И
  • 15 серпня 2017 р. 07:40

Он самый

Evgenii Legotckoi
  • 15 серпня 2017 р. 07:44

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

Ruslan Polupan
  • 28 жовтня 2017 р. 04:23

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

Q_LOGGING_CATEGORY(logWarning,  "Предупреждение")
Evgenii Legotckoi
  • 28 жовтня 2017 р. 06:06

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

Но может Вам поможет вот этот топик .
Здесь скорее всего нужно правильно с кодировкой поколдовать.
p
  • 18 липня 2018 р. 08:45

А что мешает сохранить адрес дефолтного обработчика и после вывода в файл вызывать и его?

Evgenii Legotckoi
  • 18 липня 2018 р. 10:56

Хм.. Как-то даже не подумал ))

b
  • 01 серпня 2019 р. 01:57

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

b
  • 01 серпня 2019 р. 01:58

таки да. используется

Evgenii Legotckoi
  • 01 серпня 2019 р. 03:14

Мне видимо нужно было добавить тег /sarcasm ?
Всем ясно, что legaci есть и будет, но это не значит, что я, например, добровольно полезу болото месить.

МА
  • 03 червня 2020 р. 11:13

Вопрос- как перенести из файла .cpp

// Умный указатель на файл логирования
QScopedPointer<QFile>   m_logFile;
// Объявляение обработчика
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);

Эти записи?
Если их помещаю как private, то ошибка.
Или обработчик messageHandler должен именно вне класса быть? Хочу его методом класса сделать.
А так- работает.

МА
  • 03 червня 2020 р. 12:40

Как только переношу в класс метод messageHandler, он подчеркнут ошибкой в конструкторе:

qInstallMessageHandler(messageHandler);

Сам метод в классе называется уже:

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

с расширение названия класса.
И не пойму как в класс его затулить.

Evgenii Legotckoi
  • 03 червня 2020 р. 17:33

Этот обработчик должен быть отдельной функцией.
Не стоит его пытаться запихать в класс.
Как бы скорее всего это возможно, но это тухлого яйца не стоит.

МА
  • 04 червня 2020 р. 03:53

Отдельной ф-ей вне класса?
А если у меня еще класс будет, откуда я буду его использовать? То эту же ф-ю вызывать надо будет? Просто extern ее пробросить в то место, где вызываю?

Evgenii Legotckoi
  • 04 червня 2020 р. 04:00

Этот обработчик вызывается автоматически при вызове qDebug(), qWarning() и т.д.
Зачем вам вообще это куда-то пробрасывать?
Нет никакого смысла использовать класс. А даже если и будет класс, то метод должен быть статическим. Бессмысленная затея.

МА
  • 04 червня 2020 р. 04:21
    // настройка логгера
    // Устанавливаем файл логирования,
    m_logFile.reset(new QFile("logFile.txt"));
    // Открываем файл логирования
    m_logFile.data()->open(QFile::Append | QFile::Text);
    // Устанавливаем обработчик
    qInstallMessageHandler(messageHandler);

Это делаю в конструкторе класса. Если у меня два класса, откуда я хочу логгирование, то дважды вставляем в конструкторы инициализацию?
Или как правильней поступить?

МА
  • 04 червня 2020 р. 04:24

И вопрос по приватной переменной
QLoggingCategory m_category;
где в конструкторе она инициализируется- с каждым классом так проделывать?

Evgenii Legotckoi
  • 04 червня 2020 р. 04:37

Это глобальный логгер, а не для отдельных классов. Он один раз устанавливается в main функции.

Evgenii Legotckoi
  • 04 червня 2020 р. 04:38

В статье уже есть ответ на этот вопрос

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

МА
  • 04 червня 2020 р. 04:48

Эксперимент показал, что инициализация в конструкторе main достаточна. Дальше в конструкторе каждого класса делаю инициализацию приватной переменной m_category(" название класса:")
И уже в логгере отображается
2020-06-04 11:43:57.192 MainWindow: : Button Customers_clicked

Т.о. можно делать разделение по классам.

Разобрался, спасибо.

Отдельно отмечу, что ваш блог помогает разобраться с вопросами по Qt, отдельная благодарность)

AC
  • 24 червня 2023 р. 08:26

"И присвоим имена данным категориям "

Я правильно понимаю, что такие имена документация присваивать не рекомендует?
"Avoid the category names: debug, info, warning, and critical."

Evgenii Legotckoi
  • 24 червня 2023 р. 08:56

Условно да, для примера не важно, но больше выглядит как набор рекомендаций, который говорит о том, что в итоге что-то может пойти не так и вы сами себе злобные Буратины.

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах