#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__ \
}
};
-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 {
: 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_;
};