// Copyright 2019 Roman Perepelitsa. // // This file is part of GitStatus. // // GitStatus is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // GitStatus is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with GitStatus. If not, see . #include "logging.h" #include #include #include #include #include #include #include #include namespace gitstatus { namespace internal_logging { namespace { std::mutex g_log_mutex; constexpr char kHexLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; void FormatThreadId(char (&out)[2 * sizeof(std::uintptr_t) + 1]) { std::uintptr_t tid = (std::uintptr_t)pthread_self(); char* p = out + sizeof(out) - 1; *p = 0; do { --p; *p = kHexLower[tid & 0xF]; tid >>= 4; } while (p != out); } void FormatCurrentTime(char (&out)[64]) { std::time_t time = std::time(nullptr); struct tm tm; if (localtime_r(&time, &tm) != &tm || std::strftime(out, sizeof(out), "%F %T", &tm) == 0) { std::strcpy(out, "undef"); } } } // namespace LogStreamBase::LogStreamBase(const char* file, int line, LogLevel lvl) : errno_(errno), file_(file), line_(line), lvl_(LogLevelStr(lvl)) { strm_ = std::make_unique(); } void LogStreamBase::Flush() { { std::string msg = strm_->str(); char tid[2 * sizeof(std::uintptr_t) + 1]; FormatThreadId(tid); char time[64]; FormatCurrentTime(time); std::unique_lock lock(g_log_mutex); std::fprintf(stderr, "[%s %s %s %s:%d] %s\n", time, tid, lvl_, file_, line_, msg.c_str()); } strm_.reset(); errno = errno_; } std::ostream& operator<<(std::ostream& strm, Errno e) { // GNU C Library uses a buffer of 1024 characters for strerror(). Mimic to avoid truncations. char buf[1024]; auto x = strerror_r(e.err, buf, sizeof(buf)); // There are two versions of strerror_r with different semantics. We can figure out which // one we've got by looking at the result type. if (std::is_same::value) { // XSI-compliant version. strm << (x ? "unknown error" : buf); } else if (std::is_same::value) { // GNU-specific version. strm << x; } else { // Something else entirely. strm << "unknown error"; } return strm; } } // namespace internal_logging LogLevel g_min_log_level = INFO; const char* LogLevelStr(LogLevel lvl) { switch (lvl) { case DEBUG: return "DEBUG"; case INFO: return "INFO"; case WARN: return "WARN"; case ERROR: return "ERROR"; case FATAL: return "FATAL"; } return "UNKNOWN"; } bool ParseLogLevel(const char* s, LogLevel& lvl) { if (!s) return false; else if (!std::strcmp(s, "DEBUG")) lvl = DEBUG; else if (!std::strcmp(s, "INFO")) lvl = INFO; else if (!std::strcmp(s, "WARN")) lvl = WARN; else if (!std::strcmp(s, "ERROR")) lvl = ERROR; else if (!std::strcmp(s, "FATAL")) lvl = FATAL; else return false; return true; } } // namespace gitstatus