#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>
}
};
+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)
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