diff options
author | romkatv <roman.perepelitsa@gmail.com> | 2020-05-10 16:58:05 +0300 |
---|---|---|
committer | romkatv <roman.perepelitsa@gmail.com> | 2020-05-10 16:58:05 +0300 |
commit | 1531d6e5439daae01627b2645684876b75eaf5eb (patch) | |
tree | 05ed820f16dbb04be4b4b8ec3055f93431518773 /src/logging.cc |
Squashed 'gitstatus/' content from commit 6b9ba17
git-subtree-dir: gitstatus
git-subtree-split: 6b9ba179c6655286c4c399e7926d5098dd6bd706
Diffstat (limited to 'src/logging.cc')
-rw-r--r-- | src/logging.cc | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/logging.cc b/src/logging.cc new file mode 100644 index 00000000..fb9ac9ea --- /dev/null +++ b/src/logging.cc @@ -0,0 +1,139 @@ +// 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 <https://www.gnu.org/licenses/>. + +#include "logging.h" + +#include <pthread.h> +#include <time.h> + +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <ctime> +#include <mutex> +#include <string> + +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<std::ostringstream>(); +} + +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<std::mutex> 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<decltype(x), int>::value) { + // XSI-compliant version. + strm << (x ? "unknown error" : buf); + } else if (std::is_same<decltype(x), char*>::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 |