Selaa lähdekoodia

Added Logger class

- added Logger class to provide logging functionality
- added LogLevel class to use different logging level
- added tests for Logger class
- added TODO for extending Logger class by adding
appending functionality
Patrick-Laptop 3 vuotta sitten
vanhempi
commit
849d1e79f6

+ 8 - 3
CMakeLists.txt

@@ -70,9 +70,13 @@ set(SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/IStorable.hpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/StorableFile.hpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/StorableFile.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/LogLevel.hpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/logging/LogLevel.hpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/FileOutputStream.hpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/FileOutputStream.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/FileOutputStream.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/logging/Logger.hpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/logging/Logger.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/logging/LogLevel.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/logging/LogLevelValue.hpp)
 
 set(TEST_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/boxing/IntegerTest.cpp
@@ -90,7 +94,8 @@ set(TEST_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/FileReaderTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/utils/RegexUtilsTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/StorableFileTest.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/FileOutputStreamTest.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/FileOutputStreamTest.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/logging/LoggerTest.cpp)
 
 ##########################################################
 # Build

+ 51 - 0
source/io/logging/LogLevel.cpp

@@ -0,0 +1,51 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-08-20
+ * Changed:         2020-08-20
+ *
+ * */
+
+#include "LogLevel.hpp"
+
+ls_std::LogLevel::LogLevel(const ls_std::LogLevelValue& _value) : Class("LogLevel"),
+value(_value)
+{
+  this->_init();
+}
+
+ls_std::LogLevel::LogLevel() : Class("LogLevel")
+{}
+
+ls_std::LogLevel::operator unsigned char() const
+{
+  return this->value;
+}
+
+ls_std::LogLevel & ls_std::LogLevel::operator=(const ls_std::LogLevelValue &_value)
+{
+  this->value = _value;
+  return *this;
+}
+
+bool ls_std::LogLevel::operator<=(const ls_std::LogLevelValue &_value)
+{
+  return this->value <= _value;
+}
+
+std::string ls_std::LogLevel::toString() const
+{
+  return this->level.at(this->value);
+}
+
+void ls_std::LogLevel::_init()
+{
+  this->level.insert({ls_std::LogLevelValue::FATAL, "FATAL"});
+  this->level.insert({ls_std::LogLevelValue::ERR, "ERROR"});
+  this->level.insert({ls_std::LogLevelValue::WARN, "WARN"});
+  this->level.insert({ls_std::LogLevelValue::INFO, "INFO"});
+  this->level.insert({ls_std::LogLevelValue::DEBUG, "DEBUG"});
+  this->level.insert({ls_std::LogLevelValue::TRACE, "TRACE"});
+  this->level.insert({ls_std::LogLevelValue::OFF, "OFF"});
+}

+ 40 - 0
source/io/logging/LogLevel.hpp

@@ -0,0 +1,40 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-08-20
+ * Changed:         2020-08-20
+ *
+ * */
+
+#ifndef LOG_LEVEL_HPP
+#define LOG_LEVEL_HPP
+
+#include <unordered_map>
+#include "../../base/Class.hpp"
+#include "LogLevelValue.hpp"
+
+namespace ls_std {
+  class LogLevel : public Class {
+    public:
+
+      explicit LogLevel(const ls_std::LogLevelValue& _value);
+      LogLevel();
+      ~LogLevel() = default;
+
+      operator unsigned char() const;
+      LogLevel& operator=(const ls_std::LogLevelValue& _value);
+      bool operator<=(const ls_std::LogLevelValue& _value);
+
+      std::string toString() const;
+
+    private:
+
+      std::unordered_map<ls_std::LogLevelValue, std::string> level {};
+      ls_std::LogLevelValue value {};
+
+      void _init();
+  };
+}
+
+#endif

+ 4 - 4
source/io/LogLevel.hpp → source/io/logging/LogLevelValue.hpp

@@ -7,13 +7,13 @@
  *
  * */
 
-#ifndef LOG_LEVEL_HPP
-#define LOG_LEVEL_HPP
+#ifndef LOG_LEVEL_VALUE_HPP
+#define LOG_LEVEL_VALUE_HPP
 
 namespace ls_std {
-  enum LogLevel {
+  enum LogLevelValue {
     FATAL = 0,
-    ERROR,
+    ERR,
     WARN,
     INFO,
     DEBUG,

+ 118 - 0
source/io/logging/Logger.cpp

@@ -0,0 +1,118 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-08-20
+ * Changed:         2020-08-20
+ *
+ * */
+
+#include "Logger.hpp"
+#include "../../time/Date.hpp"
+#include "../NewLine.hpp"
+
+ls_std::Logger::Logger(const std::string &_path) : Class("Logger"),
+file(ls_std::File{_path}),
+logLevel(ls_std::LogLevelValue::INFO)
+{
+  this->_init();
+}
+
+void ls_std::Logger::close()
+{
+  this->outputStream->close();
+}
+
+void ls_std::Logger::debug(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::DEBUG;
+
+  if(this->logLevel >= ls_std::LogLevelValue::DEBUG) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::DEBUG));
+  }
+}
+
+void ls_std::Logger::error(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::ERR;
+
+  if(this->logLevel >= ls_std::LogLevelValue::ERR) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::ERR));
+  }
+}
+
+void ls_std::Logger::fatal(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::FATAL;
+
+  if(this->logLevel >= ls_std::LogLevelValue::FATAL) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::FATAL));
+  }
+}
+
+ls_std::LogLevel ls_std::Logger::getLogLevel()
+{
+  return this->logLevel;
+}
+
+void ls_std::Logger::info(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::INFO;
+
+  if(this->logLevel >= ls_std::LogLevelValue::INFO) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::INFO));
+  }
+}
+
+void ls_std::Logger::setLogLevel(const ls_std::LogLevelValue &_logLevelValue)
+{
+  this->logLevel = _logLevelValue;
+}
+
+void ls_std::Logger::trace(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::TRACE;
+
+  if(this->logLevel >= ls_std::LogLevelValue::TRACE) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::TRACE));
+  }
+}
+
+void ls_std::Logger::warn(const ls_std::byte *_data)
+{
+  uint8_t thisLogLevel = this->logLevel;
+  uint8_t constant = ls_std::LogLevelValue::WARN;
+
+  if(this->logLevel >= ls_std::LogLevelValue::WARN) {
+    this->_log(_data, ls_std::LogLevel(ls_std::LogLevelValue::WARN));
+  }
+}
+
+void ls_std::Logger::_init()
+{
+  if(!this->file.exists()) {
+    this->file.createNewFile();
+  }
+}
+
+void ls_std::Logger::_log(const ls_std::byte *_data, const ls_std::LogLevel& _logLevel)
+{
+  if(this->outputStream == nullptr) {
+    this->outputStream = std::make_shared<ls_std::FileOutputStream>(this->file);
+  }
+
+  ls_std::Date date {};
+
+  std::string message = "[" +
+      date.toString() + "] " +
+      _logLevel.toString() + ":\t" +
+      std::string(_data) +
+      ls_std::NewLine::getUnixNewLine();
+
+  outputStream->write(message.c_str());
+}

