Ruslan Polupan
July 12, 2019, 4:29 p.m.

IMpos project. Part 003. Configuring logging. Creation and reading of the application settings base

Logging setup

Logging support in the application allows you to at least solve the following tasks:

  • fixing user actions in the application;
  • fixing the performance of operations with data;
  • fixing critical events during program operation.

And in general, reading logs is an exciting and exciting process while providing support for the operation of the software.

I borrowed the organization of application event logging from this article . Slightly modifying the handler implementation so that messages are duplicated in the console window.


We add registration of logging categories to the project through a macro.

loggingcategories.h

  1. #ifndef LOGGINGCATEGORIES_H
  2. #define LOGGINGCATEGORIES_H
  3.  
  4. #include <QLoggingCategory>
  5.  
  6. Q_DECLARE_LOGGING_CATEGORY(logDebug)
  7. Q_DECLARE_LOGGING_CATEGORY(logInfo)
  8. Q_DECLARE_LOGGING_CATEGORY(logWarning)
  9. Q_DECLARE_LOGGING_CATEGORY(logCritical)
  10.  
  11. #endif // LOGGINGCATEGORIES_H
  12.  

loggingcategories.cpp

  1. #include "loggingcategories.h"
  2.  
  3. Q_LOGGING_CATEGORY(logDebug, "Debug")
  4. Q_LOGGING_CATEGORY(logInfo, "Info")
  5. Q_LOGGING_CATEGORY(logWarning, "Warning")
  6. Q_LOGGING_CATEGORY(logCritical, "Critical")

Change main.cpp.

main.cpp

  1. #include "mainwindow.h"
  2. #include "LoggingCategories/loggingcategories.h"
  3. #include <QApplication>
  4. #include <QFile>
  5. #include <QDateTime>
  6.  
  7. // Умный указатель на файл логирования
  8. static QScopedPointer<QFile> m_logFile;
  9.  
  10. // Объявление обработчика
  11. void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
  12.  
  13. int main(int argc, char *argv[])
  14. {
  15. QApplication a(argc, argv);
  16.  
  17. // Устанавливаем файл логирования
  18. m_logFile.reset(new QFile("iMpos.log"));
  19. // Открываем файл логирования
  20. m_logFile.data()->open(QFile::Append | QFile::Text);
  21. // Устанавливаем обработчик
  22. qInstallMessageHandler(messageHandler);
  23. qInfo(logInfo()) << "Запуск программы.";
  24.  
  25.  
  26. MainWindow w;
  27. w.show();
  28.  
  29. return a.exec();
  30. }
  31.  
  32. void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
  33. {
  34. // Открываем поток записи в файл
  35. QTextStream out(m_logFile.data());
  36. QTextStream console(stdout);
  37.  
  38.  
  39. // Записываем дату записи
  40. out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
  41. // По типу определяем, к какому уровню относится сообщение
  42. switch (type)
  43. {
  44. #ifdef QT_DEBUG
  45. case QtInfoMsg: out << "[INF] "; console << "Info: " << msg << endl; break;
  46. case QtDebugMsg: out << "[DBG] "; console << "Debug: " << msg << endl; break;
  47. case QtWarningMsg: out << "[WRN] "; console << "Warning: " << msg << endl; break;
  48. case QtCriticalMsg: out << "[CRT] "; console << "Critical: " << msg << endl; break;
  49. case QtFatalMsg: out << "[FTL] "; console << "Fatality: " << msg << endl; break;
  50. #else
  51. case QtInfoMsg: out << "[INF] "; break;
  52. case QtDebugMsg: out << "[DBG] "; break;
  53. case QtWarningMsg: out << "[WRN] "; break;
  54. case QtCriticalMsg: out << "[CRT] "; break;
  55. case QtFatalMsg: out << "[FTL] "; break;
  56. #endif
  57.  
  58. }
  59. // Записываем в вывод категорию сообщения и само сообщение
  60. out << context.category << ": " << msg << endl;
  61. // Очищаем буферизированные данные
  62. out.flush();
  63. console.flush();
  64. }

We start the program. Logging messages are displayed in the console.

An iMpos.log file has been created in the project's assembly folder

Create, read application settings database

We will use a SQLite database to store application settings and other data. As soon as we start using databases, we need to add the qmake QT += sql parameter to the project file.

  1. QT += core gui sql

Adding the DataBases class to the project
databases.h

  1. #ifndef DATABASES_H
  2. #define DATABASES_H
  3.  
  4. #include <QObject>
  5.  
  6. class DataBases : public QObject
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit DataBases(QObject *parent = nullptr);
  11. bool connectOptions(); //Подключение к базе данный опций приложения
  12.  
  13. signals:
  14.  
  15. public slots:
  16. };
  17.  
  18. #endif // DATABASES_H
  19.  

databases.cpp

  1. #include "databases.h"
  2. #include "LoggingCategories/loggingcategories.h"
  3.  
  4. #include <QFile>
  5. #include <QSqlQuery>
  6. #include <QSqlError>
  7.  
  8. #define DATABASE_NAME "iMpos.opt"
  9. #define DATABASE_HOSTNAME "iMpos"
  10.  
  11. DataBases::DataBases(QObject *parent) : QObject(parent)
  12. {
  13.  
  14. }
  15.  
  16. bool DataBases::connectOptions()
  17. {
  18. bool result;
  19.  
  20. //Проверяемналичие файла базы данных
  21. if(QFile(DATABASE_NAME).exists()){
  22. //Файл существует создаем подключение к базе данных
  23. qInfo(logInfo()) << "Открываем файл настроек приложения.";
  24.  
  25. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","options");
  26. db.setHostName(DATABASE_HOSTNAME);
  27. db.setDatabaseName(DATABASE_NAME);
  28. if(db.open()){
  29. qInfo(logInfo()) << "Файл настроек открыт успешно";
  30. result = true;
  31. } else {
  32. qCritical(logCritical()) << "Не удалось открыть файл настроек приложения.";
  33. result = false;
  34. }
  35. } else {
  36. //Файл отсутсвует, создем базу данных
  37. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","options");
  38. db.setHostName(DATABASE_HOSTNAME);
  39. db.setDatabaseName(DATABASE_NAME);
  40. if(db.open()){
  41. QStringList listSQL; //Список запросов
  42. QSqlQuery q = QSqlQuery(db);
  43. //Создаем таблицу OPTIONS и добавлем в нее записи
  44. listSQL << "CREATE TABLE `options` (`option_id` INTEGER NOT NULL, `value` TEXT NOT NULL, `comment` TEXT, PRIMARY KEY(`option_id`))";
  45. listSQL << "INSERT INTO `options`(`option_id`,`value`,`comment`) VALUES (1000, 'false', 'Использовать аутентификацию')";
  46. listSQL << "INSERT INTO `options`(`option_id`,`value`,`comment`) VALUES (1010, 'false', 'Использовать привязку по региону')";
  47. //Создаем таблицу пользователей приложения и добавляем в нее запись
  48. listSQL << "CREATE TABLE `users` ( `user_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `fio` TEXT NOT NULL, `password` TEXT, `isactive` TEXT NOT NULL DEFAULT 'true' )";
  49. listSQL << "INSERT INTO `users`(`fio`,`password`) VALUES ('Администратор','masterkey')";
  50.  
  51. //Выполняем запросы
  52. for (int i =0;i<listSQL.size();++i) {
  53. if(!q.exec(listSQL.at(i)))
  54. qCritical(logCritical()) << Q_FUNC_INFO << "Не удалось выполнить запрос." << listSQL.at(i) << q.lastError().text();
  55. }
  56. qInfo(logInfo()) << "Создан файл настроек приложения.";
  57. result = true;
  58. } else {
  59. qCritical(logCritical()) << "Не удалось создать файл настроек приложения.";
  60. result = false;
  61. }
  62.  
  63. }
  64.  
  65. return result;
  66. }
  67.  

In main.cpp, before exporting the main window, add

  1. DataBases *db = new DataBases();
  2. if(!db->connectOptions()){
  3. qInfo(logInfo()) << "Аварийное завершение работы.";
  4. return 1;
  5. }

The iMpos.opt file appears in the project build folder on the first run

The next time it starts, it connects to an existing SQLite database.

Current project status on GitHub here.

Project archive.
iMpos_ch003.zip iMpos_ch003.zip

By article asked0question(s)

2

Do you like it? Share on social networks!

Evgenii Legotckoi
  • July 12, 2019, 4:52 p.m.
  • (edited)

Я с некоторых пор перестал использовать вот такие дефайны (знаю, что в моих статьях попадаются, но это в старых статьях)

  1. #define DATABASE_NAME "iMpos.opt"
  2. #define DATABASE_HOSTNAME "iMpos"

Сейчас пишу так

Header

  1. class DataBaseSettings
  2. {
  3. public:
  4. static const QString NAME;
  5. static const QString HOSTNAME;
  6. }

CPP

  1. #include "DataBaseSettings.h"
  2.  
  3. const QString DataBaseSettings::NAME = "iMpos.opt";
  4. const QString DataBaseSettings::HOSTNAME = "iMpos";

Дело в том, что если изменить этот дефайн

  1. #define DATABASE_NAME "iMpos.opt"

То будут перекомпилироваться абсолютно все места в проекте, где он использовался. А в случае со статическими константными переменными будет перекомпилироваться только DataBaseSettings. Это даёт очень большой выигрыш по времени перекомпиляции в очень крупных проектах при частом использовании тех или иных переменных подобного рода.

Ruslan Polupan
  • July 12, 2019, 5:03 p.m.

Спасибо. Возьму на заметку.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup