Add LogCore class for file-based logging 60/202260/7
authorSangyoon Jang <jeremy.jang@samsung.com>
Tue, 26 Mar 2019 10:35:51 +0000 (19:35 +0900)
committerSangyoon Jang <jeremy.jang@samsung.com>
Wed, 27 Mar 2019 09:11:11 +0000 (18:11 +0900)
To writing log into a file:

  // set file name
  ::utils::LogCore::GetCore().SetFileName("log");
  // set log file size, 2MB
  ::utils::LogCore::GetCore().SetRotationSize(1024 * 1024 * 2);
  // set maximum rotation size (log, log.1, log.2, log.3 ...)
  ::utils::LogCore::GetCore().SetMaximumRotation(5);

  // put some logs...
  LOG(INFO) << "foo";
  LOG(ERROR) << "bar";

  // write log into a file
  ::utils::LogCore::GetCore().WriteLog();

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

index fe4cce3576bf2c8e3ad796133eaf86624e3af4af..141ac71d0122f30710741ccee0ebc3b980d02926 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>
 
@@ -64,6 +72,101 @@ class StringStream : private std::basic_ostringstream<charT, traits> {
   }
 };
 
+class LogCore {
+ public:
+  // Do not call this function at destructor of global object
+  static LogCore& GetCore() {
+    static LogCore core;
+    return core;
+  }
+
+  void SetFileName(const std::string& name) {
+    file_name_ = name;
+  }
+
+  void SetRotationSize(int size) {
+    rotation_size_ = size;
+  }
+
+  void SetMaximumRotation(int n) {
+    max_rotation_ = n;
+  }
+
+  void StreamLog(const std::string& log) {
+    *log_stream_ << GetTimeStamp() << GetPid() << log << std::endl;
+  }
+
+  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();
+  }
+
+ private:
+  LogCore() : file_name_(""), rotation_size_(LONG_MAX), max_rotation_(1) {
+    log_stream_.reset(new std::ostringstream());
+  }
+  ~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_;
+};
+
 class LogCatcher {
  public:
   LogCatcher(LogLevel level, const char* tag)
@@ -75,6 +178,9 @@ class LogCatcher {
 
     if (level_ == LogLevel::LOG_ERROR)
       std::cerr << str.str() << std::endl;
+
+    if (level_ != LogLevel::LOG_DEBUG)
+      LogCore::GetCore().StreamLog(str.str());
   }
  private:
   // Since LogCatcher passes input to dlog_print(), the input which contains