+ 49 - 0
source/io/logging/Logger.hpp

@@ -0,0 +1,49 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-08-20
+ * Changed:         2020-08-20
+ *
+ * */
+
+#ifndef LOGGER_HPP
+#define LOGGER_HPP
+
+#include "../../base/Class.hpp"
+#include "LogLevel.hpp"
+#include "../IWriter.hpp"
+#include "../File.hpp"
+#include "../FileOutputStream.hpp"
+#include <string>
+
+// TODO: add append functionality
+namespace ls_std {
+  class Logger : public Class {
+    public:
+
+      explicit Logger(const std::string& _path);
+      ~Logger() = default;
+
+      void close();
+      void debug(const ls_std::byte* _data);
+      void error(const ls_std::byte* _data);
+      void fatal(const ls_std::byte* _data);
+      ls_std::LogLevel getLogLevel();
+      void info(const ls_std::byte* _data);
+      void setLogLevel(const ls_std::LogLevelValue& _logLevelValue);
+      void trace(const ls_std::byte* _data);
+      void warn(const ls_std::byte* _data);
+
+    private:
+
+      ls_std::File file;
+      ls_std::LogLevel logLevel {};
+      std::shared_ptr<ls_std::FileOutputStream> outputStream {};
+
+      void _init();
+      void _log(const ls_std::byte* _data, const ls_std::LogLevel& _logLevel);
+  };
+}
+
+#endif

+ 214 - 0
test/cases/io/logging/LoggerTest.cpp

