diff --git a/logger/include/log_level.hpp b/logger/include/log_level.hpp index cbcfea3..29b4522 100644 --- a/logger/include/log_level.hpp +++ b/logger/include/log_level.hpp @@ -1,7 +1,75 @@ #pragma once +#include + namespace logger { -enum class LogLevel { Debug = 0, Info = 1, Error = 2 }; +/** + * @brief Представляет уровень логирования. + * + * Экземпляры класса создаются только через статические фабричные методы. + */ +class LogLevel { +public: + /** + * @brief Создает уровень Debug. + */ + static LogLevel debug() { return LogLevel(Type::Debug); } + + /** + * @brief Создает уровень Info. + */ + static LogLevel info() { return LogLevel(Type::Info); } + + /** + * @brief Создает уровень Error. + */ + static LogLevel error() { return LogLevel(Type::Error); } + + /** + * @brief Возвращает строковое представление уровня. + * + * @return "DEBUG", "INFO" или "ERROR". + */ + [[nodiscard]] + std::string_view toString() const { + switch (type_) { + case Type::Debug: + return "DEBUG"; + + case Type::Info: + return "INFO"; + + case Type::Error: + return "ERROR"; + } + + return "UNKNOWN"; + } + + /** + * @brief Сравнивает два уровня логирования. + */ + [[nodiscard]] + bool operator==(const LogLevel &other) const { + return type_ == other.type_; + } + + /** + * @brief Сравнивает два уровня логирования. + */ + [[nodiscard]] + bool operator!=(const LogLevel &other) const { + return !(*this == other); + } + +private: + enum class Type { Debug, Info, Error }; + + explicit LogLevel(Type type) : type_(type) {} + +private: + Type type_; +}; } // namespace logger diff --git a/logger/include/logger.hpp b/logger/include/logger.hpp index 949cf61..2864a9a 100644 --- a/logger/include/logger.hpp +++ b/logger/include/logger.hpp @@ -58,18 +58,33 @@ public: Logger &operator=(Logger &&) = delete; /** - * @brief Записывает сообщение в журнал. + * @brief Записывает сообщение в журнал с указанным уровнем логирования. * * В случае успешной записи возвращается успешный Status. * При ошибке открытия файла или записи возвращается Status * с описанием ошибки. * - * @param level Уровень важности сообщения. + * @param level Уровень логирования сообщения. * @param message Текст сообщения. * * @return Статус выполнения операции. */ - core::Status writeLog(LogLevel level, const std::string &message); + core::Status writeMessage(LogLevel level, const std::string &message); + + /** + * @brief Записывает сообщение в журнал с уровнем логирования по умолчанию. + * + * Метод является удобной перегрузкой и эквивалентен вызову: + * + * @code + * writeMessage(getLogLevel(), message); + * @endcode + * + * @param message Текст сообщения. + * + * @return Статус выполнения операции. + */ + core::Status writeMessage(const std::string &message); /** * @brief Создаёт новый Logger с изменённым уровнем логирования. @@ -96,14 +111,14 @@ public: /** * @brief Возвращает текущий уровень логирования. * - * @return Уровень логирования. + * @return Уровень логирования по умолчанию. */ LogLevel getLogLevel() const; /** * @brief Возвращает имя файла журнала. * - * @return Имя файла. + * @return Имя файла журнала. */ const std::string &getFileName() const; diff --git a/logger/src/logger.cpp b/logger/src/logger.cpp index 78f7da0..f0fe1c0 100644 --- a/logger/src/logger.cpp +++ b/logger/src/logger.cpp @@ -1,7 +1,7 @@ -// logger.cpp - #include "logger.hpp" +#include + namespace logger { Logger::Logger(const std::string &fileName, LogLevel defaultLevel) @@ -9,11 +9,26 @@ Logger::Logger(const std::string &fileName, LogLevel defaultLevel) Logger::~Logger() = default; -core::Status Logger::writeLog(LogLevel /*level*/, - const std::string & /*message*/) { +core::Status Logger::writeMessage(LogLevel level, const std::string &message) { + std::ofstream stream(fileName_, std::ios::app); + + if (!stream.is_open()) { + return core::Status::Failure("Unable to open log file: " + fileName_); + } + + stream << "[" << level.toString() << "] " << message << '\n'; + + if (!stream.good()) { + return core::Status::Failure("Failed to write to log file: " + fileName_); + } + return core::Status::Success(true); } +core::Status Logger::writeMessage(const std::string &message) { + return writeMessage(defaultLevel_, message); +} + Logger Logger::withLogLevel(LogLevel level) const { return Logger(fileName_, level); } diff --git a/tests/app/core/logger_test.cpp b/tests/core/logger_test.cpp similarity index 65% rename from tests/app/core/logger_test.cpp rename to tests/core/logger_test.cpp index cc5d8ee..da86ad5 100644 --- a/tests/app/core/logger_test.cpp +++ b/tests/core/logger_test.cpp @@ -6,30 +6,32 @@ namespace { +using namespace logger; + void testLoggerCreation() { - logger::Logger logger("test.log", logger::LogLevel::Info); + Logger logger("test.log", LogLevel::info()); assert(logger.getFileName() == "test.log"); - assert(logger.getLogLevel() == logger::LogLevel::Info); + assert(logger.getLogLevel() == LogLevel::info()); } void testWithLogLevel() { - logger::Logger logger("test.log", logger::LogLevel::Info); + Logger logger("test.log", LogLevel::info()); - auto debugLogger = logger.withLogLevel(logger::LogLevel::Debug); + auto debugLogger = logger.withLogLevel(LogLevel::debug()); // Исходный объект не изменился - assert(logger.getLogLevel() == logger::LogLevel::Info); + assert(logger.getLogLevel() == LogLevel::info()); // Новый объект получил новый уровень - assert(debugLogger.getLogLevel() == logger::LogLevel::Debug); + assert(debugLogger.getLogLevel() == LogLevel::debug()); // Имя файла сохранилось assert(debugLogger.getFileName() == "test.log"); } void testWithFileName() { - logger::Logger logger("old.log", logger::LogLevel::Info); + Logger logger("old.log", LogLevel::info()); auto newLogger = logger.withFileName("new.log"); @@ -40,15 +42,15 @@ void testWithFileName() { assert(newLogger.getFileName() == "new.log"); // Уровень сохранился - assert(newLogger.getLogLevel() == logger::LogLevel::Info); + assert(newLogger.getLogLevel() == LogLevel::info()); } void testWriteLogSuccess() { const std::string fileName = "logger_test.log"; - logger::Logger logger(fileName, logger::LogLevel::Info); + Logger logger(fileName, LogLevel::info()); - auto result = logger.writeLog(logger::LogLevel::Info, "Application started"); + auto result = logger.writeMessage(LogLevel::info(), "Application started"); assert(result.isSuccess()); assert(result.getValue()); @@ -68,9 +70,9 @@ void testWriteLogSuccess() { } void testWriteLogFailure() { - logger::Logger logger("/invalid/path/logger.log", logger::LogLevel::Info); + Logger logger("/invalid/path/logger.log", LogLevel::info()); - auto result = logger.writeLog(logger::LogLevel::Info, "message"); + auto result = logger.writeMessage(LogLevel::info(), "message"); assert(result.isFailure()); assert(!result.getError().empty()); @@ -79,11 +81,11 @@ void testWriteLogFailure() { void testMultipleWrites() { const std::string fileName = "multiple.log"; - logger::Logger logger(fileName, logger::LogLevel::Info); + Logger logger(fileName, LogLevel::info()); - assert(logger.writeLog(logger::LogLevel::Info, "first").isSuccess()); + assert(logger.writeMessage(LogLevel::info(), "first").isSuccess()); - assert(logger.writeLog(logger::LogLevel::Error, "second").isSuccess()); + assert(logger.writeMessage(LogLevel::error(), "second").isSuccess()); std::ifstream file(fileName); diff --git a/tests/logger/logger_test.cpp b/tests/logger/logger_test.cpp index 04f5b39..1a66393 100644 --- a/tests/logger/logger_test.cpp +++ b/tests/logger/logger_test.cpp @@ -8,33 +8,35 @@ namespace { +using namespace logger; + void testLoggerCreation() { - logger::Logger logger("test.log", logger::LogLevel::Info); + Logger logger("test.log", LogLevel::info()); assert(logger.getFileName() == "test.log"); - assert(logger.getLogLevel() == logger::LogLevel::Info); + assert(logger.getLogLevel() == LogLevel::info()); } void testWithLogLevel() { - logger::Logger logger("test.log", logger::LogLevel::Info); + Logger logger("test.log", LogLevel::info()); - auto debugLogger = logger.withLogLevel(logger::LogLevel::Debug); + auto debugLogger = logger.withLogLevel(LogLevel::debug()); - assert(logger.getLogLevel() == logger::LogLevel::Info); - assert(debugLogger.getLogLevel() == logger::LogLevel::Debug); + assert(logger.getLogLevel() == LogLevel::info()); + assert(debugLogger.getLogLevel() == LogLevel::debug()); assert(debugLogger.getFileName() == "test.log"); } void testWithFileName() { - logger::Logger logger("old.log", logger::LogLevel::Info); + Logger logger("old.log", LogLevel::info()); auto newLogger = logger.withFileName("new.log"); assert(logger.getFileName() == "old.log"); assert(newLogger.getFileName() == "new.log"); - assert(newLogger.getLogLevel() == logger::LogLevel::Info); + assert(newLogger.getLogLevel() == LogLevel::info()); } void testWriteLogSuccess() { @@ -42,9 +44,9 @@ void testWriteLogSuccess() { file.throwIfExists(); - logger::Logger logger(file.getFileName(), logger::LogLevel::Info); + Logger logger(file.getFileName(), LogLevel::info()); - auto result = logger.writeLog(logger::LogLevel::Info, "Application started"); + auto result = logger.writeMessage(LogLevel::info(), "Application started"); assert(result.isSuccess()); assert(result.getValue()); @@ -66,9 +68,9 @@ void testWriteLogFailure() { std::filesystem::create_directory(file.getFileName()); - logger::Logger logger(file.getFileName(), logger::LogLevel::Info); + Logger logger(file.getFileName(), LogLevel::info()); - auto result = logger.writeLog(logger::LogLevel::Info, "message"); + auto result = logger.writeMessage(LogLevel::info(), "message"); assert(result.isFailure()); assert(!result.getError().empty()); @@ -81,11 +83,11 @@ void testMultipleWrites() { file.throwIfExists(); - logger::Logger logger(file.getFileName(), logger::LogLevel::Info); + Logger logger(file.getFileName(), LogLevel::info()); - auto first = logger.writeLog(logger::LogLevel::Info, "first message"); + auto first = logger.writeMessage(LogLevel::info(), "first message"); - auto second = logger.writeLog(logger::LogLevel::Error, "second message"); + auto second = logger.writeMessage(LogLevel::error(), "second message"); assert(first.isSuccess()); assert(second.isSuccess());