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
#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")
Change 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(); }
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.
QT += core gui sql
Adding the DataBases class to the project
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; }
In main.cpp, before exporting the main window, add
DataBases *db = new DataBases(); if(!db->connectOptions()){ qInfo(logInfo()) << "Аварийное завершение работы."; return 1; }
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
Я с некоторых пор перестал использовать вот такие дефайны (знаю, что в моих статьях попадаются, но это в старых статьях)
Сейчас пишу так
Header
CPP
Дело в том, что если изменить этот дефайн
То будут перекомпилироваться абсолютно все места в проекте, где он использовался. А в случае со статическими константными переменными будет перекомпилироваться только DataBaseSettings. Это даёт очень большой выигрыш по времени перекомпиляции в очень крупных проектах при частом использовании тех или иных переменных подобного рода.
Спасибо. Возьму на заметку.