Проект iMpos. Часть 003. Настройка логирования. Создание, чтение базы настроек приложения

database, QSQLITE, Qt, Logs

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

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

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

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

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

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

loggingcategories.h

#ifndef LOGGINGCATEGORIES_H
#define LOGGINGCATEGORIES_H

#include <QLoggingCategory>

Q_DECLARE_LOGGING_CATEGORY(logDebug)
Q_DECLARE_LOGGING_CATEGORY(logInfo)
Q_DECLARE_LOGGING_CATEGORY(logWarning)
Q_DECLARE_LOGGING_CATEGORY(logCritical)

#endif // LOGGINGCATEGORIES_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")

Изменяем main.cpp.

main.cpp

#include "mainwindow.h"
#include "LoggingCategories/loggingcategories.h"
#include <QApplication>
#include <QFile>
#include <QDateTime>

// Умный указатель на файл логирования
static 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("iMpos.log"));
   // Открываем файл логирования
   m_logFile.data()->open(QFile::Append | QFile::Text);
   // Устанавливаем обработчик
   qInstallMessageHandler(messageHandler);
   qInfo(logInfo()) << "Запуск программы.";


   MainWindow w;
   w.show();

   return a.exec();
}

void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
   // Открываем поток записи в файл
   QTextStream out(m_logFile.data());
   QTextStream console(stdout);


   // Записываем дату записи
   out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
   // По типу определяем, к какому уровню относится сообщение
   switch (type)
   {
#ifdef QT_DEBUG
   case QtInfoMsg:     out << "[INF] "; console << "Info:     " << msg << endl; break;
   case QtDebugMsg:    out << "[DBG] "; console << "Debug:    " << msg << endl; break;
   case QtWarningMsg:  out << "[WRN] "; console << "Warning:  " << msg << endl; break;
   case QtCriticalMsg: out << "[CRT] "; console << "Critical: " << msg << endl; break;
   case QtFatalMsg:    out << "[FTL] "; console << "Fatality: " << msg << endl; break;
#else
   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;
#endif

   }
   // Записываем в вывод категорию сообщения и само сообщение
   out << context.category << ": " << msg << endl;
   // Очищаем буферизированные данные
   out.flush();
   console.flush();
}

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

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

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

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

QT       += core gui sql

Добавляем в проект класс DataBases
databases.h

#ifndef DATABASES_H
#define DATABASES_H

#include <QObject>

class DataBases : public QObject
{
    Q_OBJECT
public:
    explicit DataBases(QObject *parent = nullptr);
    bool connectOptions(); //Подключение к базе данный опций приложения

signals:

public slots:
};

#endif // DATABASES_H

databases.cpp

#include "databases.h"
#include "LoggingCategories/loggingcategories.h"

#include <QFile>
#include <QSqlQuery>
#include <QSqlError>

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

DataBases::DataBases(QObject *parent) : QObject(parent)
{

}

bool DataBases::connectOptions()
{
    bool result;

    //Проверяемналичие файла базы данных
    if(QFile(DATABASE_NAME).exists()){
        //Файл существует создаем подключение к базе данных
        qInfo(logInfo()) << "Открываем файл настроек приложения.";

        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","options");
        db.setHostName(DATABASE_HOSTNAME);
        db.setDatabaseName(DATABASE_NAME);
        if(db.open()){
            qInfo(logInfo()) << "Файл настроек открыт успешно";
            result = true;
        } else {
            qCritical(logCritical()) << "Не удалось открыть файл настроек приложения.";
            result = false;
        }
    } else {
        //Файл отсутсвует, создем базу данных
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","options");
        db.setHostName(DATABASE_HOSTNAME);
        db.setDatabaseName(DATABASE_NAME);
        if(db.open()){
            QStringList listSQL;  //Список запросов
            QSqlQuery q = QSqlQuery(db);
            //Создаем таблицу OPTIONS и добавлем в нее записи
            listSQL << "CREATE TABLE `options` (`option_id` INTEGER NOT NULL, `value` TEXT NOT NULL, `comment` TEXT, PRIMARY KEY(`option_id`))";
            listSQL << "INSERT INTO `options`(`option_id`,`value`,`comment`) VALUES (1000, 'false', 'Использовать аутентификацию')";
            listSQL << "INSERT INTO `options`(`option_id`,`value`,`comment`) VALUES (1010, 'false', 'Использовать привязку по региону')";
            //Создаем таблицу пользователей приложения и добавляем в нее запись
            listSQL << "CREATE TABLE `users` ( `user_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `fio` TEXT NOT NULL, `password` TEXT, `isactive` TEXT NOT NULL DEFAULT 'true' )";
            listSQL << "INSERT INTO `users`(`fio`,`password`) VALUES ('Администратор','masterkey')";

            //Выполняем запросы
            for (int i =0;i<listSQL.size();++i) {
                if(!q.exec(listSQL.at(i)))
                    qCritical(logCritical()) << Q_FUNC_INFO << "Не удалось выполнить запрос." << listSQL.at(i) << q.lastError().text();
            }
            qInfo(logInfo()) << "Создан файл настроек приложения.";
            result = true;
        } else {
            qCritical(logCritical()) << "Не удалось создать файл настроек приложения.";
            result = false;
        }

    }

    return result;
}

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

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

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

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

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

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

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.

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

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

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

