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