Refactor logger class 88/213188/1
authorSangyoon Jang <jeremy.jang@samsung.com>
Mon, 2 Sep 2019 10:35:50 +0000 (19:35 +0900)
committerSangyoon Jang <jeremy.jang@samsung.com>
Mon, 2 Sep 2019 10:35:50 +0000 (19:35 +0900)
Add ILogBackend interface for custom logging backends.
Now LogCore writes the log using multiple custom logging backends.

using macro LOG() -> LogCatcher -> LogCore -> CustomLogBackends

Change-Id: Ie406031c11934a4444a312a6456fd08fc6283ac2
Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
src/manifest_parser/utils/logging.h

index b05c04f178f5bb84065d832090fa851c642e532b..1ceb784948cbfc2dc0b3879435caa284c000fa6d 100644 (file)
 #undef LOG
 #endif
 
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
 #include <cassert>
 #include <climits>
 #include <cstdio>
 #include <cstring>
-#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <memory>
 #include <sstream>
 #include <string>
+#include <vector>
 
 #ifndef __FILENAME__
 #define __FILENAME__                                                           \
@@ -72,99 +68,70 @@ class StringStream : private std::basic_ostringstream<charT, traits> {
   }
 };
 
-class LogCore {
+// Interface class for logging backends. The custom LogBackend which wants
+// log using LOG() macro should be implement following interface.
+class ILogBackend {
  public:
-  // Do not call this function at destructor of global object
-  static LogCore& GetCore() {
-    static LogCore core;
-    return core;
-  }
+  virtual void WriteLog(LogLevel level, const std::string& tag,
+      const std::string& logstr) = 0;
+};
 
-  void SetFileName(const std::string& name) {
-    file_name_ = name;
+class DLogBackend : public ILogBackend {
+ public:
+  void WriteLog(LogLevel level, const std::string& tag,
+      const std::string& logstr) override {
+    dlog_print(LogLevelToPriority(level), tag.c_str(), "%s",
+        Escape(logstr).c_str());
+
+    if (level == LogLevel::LOG_ERROR)
+      std::cerr << logstr << std::endl;
   }
 
-  void SetRotationSize(int size) {
-    rotation_size_ = size;
+ private:
+  // Since LogCatcher passes input to dlog_print(), the input which contains
+  // format string(such as %d, %n) can cause unexpected result.
+  // This is simple function to escape '%'.
+  // NOTE: Is there any gorgeous way instead of this?
+  std::string Escape(const std::string& str) const {
+    std::string escaped = std::string(str);
+    size_t start_pos = 0;
+    std::string from = "%";
+    std::string to = "%%";
+    while ((start_pos = escaped.find(from, start_pos)) != std::string::npos) {
+      escaped.replace(start_pos, from.length(), to);
+      start_pos += to.length();
+    }
+    return escaped;
   }
+};
 
-  void SetMaximumRotation(int n) {
-    max_rotation_ = n;
+class LogCore {
+ public:
+  // Do not call this function at destructor of global object
+  static LogCore& GetCore() {
+    static LogCore core;
+    return core;
   }
 
-  void StreamLog(const std::string& log) {
-    *log_stream_ << GetTimeStamp() << GetPid() << log << std::endl;
+  void AddLogBackend(std::shared_ptr<ILogBackend> backend) {
+    backend_list_.emplace_back(backend);
   }
 
-  void WriteLog() {
-    if (file_name_.empty())
-      return;
-    int size = GetFileSize(file_name_);
-    if (size > rotation_size_)
-      Rotate();
-    std::ofstream ofs(file_name_.c_str(), std::ios::app);
-    ofs << log_stream_->str();
-    ofs.close();
-    // clean the log stream
-    log_stream_->str("");
-    log_stream_->clear();
+  void Log(LogLevel level, const std::string& tag, const std::string& log) {
+    for (auto backend : backend_list_)
+      backend->WriteLog(level, tag, log);
   }
 
  private:
-  LogCore() : file_name_(""), rotation_size_(LONG_MAX), max_rotation_(1) {
-    log_stream_.reset(new std::ostringstream());
+  LogCore() {
+    // add default dlog backend
+    AddLogBackend(std::shared_ptr<ILogBackend>(new DLogBackend()));
   }
   ~LogCore() = default;
   LogCore(const LogCore&) = delete;
   LogCore& operator=(const LogCore&) = delete;
 
-  void Rotate() {
-    for (int i = max_rotation_; i > 0; i--) {
-      std::string old_log = file_name_ + "." + std::to_string(i);
-      // the oldest log will be removed
-      if (i == max_rotation_) {
-        std::remove(old_log.c_str());
-      } else {
-        std::string new_log = file_name_ + "." + std::to_string(i + 1);
-        std::rename(old_log.c_str(), new_log.c_str());
-      }
-    }
-    std::string new_log = file_name_ + ".1";
-    std::rename(file_name_.c_str(), new_log.c_str());
-  }
-
-  int GetFileSize(const std::string& file_name) {
-    struct stat sb;
-    int ret = stat(file_name.c_str(), &sb);
-    return ret == 0 ? sb.st_size : -1;
-  }
-
-  std::string GetTimeStamp() {
-    struct timespec ts;
-    clock_gettime(CLOCK_REALTIME, &ts);
-
-    time_t seconds = ts.tv_sec;
-    struct tm gmt;
-    if (!gmtime_r(&seconds, &gmt))
-      return "[]";
-    int32_t miliseconds = ts.tv_nsec / 1000000;
-
-    char buf[32];
-    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmt);
-    char timestamp[32];
-    snprintf(timestamp, sizeof(timestamp), "[%s.%03d UTC]", buf, miliseconds);
-
-    return timestamp;
-  }
-
-  std::string GetPid() {
-    return "[" + std::to_string(getpid()) + "]";
-  }
-
-  std::string file_name_;
-  int rotation_size_;
-  int max_rotation_;
-  std::unique_ptr<std::ostringstream> log_stream_;
+  std::vector<std::shared_ptr<ILogBackend>> backend_list_;
 };
 
 class LogCatcher {
@@ -173,32 +140,10 @@ class LogCatcher {
     : level_(level), tag_(tag) { }
 
   void operator&(const StringStream<char>& str) const {
-    dlog_print(LogLevelToPriority(level_), tag_.c_str(), "%s",
-        Escape(str.str()).c_str());
-
-    if (level_ == LogLevel::LOG_ERROR)
-      std::cerr << str.str() << std::endl;
-
-    if (level_ != LogLevel::LOG_DEBUG)
-      LogCore::GetCore().StreamLog(str.str());
+    LogCore::GetCore().Log(level_, tag_, str.str());
   }
 
  private:
-  // Since LogCatcher passes input to dlog_print(), the input which contains
-  // format string(such as %d, %n) can cause unexpected result.
-  // This is simple function to escape '%'.
-  // NOTE: Is there any gorgeous way instead of this?
-  std::string Escape(const std::string& str) const {
-    std::string escaped = std::string(str);
-    size_t start_pos = 0;
-    std::string from = "%";
-    std::string to = "%%";
-    while ((start_pos = escaped.find(from, start_pos)) != std::string::npos) {
-      escaped.replace(start_pos, from.length(), to);
-      start_pos += to.length();
-    }
-    return escaped;
-  }
   LogLevel level_;
   std::string tag_;
 };