Настройка логирования
Поддержка логирования в приложении позволяет как минимум решать следующие задачи:
- фиксирование действий пользователя в приложении;
- фиксирование выполнения операций с данными;
- фиксирование критических событий при работе программы.
И вообще чтение логов волнующий и захватывающий процесс при обеспечении поддержки работы программного обеспечения.
Организацию логирования событий приложения я позаимствовал из этой статьи . Немного модифицировав Реализацию обработчика для того чтобы сообщения дублировались в окно консоли.
Добавляем в проект регистрацию категорий логирования через макрос.
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
Я с некоторых пор перестал использовать вот такие дефайны (знаю, что в моих статьях попадаются, но это в старых статьях)
Сейчас пишу так
Header
CPP
Дело в том, что если изменить этот дефайн
То будут перекомпилироваться абсолютно все места в проекте, где он использовался. А в случае со статическими константными переменными будет перекомпилироваться только DataBaseSettings. Это даёт очень большой выигрыш по времени перекомпиляции в очень крупных проектах при частом использовании тех или иных переменных подобного рода.
Спасибо. Возьму на заметку.