Created
October 23, 2016 21:12
-
-
Save 6502/817e9ec49e17ff9fcc355cec622adf23 to your computer and use it in GitHub Desktop.
A simple logging facility
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
#if !defined(LOG_H_INCLUDED) | |
#define LOG_H_INCLUDED | |
#include <stdio.h> | |
#include <vector> | |
#include <string> | |
#include <deque> | |
#include <map> | |
#include <functional> | |
#include <time.h> | |
#include <string.h> | |
#include <stdarg.h> | |
namespace log { | |
std::string stringf(const char *fmt, ...) { | |
std::vector<char> buffer(256); | |
va_list args, cp; | |
va_start(args, fmt); | |
va_copy(cp, args); | |
int sz = vsnprintf(&buffer[0], buffer.size(), fmt, args); | |
if (sz >= int(buffer.size())) { | |
buffer.resize(sz + 1); | |
vsnprintf(&buffer[0], buffer.size(), fmt, cp); | |
} | |
va_end(cp); | |
va_end(args); | |
return &buffer[0]; | |
} | |
inline std::map<int, std::string>& severities(){ | |
static std::map<int, std::string> s{ | |
{0, "info"}, | |
{100, "warning"}, | |
{200, "error"}, | |
{1000, "fatal error"}}; | |
return s; | |
} | |
inline std::string sevname(int severity) { | |
auto& s = severities(); | |
auto it = s.find(severity); | |
if (it == s.end()) it = s.find(severity/100*100); | |
if (it != s.end()) return it->second; | |
return stringf("severity=%i", severity); | |
} | |
struct Entry { | |
double time; | |
int severity; | |
std::string context; | |
std::string message; | |
}; | |
typedef std::function<std::string(const Entry&)> Formatter; | |
typedef std::function<void(const Entry&)> Logger; | |
typedef std::function<bool(const Entry&)> Filter; | |
inline Formatter default_formatter() { | |
return [](const Entry& e) -> std::string { | |
char ctimebuf[30]; | |
time_t t = e.time; | |
ctime_r(&t, ctimebuf); ctimebuf[strlen(ctimebuf)-1] = '\0'; | |
return stringf("%s - %s: (%s) -- %s", | |
ctimebuf, | |
sevname(e.severity).c_str(), | |
e.context.c_str(), | |
e.message.c_str()); | |
}; | |
} | |
struct MultiLogger { | |
std::vector<Logger> Ls; | |
void operator()(const Entry& e) { | |
for (auto& x : Ls) x(e); | |
} | |
}; | |
struct FilteringLogger { | |
Logger L; | |
Filter f; | |
void operator()(const Entry& e) { | |
if (f(e)) L(e); | |
} | |
}; | |
inline FilteringLogger severityFilter(Logger L, int low, int high=-1) { | |
return FilteringLogger{L, [low, high](const Entry& e) { | |
return e.severity >= low && (high == -1 || e.severity <= high); | |
}}; | |
} | |
struct MemLogger { | |
std::deque<Entry> q; | |
int max_size; | |
MemLogger(int max_size=-1) : max_size(max_size) {} | |
void operator()(const Entry& e) { | |
q.push_back(e); | |
while (max_size != -1 && int(q.size()) > max_size) { | |
q.pop_front(); | |
} | |
} | |
}; | |
struct FileLogger { | |
FILE *file; | |
Formatter formatter; | |
bool autoclose; | |
FileLogger(FILE *file, const Formatter& formatter = default_formatter(), bool autoclose = false) | |
: file(file), formatter(formatter), autoclose(autoclose) | |
{ } | |
~FileLogger() { | |
if (autoclose) fclose(file); | |
} | |
void operator()(const Entry& e) { | |
fprintf(file, "%s\n", formatter(e).c_str()); | |
} | |
}; | |
inline Logger& root() { | |
static Logger L = FileLogger(stderr); | |
return L; | |
} | |
inline double now() { | |
return time(0); | |
} | |
} | |
#define LOGSTRINGIFY(x) #x | |
#define LOGTOSTRING(x) LOGSTRINGIFY(x) | |
#define LOG(severity, ...) \ | |
log::root()(log::Entry{log::now(), \ | |
severity, \ | |
__FILE__ ":" LOGTOSTRING(__LINE__), \ | |
log::stringf(__VA_ARGS__)}) | |
#define INFO(...) LOG( 0, __VA_ARGS__) | |
#define WARNING(...) LOG( 100, __VA_ARGS__) | |
#define ERROR(...) LOG( 200, __VA_ARGS__) | |
#define FATAL(...) LOG(1000, __VA_ARGS__) | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment