Created
May 26, 2016 19:09
-
-
Save EAirPeter/00049ed2f3b3dea3b42011cec9fc43ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <chrono> // std::chrono | |
#include <cstddef> // std::size_t | |
#include <fstream> // std::ofstream | |
#include <future> // std::{async,future} | |
#include <iomanip> // std::{put_time,setw} | |
#include <iostream> // std::{cout,endl} | |
#include <mutex> // std::{mutex,unique_lock} | |
#include <sstream> // std::ostringstream | |
#include <string> // std::{,to_}string | |
#include <thread> // std::{,this_}thead | |
#include <unordered_map> // std::unordered_map | |
#include <utility> // std::swap | |
#include <vector> // std::vector | |
using TCondVar = std::condition_variable; | |
using TFuture = std::future<void>; | |
using TMutex = std::mutex; | |
using TUniqueLock = std::unique_lock<TMutex>; | |
class Logger { | |
private: | |
using TMapTags = std::unordered_map<std::thread::id, std::string>; | |
using TVecStrs = std::vector<std::string>; | |
private: | |
constexpr static int X_TAGW = 12; | |
static TMapTags x_mTags; | |
static TMutex x_mtxMTags; | |
public: | |
static void ThreadTagSet(const std::string &sTag, | |
const std::thread::id &aId = std::this_thread::get_id()) | |
{ | |
std::unique_lock<std::mutex> ulk(x_mtxMTags); | |
x_mTags[aId] = sTag; | |
} | |
static void ThreadTagErase( | |
const std::thread::id &aId = std::this_thread::get_id()) | |
{ | |
TUniqueLock ulk(x_mtxMTags); | |
x_mTags.erase(aId); | |
} | |
static std::string ThreadTagGet( | |
const std::thread::id &aId = std::this_thread::get_id()) | |
{ | |
TUniqueLock ulk(x_mtxMTags); | |
auto i = x_mTags.find(aId); | |
if (i != x_mTags.end()) | |
return i->second; | |
return "Unknown"; | |
} | |
private: | |
constexpr static std::size_t X_BUFSIZE = 4; | |
public: | |
Logger() = delete; | |
Logger(const Logger &) = delete; | |
Logger(Logger &&) = delete; | |
Logger(const std::string &sFileName) : | |
x_stream(sFileName) | |
{ | |
xx_bufA.reserve(X_BUFSIZE); | |
xx_bufB.reserve(X_BUFSIZE); | |
} | |
~Logger() { | |
if (x_futWrite) { | |
x_futWrite->wait(); | |
delete x_futWrite; | |
} | |
} | |
private: | |
void X_Sync() { | |
for (auto &&s : *x_bufToWrite) { | |
for (auto i = s.begin(); i != s.end(); ++i) | |
if (*i == '\x1b') { | |
if (++i != s.end() && *i == '[') | |
while (i != s.end() && *i != 'm') | |
++i; | |
else | |
x_stream.put(*--i); | |
} | |
else | |
x_stream.put(*i); | |
std::endl(x_stream); | |
} | |
x_bufToWrite->clear(); | |
} | |
void X_LogAsync(const std::string &sMsg) { | |
TUniqueLock ulk(x_mtxBuf); | |
if (x_buf->size() == X_BUFSIZE) { | |
if (x_futWrite) { | |
x_futWrite->wait(); | |
delete x_futWrite; | |
} | |
std::swap(x_buf, x_bufToWrite); | |
x_futWrite = new TFuture(std::move( | |
std::async(std::launch::async, std::bind(&X_Sync, this)))); | |
} | |
x_buf->push_back(sMsg); | |
} | |
private: | |
std::ofstream x_stream; | |
TVecStrs xx_bufA, xx_bufB; | |
TVecStrs * x_buf = &xx_bufA; | |
TVecStrs * x_bufToWrite = &xx_bufB; | |
TMutex x_mtxBuf; | |
TFuture * x_futWrite = nullptr; | |
private: | |
friend class StreamLog; | |
}; | |
Logger::TMapTags Logger::x_mTags; | |
TMutex Logger::x_mtxMTags; | |
enum : unsigned { | |
FATAL, | |
ERROR, | |
WARN, | |
INFO, | |
DEBUG, | |
}; | |
struct LogLevel { | |
const char namLab[16]; | |
const char fmtLab[16]; | |
const char fmtMsg[16]; | |
}; | |
constexpr LogLevel X_LOGLVL[] = { | |
{"FATAL", "45;1", "35;1"}, | |
{"ERROR", "41;1", "31;1"}, | |
{"WARN ", "43;1", "33;1"}, | |
{"INFO ", "42", "37"}, | |
{"DEBUG", "46", "37;1"}, | |
}; | |
TMutex g_mtxCout; | |
class StreamLog { | |
public: | |
StreamLog(Logger &lg, const LogLevel &ll) : x_lg(lg), x_ll(ll) { | |
using std::chrono::system_clock; | |
auto now = system_clock::to_time_t(system_clock::now()); | |
x_oss << "\x1b[0;32m["; | |
x_oss << std::put_time(std::localtime(&now), "%H:%M:%S"); | |
x_oss << "] \x1b[0;37;" << x_ll.fmtLab << "m["; | |
x_oss << std::setw(Logger::X_TAGW) << Logger::ThreadTagGet(); | |
x_oss << "/" << x_ll.namLab << "]\x1b[0;" << x_ll.fmtMsg << "m "; | |
} | |
~StreamLog() { | |
x_oss << "\x1b[0;m"; | |
auto msg = x_oss.str(); | |
x_lg.X_LogAsync(msg); | |
{ | |
TUniqueLock ulk(g_mtxCout); | |
std::cout << msg << std::endl; | |
} | |
} | |
template<class tRhs> | |
StreamLog &operator ,(tRhs &&aRhs) noexcept { | |
try { | |
x_oss << std::forward<tRhs>(aRhs); | |
} | |
catch (...) { | |
} | |
return *this; | |
} | |
private: | |
Logger &x_lg; | |
const LogLevel &x_ll; | |
std::ostringstream x_oss; | |
}; | |
#define LOG_(logger_, level_, ...) \ | |
(static_cast<void>(StreamLog((logger_), (level_)), __VA_ARGS__)) | |
#define LOG_FATAL(logger_, ...) LOG_(logger_, X_LOGLVL[FATAL], __VA_ARGS__) | |
#define LOG_ERROR(logger_, ...) LOG_(logger_, X_LOGLVL[ERROR], __VA_ARGS__) | |
#define LOG_WARN(logger_, ...) LOG_(logger_, X_LOGLVL[WARN ], __VA_ARGS__) | |
#define LOG_INFO(logger_, ...) LOG_(logger_, X_LOGLVL[INFO ], __VA_ARGS__) | |
#define LOG_DEBUG(logger_, ...) LOG_(logger_, X_LOGLVL[DEBUG], __VA_ARGS__) | |
// TEST START | |
#include <random> | |
#include <thread> | |
#include <vector> | |
namespace xx{ | |
int x = 0; | |
Logger log("logging.log"); | |
void cmt(const std::string &name) { | |
Logger::ThreadTagSet(name); | |
using namespace std::chrono; | |
std::mt19937 rng((std::mt19937::result_type)system_clock::now().time_since_epoch().count()); | |
for (int i = 0; i < 10; ++i) | |
LOG_(log, X_LOGLVL[rng() % 5], ++x); | |
Logger::ThreadTagErase(); | |
} | |
} | |
int main() { | |
std::vector<std::thread> v; | |
v.emplace_back(xx::cmt, "Client Main"); | |
v.emplace_back(xx::cmt, "Server Main"); | |
v.emplace_back(xx::cmt, "Observer"); | |
v.emplace_back(xx::cmt, "Executor 0"); | |
v.emplace_back(xx::cmt, "Executor 1"); | |
v.emplace_back(xx::cmt, "Executor 2"); | |
v.emplace_back(xx::cmt, "Executor 3"); | |
v.emplace_back(xx::cmt, "Logging"); | |
v.emplace_back(xx::cmt, "Scheduler"); | |
v.emplace_back(xx::cmt, "Network IO"); | |
for (auto &t : v) | |
t.join(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment