Evgenii Legotckoi
Evgenii LegotckoiJune 5, 2016, 8:28 a.m.

Qt/C++ - Lesson 050. Logging Qt application events to a text file

All developers uses macro qDebug() , when debugging an application written in the Qt, but there are also macros qInfo(), qWarning(), qCritical() and qFatal() (which, as of this writing has errors and did not work).

With these events, you can divide errors in the application on the level of significance and use filters.

To redirect error messages to a text file, you need to install the CallBack-function handler into the application. For this purpose qInstalMessageHandler function.

The signature handler must be as follows:

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

Through it, we will receive the following information:

  1. QtMsgType type - Message Type
  2. QtInfoMsg
  3. QtDebugMsg
  4. QtWarningMsg
  5. QtCriticalMsg
  6. QtFatalMsg
  7. QMessageLogContext &context - context of a message, the most useful of it - this is the message category. What is important when working with data output can be used, and additional categories, due to which it is possible to improve the localization of posts in the component of the program, if you used the same number of messages.
  8. QString &msg - message transmitted in error logging

Additional categories - QLoggingCategory

Additional categories are objects QLoggingCategory class. There may be several approaches to work with the categories, for example, you can create a category object in any class and pass it to an error conclusion:

QLoggingCategory  m_category("Test");
qDebug(m_category) << "Check it";

Then we obtain the following output:

Test: Check it

Inconvenience approach is that each will generate a class data category and some grades they generally can be the same. Therefore, we use a different approach. Namely registration category by the macro.

LoggingCategories.h

We declare four different categories.

#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

Assign data names of categories.

#include "LoggingCategories.h"

Q_LOGGING_CATEGORY(logDebug,    "Debug")
Q_LOGGING_CATEGORY(logInfo,     "Info")
Q_LOGGING_CATEGORY(logWarning,  "Warning")
Q_LOGGING_CATEGORY(logCritical, "Critical")

Using LoggingCategories

In order to take advantage of these categories, you must include the header file in the file where we will use these categories. And use them as follows:

qDebug(logDebug()) << "Check it";

Then we obtain the following output:

Debug: Check it

Installing the handler

To demonstrate the handler we will use graphics application, which will has four buttons:

  • Debug
  • Info
  • Warning
  • Critical

In the handler of each button will cause a corresponding macros logging of events and to apply the appropriate categories listed above. Window application created by a graphic designer and is as follows.

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>

// Smart pointer to log file
QScopedPointer<QFile>   m_logFile;

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // Set the logging file
    // check which a path to file you use 
    m_logFile.reset(new QFile("C:/example/logFile.txt"));
    // Open the file logging
    m_logFile.data()->open(QFile::Append | QFile::Text);
    // Set handler
    qInstallMessageHandler(messageHandler);

    MainWindow w;
    w.show();

    return a.exec();
}

// The implementation of the handler
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    // Open stream file writes
    QTextStream out(m_logFile.data());
    // Write the date of recording
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
    // By type determine to what level belongs message
    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;
    }
    // Write to the output category of the message and the message itself
    out << context.category << ": "
        << msg << endl;
    out.flush();    // Clear the buffered data
}

Result

As a result, the file of logging events will created in the folliwnf path C:\example\logFile.txt.

By pressing the buttons in the application window, the contents of the file will look like this:

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

Download Qt application with logging into text file

Video lesson

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

C
  • Jan. 21, 2017, 5:51 a.m.

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

Evgenii Legotckoi
  • Jan. 21, 2017, 8:35 a.m.

Добрый день

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

И
  • Aug. 15, 2017, 7:08 a.m.

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

Evgenii Legotckoi
  • Aug. 15, 2017, 7:10 a.m.

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

И
  • Aug. 15, 2017, 7:14 a.m.

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

Evgenii Legotckoi
  • Aug. 15, 2017, 7:23 a.m.

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

И
  • Aug. 15, 2017, 7:40 a.m.

Он самый

Evgenii Legotckoi
  • Aug. 15, 2017, 7:44 a.m.

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

Ruslan Polupan
  • Oct. 28, 2017, 4:23 a.m.

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

Q_LOGGING_CATEGORY(logWarning,  "Предупреждение")
Evgenii Legotckoi
  • Oct. 28, 2017, 6:06 a.m.

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

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

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

Evgenii Legotckoi
  • July 18, 2018, 10:56 a.m.

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

b
  • Aug. 1, 2019, 1:57 a.m.

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

b
  • Aug. 1, 2019, 1:58 a.m.

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

Evgenii Legotckoi
  • Aug. 1, 2019, 3:14 a.m.

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

МА
  • June 3, 2020, 11:13 a.m.

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

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

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

МА
  • June 3, 2020, 12:40 p.m.

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

qInstallMessageHandler(messageHandler);

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

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

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

Evgenii Legotckoi
  • June 3, 2020, 5:33 p.m.

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

МА
  • June 4, 2020, 3:53 a.m.

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

Evgenii Legotckoi
  • June 4, 2020, 4 a.m.

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

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

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

МА
  • June 4, 2020, 4:24 a.m.

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

Evgenii Legotckoi
  • June 4, 2020, 4:37 a.m.

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

Evgenii Legotckoi
  • June 4, 2020, 4:38 a.m.

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

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

МА
  • June 4, 2020, 4:48 a.m.

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

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

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

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

AC
  • June 24, 2023, 8:26 a.m.

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

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

Evgenii Legotckoi
  • June 24, 2023, 8:56 a.m.

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

Comments

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

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 10:51 p.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiNov. 1, 2024, 12:37 a.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 6:19 p.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 5:51 p.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 9:02 p.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 25, 2024, 1:11 a.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 5:04 p.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 1:49 p.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 7:10 p.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks