remote/log/Log.cpp (187 lines of code) (raw):

#include "Log.h" #include "../Utils.h" #include <stdio.h> #include <cstdarg> #include <stdexcept> #include <vector> #include <thread> #include <boost/date_time/posix_time/posix_time.hpp> namespace { thread_local std::vector<std::string> ourNDC; thread_local std::string ourThreadName; const std::string ourNdcSeparator = " | "; int ourLogLevel = Log::LEVEL_INFO; std::string ourLogFilePath; FILE * ourLogFile = nullptr; bool ourDoFlush = false; bool ourAddNewLine = true; bool ourPureMsg = false; const bool doPrintLogLevel = getBoolEnv("CEF_SERVER_LOG_PrintLogLevel", true); } std::string Log::level2str(int serverLogLevel) { switch (serverLogLevel) { case LEVEL_TRACE: return "trace"; case LEVEL_DEBUG: return "debug"; case LEVEL_INFO: return "info"; case LEVEL_WARN: return "warn"; case LEVEL_ERROR: return "error"; case LEVEL_FATAL: return "fatal"; case LEVEL_DISABLED: return "disabled"; default: return string_format("unknown_log_level_%d", serverLogLevel); } } int Log::str2level(std::string serverLogLevel) { std::string valLowerCase = serverLogLevel; std::transform(valLowerCase.begin(), valLowerCase.end(), valLowerCase.begin(), [](unsigned char in){ if (in <= 'Z' && in >= 'A') return in - ('Z' - 'z'); return (int)in; }); if (valLowerCase.find("verb") != valLowerCase.npos || valLowerCase.find("trace") != valLowerCase.npos) return LEVEL_TRACE; if (valLowerCase.find("debug") != valLowerCase.npos) return LEVEL_DEBUG; if (valLowerCase.find("info") != valLowerCase.npos) return LEVEL_INFO; if (valLowerCase.find("warn") != valLowerCase.npos) return LEVEL_WARN; if (valLowerCase.find("err") != valLowerCase.npos) return LEVEL_ERROR; if (valLowerCase.find("fatal") != valLowerCase.npos) return LEVEL_FATAL; if (valLowerCase.find("disable") != valLowerCase.npos) return LEVEL_DISABLED; // Default val is disabled. return LEVEL_DISABLED; } std::string Log::cefLogLevel2str(int serverLogLevel) { switch (serverLogLevel) { case LOGSEVERITY_VERBOSE: return "trace"; case LOGSEVERITY_INFO: return "info"; case LOGSEVERITY_WARNING: return "warn"; case LOGSEVERITY_ERROR: return "error"; case LOGSEVERITY_FATAL: return "fatal"; case LOGSEVERITY_DISABLE: return "disabled"; default: return string_format("unknown_log_level_%d", serverLogLevel); } } void Log::setThreadName(std::string name) { ourThreadName.assign(name); } void Log::init(int level, std::string logfile) { if (level < 0) level = 0; // max verbose if (level >= LEVEL_DISABLED) { ourLogLevel = LEVEL_DISABLED; return; } ourLogFilePath = logfile; fprintf(stderr, "Initialize cef_server logger: level=%s file='%s'\n", level2str(level).c_str(), logfile.c_str()); if (!logfile.empty() && logfile.compare("stderr") != 0) { FILE* flog = fopen(logfile.c_str(), "a"); if (flog != nullptr) { initImpl(level, flog); } else { fprintf(stderr, "Can't open log file '%s', will be used default (stderr)\n", logfile.c_str()); initImpl(level); } } else initImpl(level); } void Log::initImpl(int level, FILE* logFile) { ourLogLevel = level; if (logFile != nullptr) { ourDoFlush = true; ourLogFile = logFile; } else ourLogFile = stderr; } bool Log::isDebugEnabled() { return ourLogLevel <= LEVEL_DEBUG; } bool Log::isTraceEnabled() { return ourLogLevel <= LEVEL_TRACE; } bool Log::isStdStreamLogger() { return ourLogFile == stderr; } std::string Log::printLevelAndPath() { return string_format("level=%d,file=%s", ourLogLevel, ourLogFilePath.c_str()); } void Log::log(int level, const char *const format, ...) { if (level < ourLogLevel) return; auto temp = std::vector<char>{}; auto length = std::size_t {63}; std::va_list args; while (temp.size() <= length) { temp.resize(length + 1); va_start(args, format); const auto status = std::vsnprintf(temp.data(), temp.size(), format, args); va_end(args); if (status < 0) throw std::runtime_error {"string formatting error"}; length = static_cast<std::size_t>(status); } std::string msg(temp.data(), length); std::string ndc; if (!ourNDC.empty()) { for (auto s: ourNDC) { if (!ndc.empty()) ndc.append(ourNdcSeparator); ndc.append(s); } } if (ourThreadName.empty()) { // TODO: pass thread name // size_t tidHash = std::hash<std::thread::id>()(std::this_thread::get_id()); static int tidLocal = 0; ourThreadName.assign(string_format("th%d", tidLocal++)); } const boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); const boost::posix_time::time_duration td = now.time_of_day(); const long hours = td.hours(); const long minutes = td.minutes(); const long seconds = td.seconds(); const long milliseconds = td.total_milliseconds() - ((hours * 3600 + minutes * 60 + seconds) * 1000); char timeBuf[64]; sprintf(timeBuf, "%02ld:%02ld:%02ld.%03ld", hours, minutes, seconds, milliseconds); const char * logLevel = ""; if (doPrintLogLevel) { if (level == LEVEL_FATAL) logLevel = "[FATAL]"; else if (level == LEVEL_ERROR) logLevel = "[ERROR]"; else if (level == LEVEL_WARN) logLevel = "[WARN]"; else if (level == LEVEL_INFO) logLevel = "[I]"; else if (level == LEVEL_DEBUG) logLevel = "[D]"; else if (level == LEVEL_TRACE) logLevel = "[T]"; } const char * end = ourAddNewLine ? "\n" : ""; if (ourPureMsg) fprintf(ourLogFile, "%s%s%s", logLevel, msg.c_str(), end); else if (ndc.empty()) fprintf(ourLogFile, "%s %s[%s] %s%s", timeBuf, logLevel, ourThreadName.c_str(), msg.c_str(), end); else fprintf(ourLogFile, "%s %s[%s %s] %s%s", timeBuf, logLevel, ourThreadName.c_str(), ndc.c_str(), msg.c_str(), end); if (ourDoFlush) fflush(ourLogFile); } LogNdc::LogNdc(std::string file, std::string func, std::string threadName) { std::string msg; if (func.empty()) { msg.assign(file); } else { // Make short file name for (auto ch: file) if (std::isupper(ch)) msg += ch; if (msg.empty()) msg.assign(file); msg.append(":"); msg.append(func); } ourNDC.push_back(msg); if (!threadName.empty()) ourThreadName.assign(threadName); } LogNdc::~LogNdc() { ourNDC.pop_back(); }