Header

class DataBaseSettings
{
public:
    static const QString NAME;
    static const QString HOSTNAME;
}

CPP

#include "DataBaseSettings.h"

const QString DataBaseSettings::NAME = "iMpos.opt";
const QString DataBaseSettings::HOSTNAME = "iMpos";

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

#define DATABASE_NAME "iMpos.opt"

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

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

Comments

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

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

VZ
Nov. 17, 2019, 2:25 a.m.
Vladimir Zhitkovsky

Qt - Test 001. Signals and slots

  • Result:78points,
  • Rating points2
m
Nov. 17, 2019, 2:20 a.m.
masha16

C++ - Test 002. Constants

  • Result:50points,
  • Rating points-4
m
Nov. 17, 2019, 2:15 a.m.
masha16

C++ - Test 001. The first program and data types

  • Result:53points,
  • Rating points-4
Last comments
c
Nov. 17, 2019, 2:25 p.m.
cyberaxe77

Шикарно! Как обычно всё объясняется чётко, ясно и, по-возможности, кратко. Побольше бы контента по PyQt5...
GV
Nov. 16, 2019, 4:16 p.m.
Gibraltar Vol'framov

Разрешите узнать, как вы получили qt+qml приложение размером в 10мб(даже релизных). Дело в том, что пустое приложение, хоть и со стек вью, обходится мне на все 40мб пространства. Или же вы воспо…
b
Nov. 9, 2019, 8:28 a.m.
bastonc

спасибо ещё раз. огромное, за уделённое время
b
Nov. 9, 2019, 8:24 a.m.
bastonc

Спасибо Вам большое. Буду изучать.
Nov. 9, 2019, 5:58 a.m.
Evgenij Legotskoj

Добрый день. По первым двум вопросам вы найдёте ответ в этой статье - PyQt5 - Урок 008. Работа с QTableWidget (Обновление урока 006) Что касается последнего вопроса, то я вам…
Now discuss on the forum
Nov. 17, 2019, 12:30 p.m.
Mihail Sermjazhko

Взял этот код https://github.com/promixis/Code128/tree/master/src и сделал так m_Barcode = new Code128Item(); m_Barcode->setWidth( 200 ); m_Barcode->setHeight( 80 ); m_Barco…
s
Nov. 16, 2019, 5:58 a.m.
sladkoewka

Благодарю! Пойду читать всю эту документацию, чтобы понять, как это вообще работает и дальше мучать свой прогресс бар.
Nov. 15, 2019, 7:37 a.m.
Intruder

Евгений, почитав про эту проблему пришел к выводу, что либо нужно говорить очередь, либо все вернуть из библиотеки (dll в моем случае) в приложение, потому что в приложении все работает просто з…
Nov. 15, 2019, 6:06 a.m.
Evgenij Legotskoj

Ну тогда не знаю )) Я большую часть времени на C++ с Qt работаю, а PyQt5 у меня боком. Так что, чем можем помочь ))
H
Nov. 15, 2019, 5:18 a.m.
Hyperfish

Да, пробовал, с преобразованием int array[]={1,2,3,4,5,6,7} в jintArray(array). Если так, то программа компилируется без ошибок и предупреждений, но вываливается с ошибкой времени выполнени…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB