commit 83161921a11fd0048cf477800592ed31bef41d2f Author: Jiga228 Date: Thu Aug 13 14:35:01 2026 +0700 Init public repo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ef76f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CC = g++ + +all: app Saver clean + +clean: + rm -rf *.o + +app: Saver + ${CC} src/app/UserInput/UserInput.cpp -o UserInput.o -c + ${CC} src/app/SaverLib.cpp -o SaverLib.o -c + ${CC} src/app/main.cpp -o main.o -c + ${CC} src/app/SaveThread/SaveThread.cpp -o SaveThread.o -c + ${CC} main.o SaveThread.o SaverLib.o UserInput.o -o app -ldl + +Saver: + ${CC} src/Saver/Saver.cpp -o Saver.o -fPIC -c + ${CC} src/Saver/SaverBuilder.cpp -o SaverBuilder.o -fPIC -c + ${CC} -shared Saver.o SaverBuilder.o -o libsaver.so diff --git a/README.md b/README.md new file mode 100644 index 0000000..77a7174 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Saver +Это программа для логирования сообщений + +# Сборка +Запустите в корне make + +# Использование + +```bash + ./app +``` + +Существует 3 уровня важности: +- Low +- Middle +- High + +Далее программа будет поочерёдно просить ввести сообщение и его важность. Если ничего не вводить или ввести не коректно в поле важности то будет присовено значение base_importance. + + diff --git a/src/Saver/ISaver.h b/src/Saver/ISaver.h new file mode 100644 index 0000000..4941039 --- /dev/null +++ b/src/Saver/ISaver.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +/* + * Интерфейс сохранялки. + */ + +class ISaver +{ +public: + enum ImportanceMessage + { + Low = 0, + Middle = 1, + High = 2 + }; + + /* + * @throw std::runtime_error + */ + inline static std::string ImportanceMessageToString(ImportanceMessage val) + { + switch (val) + { + case Low: + return "Low"; + case Middle: + return "Middle"; + case High: + return "High"; + } + throw std::runtime_error("[ISaver::ImportanceMessageToString] invalid value"); + } + /* + * @throw std::runtime_error + */ + inline static ImportanceMessage StringToImportanceMessage(const std::string& str) + { + if (str == "Low") + return Low; + else if (str == "Middle") + return Middle; + else if (str == "High") + return High; + throw std::runtime_error("[ISaver::StringToImportanceMessage] Invalid importance value"); + } + + virtual ~ISaver() = default; + + /* + * Может бросать исключения. + */ + virtual void save(const std::string& msg, ImportanceMessage importance) = 0; + virtual void set_base_importance(ImportanceMessage min_importance) = 0; +}; diff --git a/src/Saver/Saver.cpp b/src/Saver/Saver.cpp new file mode 100644 index 0000000..219747e --- /dev/null +++ b/src/Saver/Saver.cpp @@ -0,0 +1,46 @@ +#include "Saver.h" + +#include +#include +#include +#include + +Saver::Saver(const std::string& log_file, ImportanceMessage min_importance): +log_file_(log_file), +min_importance_(min_importance) +{} + +void Saver::save(const std::string& msg, ImportanceMessage importance) +{ + if (importance < min_importance_.load()) + return; + + std::scoped_lock lock(m_db_file_); + + std::ofstream file(log_file_, std::ios::app); + if (file.is_open() == false) + throw std::runtime_error("[Saver::save] Fail open" + log_file_); + + // Получение текущего времени и преобразование его в строку + auto now = std::chrono::system_clock::now(); + std::time_t t_c = std::chrono::system_clock::to_time_t(now); + + char* date = ctime(&t_c); + for (int i = 0; date[i] != 0; ++ i) + { + if (date[i] == '\n') + { + date[i] = 0; + break; + } + } + + file << "[" << date << "] [" << ImportanceMessageToString(importance) << "] " << msg << '\n'; + file.close(); +} + +void Saver::set_base_importance(ImportanceMessage min_importance) +{ + min_importance_.store(min_importance); +} + diff --git a/src/Saver/Saver.h b/src/Saver/Saver.h new file mode 100644 index 0000000..bd1d7dc --- /dev/null +++ b/src/Saver/Saver.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ISaver.h" + +#include +#include + +/* + * Реализация сохранялки. + * Класс потокобезопасен. + */ + +class Saver : public ISaver +{ + std::mutex m_db_file_; + std::string log_file_; + std::atomic min_importance_; +public: + Saver(const std::string& log_file, ImportanceMessage min_importance); + + /* + * @throw std::runtime_error("Fail open ") + */ + void save(const std::string& msg, ImportanceMessage importance) override; + + void set_base_importance(ImportanceMessage min_importance) override; +}; diff --git a/src/Saver/SaverBuilder.cpp b/src/Saver/SaverBuilder.cpp new file mode 100644 index 0000000..bdb88aa --- /dev/null +++ b/src/Saver/SaverBuilder.cpp @@ -0,0 +1,20 @@ +#include "Saver.h" + +#include +#include + +static ISaver* saver_obj = nullptr; +static std::mutex m_saver_obj; + +extern "C" { + ISaver* BuildSaver(const std::string& log_file, ISaver::ImportanceMessage min_importance) + { + if (saver_obj == nullptr) + { + std::scoped_lock lock(m_saver_obj); + if (saver_obj == nullptr) + saver_obj = new Saver(log_file, min_importance); + } + return saver_obj; + } +} diff --git a/src/app/SaveThread/ISaveThread.h b/src/app/SaveThread/ISaveThread.h new file mode 100644 index 0000000..1703c13 --- /dev/null +++ b/src/app/SaveThread/ISaveThread.h @@ -0,0 +1,11 @@ +#pragma once + +#include "../../Saver/ISaver.h" + +class ISaveThread +{ +public: + virtual ~ISaveThread() = default; + + virtual void save(const std::string& msg, ISaver::ImportanceMessage importance) = 0; +}; diff --git a/src/app/SaveThread/SaveThread.cpp b/src/app/SaveThread/SaveThread.cpp new file mode 100644 index 0000000..811c867 --- /dev/null +++ b/src/app/SaveThread/SaveThread.cpp @@ -0,0 +1,46 @@ +#include "SaveThread.h" + +#include +#include + +SaveThread::SaveThread(SaverLib& saver) : saver_(saver) +{ + is_runing_.store(true); + save_thread_ = std::make_unique([this] { SaveThreadMethod(); }); +} + +void SaveThread::SaveThreadMethod() +{ + // поток может завершится только если не будет сообщений в очереди, иначе он сначала все сохранит + while(is_runing_.load() || queue_.empty() == false) + { + // Небольшая задержка для разгрузки CPU + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::scoped_lock lock(m_queue_); + if (queue_.empty()) + continue; + + Msg msg = queue_.front(); + try { + saver_->save(msg.msg, msg.importance); + queue_.pop_front(); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + continue; + } + } +} + +SaveThread::~SaveThread() +{ + is_runing_.store(false); + save_thread_->join(); +} + +void SaveThread::save(const std::string& msg, ISaver::ImportanceMessage importance) +{ + std::scoped_lock lock(m_queue_); + queue_.push_back(Msg {msg, importance}); +} diff --git a/src/app/SaveThread/SaveThread.h b/src/app/SaveThread/SaveThread.h new file mode 100644 index 0000000..639962e --- /dev/null +++ b/src/app/SaveThread/SaveThread.h @@ -0,0 +1,35 @@ +#pragma once + +#include "ISaveThread.h" +#include "../SaverLib.h" + +#include +#include +#include +#include +#include + +class SaveThread : public ISaveThread +{ + struct Msg + { + std::string msg; + ISaver::ImportanceMessage importance; + }; + SaverLib& saver_; + + std::list queue_; + std::mutex m_queue_; + + /* + * Поток сохранения логов + */ + std::unique_ptr save_thread_; + void SaveThreadMethod(); + std::atomic_bool is_runing_; +public: + SaveThread(SaverLib& saver); + ~SaveThread(); + + void save(const std::string& msg, ISaver::ImportanceMessage importance) override; +}; diff --git a/src/app/SaverLib.cpp b/src/app/SaverLib.cpp new file mode 100644 index 0000000..0216d2e --- /dev/null +++ b/src/app/SaverLib.cpp @@ -0,0 +1,24 @@ +#include "SaverLib.h" + +#include + +typedef ISaver*(*BuildSaver)(const std::string&, ISaver::ImportanceMessage); + +SaverLib::SaverLib(const std::string& log_file, ISaver::ImportanceMessage importance) +{ + handle = dlopen("./libsaver.so", RTLD_LAZY); + if (handle == nullptr) + throw std::runtime_error(dlerror()); + + BuildSaver builder = reinterpret_cast(dlsym(handle, "BuildSaver")); + if(builder == nullptr) + throw std::runtime_error(dlerror()); + + saver = builder(log_file, importance); +} + +SaverLib::~SaverLib() +{ + delete saver; + dlclose(handle); +} \ No newline at end of file diff --git a/src/app/SaverLib.h b/src/app/SaverLib.h new file mode 100644 index 0000000..445d7b6 --- /dev/null +++ b/src/app/SaverLib.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../Saver/ISaver.h" + +class SaverLib +{ + void* handle; + ISaver* saver; +public: + SaverLib(const std::string& log_file, ISaver::ImportanceMessage importance); + inline ISaver* operator->() + { + return saver; + } + ~SaverLib(); +}; \ No newline at end of file diff --git a/src/app/UserInput/UserInput.cpp b/src/app/UserInput/UserInput.cpp new file mode 100644 index 0000000..2c88631 --- /dev/null +++ b/src/app/UserInput/UserInput.cpp @@ -0,0 +1,36 @@ +#include "UserInput.h" + +#include +#include + +UserInput::UserInput(ISaveThread& save_thread, ISaver::ImportanceMessage base_importance) : save_thread_(save_thread), base_importance_(base_importance) {} + +void UserInput::start() +{ + while(true) + { + std::string msg, str_importance; + + std::cout << "Enter message (Enter \"" << quit_msg << "\" for quit)> "; + std::getline(std::cin, msg); + if(quit_msg == msg) + return; + + std::cout << "Enter importance (default: " << ISaver::ImportanceMessageToString(base_importance_) << ")> "; + std::getline(std::cin, str_importance); + + // Set default value + ISaver::ImportanceMessage importance = base_importance_; + if(str_importance.empty() == false) + { + try { + importance = ISaver::StringToImportanceMessage(str_importance); + } catch (const std::exception& e) { + std::cout << e.what() << '\n'; + importance = base_importance_; + std::cout << "Set The value has been set " << ISaver::ImportanceMessageToString(base_importance_) << '\n'; + } + } + save_thread_.save(msg, importance); + } +} \ No newline at end of file diff --git a/src/app/UserInput/UserInput.h b/src/app/UserInput/UserInput.h new file mode 100644 index 0000000..9eaafb8 --- /dev/null +++ b/src/app/UserInput/UserInput.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../SaveThread/ISaveThread.h" + +class UserInput +{ + ISaveThread& save_thread_; + ISaver::ImportanceMessage base_importance_; + + const char* quit_msg = "!quit"; +public: + UserInput(ISaveThread& save_thread, ISaver::ImportanceMessage base_importance); + void start(); +}; \ No newline at end of file diff --git a/src/app/main.cpp b/src/app/main.cpp new file mode 100644 index 0000000..bc31c7d --- /dev/null +++ b/src/app/main.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "SaveThread/SaveThread.h" +#include "SaverLib.h" +#include "UserInput/UserInput.h" + +int main(int argc, char* argv[]) +{ + if (argc < 3) + { + std::cerr << "app \n"; + return 0; + } + + std::string str_importance = argv[2]; + ISaver::ImportanceMessage base_importance = ISaver::StringToImportanceMessage(str_importance); + SaverLib saver_lib(argv[1], base_importance); + + SaveThread save_thread(saver_lib); + + //save_thread.save("test_low", ISaver::ImportanceMessage::Low); + //save_thread.save("test_high", ISaver::ImportanceMessage::High); + + UserInput ui(save_thread, base_importance); + ui.start(); + + return 0; +}