@@ -0,0 +1,214 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-08-20
+ * Changed:         2020-08-20
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include "../../../../source/io/logging/Logger.hpp"
+#include "../../../TestHelper.hpp"
+#include "../../../../source/io/FileReader.hpp"
+#include "../../../../source/boxing/String.hpp"
+
+namespace {
+  class LoggerTest : public ::testing::Test {
+    protected:
+
+      LoggerTest() = default;
+      ~LoggerTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(LoggerTest, debug)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_debug.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::DEBUG);
+    logger.debug("1. line!");
+    logger.info("2. line!");
+    logger.error("3. line!");
+    logger.fatal("4. line!");
+    logger.warn("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+
+    ls_std::File file {path};
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_TRUE(content.contains("1. line!"));
+    ASSERT_TRUE(content.contains("2. line!"));
+    ASSERT_TRUE(content.contains("3. line!"));
+    ASSERT_TRUE(content.contains("4. line!"));
+    ASSERT_TRUE(content.contains("5. line!"));
+    ASSERT_FALSE(content.contains("6. line!"));
+
+    file.remove();
+  }
+
+  TEST_F(LoggerTest, error)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_error.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::ERR);
+    logger.debug("1. line!");
+    logger.info("2. line!");
+    logger.error("3. line!");
+    logger.fatal("4. line!");
+    logger.warn("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+
+    ls_std::File file {path};
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_FALSE(content.contains("1. line!"));
+    ASSERT_FALSE(content.contains("2. line!"));
+    ASSERT_TRUE(content.contains("3. line!"));
+    ASSERT_TRUE(content.contains("4. line!"));
+    ASSERT_FALSE(content.contains("5. line!"));
+    ASSERT_FALSE(content.contains("6. line!"));
+
+    file.remove();
+  }
+
+  TEST_F(LoggerTest, fatal)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_fatal.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::FATAL);
+    logger.debug("1. line!");
+    logger.info("2. line!");
+    logger.error("3. line!");
+    logger.fatal("4. line!");
+    logger.warn("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+    ls_std::File file {path};
+
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_FALSE(content.contains("1. line!"));
+    ASSERT_FALSE(content.contains("2. line!"));
+    ASSERT_FALSE(content.contains("3. line!"));
+    ASSERT_TRUE(content.contains("4. line!"));
+    ASSERT_FALSE(content.contains("5. line!"));
+    ASSERT_FALSE(content.contains("6. line!"));
+
+    file.remove();
+  }
+
+  TEST_F(LoggerTest, getLogLevel)
+  {
+    ls_std::Logger logger {TestHelper::getResourcesFolderLocation() + "output.log"};
+    ASSERT_EQ(ls_std::LogLevelValue::INFO, logger.getLogLevel());
+  }
+
+  TEST_F(LoggerTest, info)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_info.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::INFO);
+    logger.fatal("1. line!");
+    logger.error("2. line!");
+    logger.warn("3. line!");
+    logger.info("4. line!");
+    logger.debug("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+    ls_std::File file {path};
+
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_TRUE(content.contains("1. line!"));
+    ASSERT_TRUE(content.contains("2. line!"));
+    ASSERT_TRUE(content.contains("3. line!"));
+    ASSERT_TRUE(content.contains("4. line!"));
+    ASSERT_FALSE(content.contains("5. line!"));
+    ASSERT_FALSE(content.contains("6. line!"));
+
+    file.remove();
+  }
+
+  TEST_F(LoggerTest, setLogLevel)
+  {
+    ls_std::Logger logger {TestHelper::getResourcesFolderLocation() + "output.log"};
+    logger.setLogLevel(ls_std::LogLevelValue::ERR);
+
+    ASSERT_EQ(ls_std::LogLevelValue::ERR, logger.getLogLevel());
+  }
+
+  TEST_F(LoggerTest, trace)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_trace.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::TRACE);
+    logger.fatal("1. line!");
+    logger.error("2. line!");
+    logger.warn("3. line!");
+    logger.info("4. line!");
+    logger.debug("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+    ls_std::File file {path};
+
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_TRUE(content.contains("1. line!"));
+    ASSERT_TRUE(content.contains("2. line!"));
+    ASSERT_TRUE(content.contains("3. line!"));
+    ASSERT_TRUE(content.contains("4. line!"));
+    ASSERT_TRUE(content.contains("5. line!"));
+    ASSERT_TRUE(content.contains("6. line!"));
+
+    file.remove();
+  }
+
+  TEST_F(LoggerTest, warn)
+  {
+    std::string path = TestHelper::getResourcesFolderLocation() + "output_trace.log";
+
+    ls_std::Logger logger {path};
+    logger.setLogLevel(ls_std::LogLevelValue::WARN);
+    logger.fatal("1. line!");
+    logger.error("2. line!");
+    logger.warn("3. line!");
+    logger.info("4. line!");
+    logger.debug("5. line!");
+    logger.trace("6. line!");
+
+    logger.close();
+    ls_std::File file {path};
+
+    ls_std::FileReader reader {file};
+    ls_std::String content {reader.read()};
+
+    ASSERT_TRUE(content.contains("1. line!"));
+    ASSERT_TRUE(content.contains("2. line!"));
+    ASSERT_TRUE(content.contains("3. line!"));
+    ASSERT_FALSE(content.contains("4. line!"));
+    ASSERT_FALSE(content.contains("5. line!"));
+    ASSERT_FALSE(content.contains("6. line!"));
+
+    file.remove();
+  }
+}

+ 0 - 0
test/resources/output.log