Skip to content

Instantly share code, notes, and snippets.

@EAirPeter
Created May 26, 2016 19:09
Show Gist options
  • Save EAirPeter/00049ed2f3b3dea3b42011cec9fc43ab to your computer and use it in GitHub Desktop.
Save EAirPeter/00049ed2f3b3dea3b42011cec9fc43ab to your computer and use it in GitHub Desktop.
#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