Ruslan Polupan
Шілде 12, 2019, 4:29 Т.Қ.

iMpos жобасы. Бөлім 003. Журнал жүргізуді орнату. Қолданба параметрлерінің мәліметтер базасын құру, оқу

Настройка логирования

Поддержка логирования в приложении позволяет как минимум решать следующие задачи:

  • фиксирование действий пользователя в приложении;
  • фиксирование выполнения операций с данными;
  • фиксирование критических событий при работе программы.

И вообще чтение логов волнующий и захватывающий процесс при обеспечении поддержки работы программного обеспечения.

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


Добавляем в проект регистрацию категорий логирования через макрос.

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")

Изменяем 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. }

Запускаем программу. В консоли отображаются сообщения логирования.

В папке собрки проекта создался файл iMpos.log

Создание, чтение базы данных настроек приложение

Для хранения настроек приложения и прочих данных будем использовать базу данных SQLite. Как только начинаем использовать базы данных необходимо добавить в файл проекта параметр qmake QT += sql.

  1. QT += core gui sql

Добавляем в проект класс DataBases
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.  

В main.cpp перед вывозом гланого окна добавляем

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

В папке сборки проекта при первом запуске появляется файл iMpos.opt

При следующем запуске происходит подключение к существующей базе SQLite.

Текущее состояние проекта на GitHub здесь.

Архив проекта.
iMpos_ch003.zip iMpos_ch003.zip

Мақала бойынша сұралады0сұрақтар(лар)

2

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Evgenii Legotckoi
  • Шілде 12, 2019, 4:52 Т.Қ.
  • (өңделген)

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

  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
  • Шілде 12, 2019, 5:03 Т.Қ.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз