Skip to content

Instantly share code, notes, and snippets.

@jo-makar
Created April 19, 2019 14:15
Show Gist options
  • Save jo-makar/3a8e379ae45e256ae33e30739d02487a to your computer and use it in GitHub Desktop.
Save jo-makar/3a8e379ae45e256ae33e30739d02487a to your computer and use it in GitHub Desktop.
Logging framework implemented as an input stream
#include "logstream.hpp"
#include <chrono>
#include <ctime>
#include <mutex>
#include <thread>
using namespace std;
unsigned int thread_id() {
static unsigned int idx = 0;
static mutex mtx;
static map<thread::id, unsigned int> ids;
lock_guard<mutex> lock(mtx);
thread::id id = this_thread::get_id();
auto it = ids.find(id);
if (it != ids.end())
return it->second;
return ids[id] = idx++;
}
ostringstream &Logstream::get(unsigned int level,
const char *func, const char *file, unsigned int line) {
for (auto it = fmt.cbegin(); it != fmt.cend(); it++) {
if (*it == "timestamp") {
// Use chrono::system_clock::now() because time() only provides second-resolution
auto n1 = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
unsigned int ms = n1.time_since_epoch().count() % 1000;
time_t n2 = chrono::system_clock::to_time_t(n1);
struct tm n3;
localtime_r(&n2, &n3);
char s[128];
size_t rv;
rv = strftime(s, sizeof(s)-1, "%Y-%m-%d %H:%M:%S", &n3);
snprintf(s + rv, sizeof(s)-rv, ".%03u", ms);
s[sizeof(s)-1] = 0;
os << s << ":";
}
else if (*it == "thread") {
os << "thread-" << thread_id() << ":";
}
else if (*it == "levelname") {
os << levelname.at(level) << ":";
}
else if (*it == "function") {
os << func << ":";
}
else if (*it == "filename") {
os << file << ":";
}
else if (*it == "fileline") {
os << line << ":";
}
}
return os;
}
const map<unsigned int, string> Logstream::levelname = {{DEBUG, "debug"}, {INFO, "info"}, {WARNING, "warning"}, {ERROR, "error"}};
unsigned int Logstream::loglevel = Logstream::INFO;
// This framework is based on Petru Marginean's design:
// http://www.drdobbs.com/cpp/logging-in-c/201804215
#include <iostream>
#include <list>
#include <map>
#include <sstream>
using namespace std;
#define logstream(level) \
if (Logstream::level > Logstream::loglevel) \
; \
else \
Logstream().get(Logstream::level, __FUNCTION__, __FILE__, __LINE__)
class Logstream {
public:
enum { DEBUG=3, INFO=2, WARNING=1, ERROR=0 };
const static map<unsigned int, string> levelname;
static unsigned int loglevel;
#define FORMAT "timestamp", "thread", "levelname", "function"
Logstream(const list<string> &fmt={FORMAT}) { this->fmt = fmt; }
ostringstream &get(unsigned int level,
const char *func, const char *file, unsigned int line);
~Logstream() {
cerr << os.str() << endl;
}
private:
list<string> fmt;
ostringstream os;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment