|
#ifndef ONE_LOG_H |
|
#define ONE_LOG_H |
|
#include <chrono> |
|
#include <cstdio> |
|
#include <ctime> |
|
#include <functional> |
|
#include <iomanip> |
|
#include <sstream> |
|
#include <string> |
|
#include <thread> |
|
#include <utility> |
|
#include <vector> |
|
|
|
namespace olog |
|
{ |
|
class stopwatch |
|
{ |
|
using Clock = std::chrono::steady_clock; |
|
typename Clock::time_point mLast; |
|
typename Clock::duration mElapsed; |
|
bool isStart; |
|
|
|
public: |
|
stopwatch(bool start = true) |
|
: mLast(Clock::now()), mElapsed(), isStart(start){}; |
|
void reset(bool start = true) |
|
{ |
|
mLast = Clock::now(); |
|
mElapsed = Clock::duration::zero(); |
|
isStart = start; |
|
} |
|
void start() |
|
{ |
|
mLast = Clock::now(); |
|
isStart = true; |
|
} |
|
void stop() |
|
{ |
|
mElapsed += Clock::now() - mLast; |
|
isStart = false; |
|
} |
|
typename Clock::duration elapsed() const |
|
{ |
|
return isStart ? Clock::now() - mLast + mElapsed : mElapsed; |
|
} |
|
float elapsedMS() const |
|
{ |
|
auto s = |
|
std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed()); |
|
return s.count() / 1000000.f; |
|
} |
|
}; |
|
|
|
using argWrapper = std::function<void(std::ostream &)>; |
|
inline std::string formatImpl(const std::string &fmt, |
|
const std::vector<argWrapper> &wrappers) |
|
{ |
|
std::ostringstream ss; |
|
size_t start = 0; |
|
for (size_t i = 0; i < wrappers.size(); i++) |
|
{ |
|
auto pos = fmt.find("{}", start); |
|
if (pos == std::string::npos) |
|
{ |
|
break; |
|
} |
|
ss << fmt.substr(start, pos - start); |
|
wrappers[i](ss); |
|
start = pos + 2; |
|
} |
|
ss << fmt.substr(start); |
|
return ss.str(); |
|
} |
|
template <typename Arg> |
|
inline argWrapper wrap(const Arg &arg) |
|
{ |
|
return [&arg](std::ostream &ss) -> void |
|
{ |
|
ss << arg; |
|
}; |
|
} |
|
template <typename... Args> |
|
inline std::string format(const std::string &fmt, Args &&... args) |
|
{ |
|
return formatImpl(fmt, {wrap(std::forward<Args>(args))...}); |
|
}; |
|
template <typename... Args> |
|
inline std::string formatf(const std::string &fmt, Args &&... args) |
|
{ |
|
int sz = |
|
std::snprintf(nullptr, 0, fmt.c_str(), std::forward<Args>(args)...); |
|
std::vector<char> buf(sz + 1); |
|
std::snprintf(buf.data(), sz + 1, fmt.c_str(), std::forward<Args>(args)...); |
|
return std::string{buf.data()}; |
|
} |
|
enum class level : int |
|
{ |
|
trace = 0, |
|
debug = 1, |
|
info = 2, |
|
warn = 3, |
|
error = 4, |
|
fatal = 5, |
|
}; |
|
extern const char *levelNames[]; |
|
struct source_loc |
|
{ |
|
const std::string &file; |
|
int line; |
|
}; |
|
class logger |
|
{ |
|
public: |
|
logger(); |
|
void setLevel(level l); |
|
bool filter(level l); |
|
void write(source_loc loc, level l, const std::string &content); |
|
|
|
// c++20 like format |
|
template <typename... Args> |
|
void log(source_loc loc, level l, const std::string &fmt, Args &&... args) |
|
{ |
|
if (filter(l)) |
|
{ |
|
write(loc, l, format(fmt, std::forward<Args>(args)...)); |
|
} |
|
} |
|
|
|
template <typename... Args> |
|
void log(level l, const std::string &fmt, Args &&... args) |
|
{ |
|
log(source_loc{"", 0}, l, fmt, std::forward<Args>(args)...); |
|
} |
|
|
|
template <typename... Args> |
|
void logf(source_loc loc, level l, const std::string &fmt, |
|
Args &&... args) |
|
{ |
|
if (filter(l)) |
|
{ |
|
write(loc, l, formatf(fmt, std::forward<Args>(args)...)); |
|
} |
|
} |
|
|
|
template <typename... Args> |
|
void logf(level l, const std::string &fmt, Args &&... args) |
|
{ |
|
logf(source_loc{"", 0}, l, fmt, std::forward<Args>(args)...); |
|
} |
|
|
|
private: |
|
level mLevel; |
|
}; |
|
|
|
inline logger *default_logger() |
|
{ |
|
static logger *l = new logger(); |
|
return l; |
|
} |
|
|
|
inline std::string prefix(source_loc loc, level l, bool detail) |
|
{ |
|
const char *levelNames[] = { |
|
"Trace", "Debug", "Info", "Warn", "Error", "Fatal", |
|
}; |
|
const char *levelColors[] = { |
|
"\x1B[34m", // blue |
|
"\x1B[36m", // cyan |
|
"\x1B[32m", // green |
|
"\x1B[33m", // yellow |
|
"\x1B[31m", // red |
|
"\x1B[35m", // magenta |
|
}; |
|
|
|
char now[16]; |
|
auto t = std::time({}); |
|
std::strftime(now, sizeof(now), "%H:%M:%S", std::localtime(&t)); |
|
|
|
std::string p; |
|
if (detail) |
|
{ |
|
auto pos = loc.file.find_last_of("/\\"); |
|
p = format( |
|
"{}[{}] [{}] [{}:{}] [thread {}]", levelColors[static_cast<int>(l)], |
|
levelNames[static_cast<int>(l)], now, |
|
(pos == std::string::npos ? loc.file : loc.file.substr(pos + 1)), |
|
loc.line, std::this_thread::get_id()); |
|
} |
|
else |
|
{ |
|
p = format("{}[{}] [{}]", levelColors[static_cast<int>(l)], |
|
levelNames[static_cast<int>(l)], now); |
|
} |
|
return p; |
|
} |
|
|
|
inline logger::logger() : mLevel(level::warn) |
|
{ |
|
#ifndef NDEBUG |
|
mLevel = level::debug; |
|
#endif |
|
auto s = std::getenv("PRNT_LEVEL"); |
|
if (s != nullptr) |
|
{ |
|
mLevel = static_cast<level>(atoi(s)); |
|
} |
|
} |
|
|
|
inline void logger::setLevel(level l) { mLevel = l; } |
|
|
|
inline bool logger::filter(level l) |
|
{ |
|
if (l < mLevel) |
|
{ |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
inline void logger::write(source_loc loc, level l, const std::string &content) |
|
{ |
|
constexpr char resetColor[] = "\033[0m"; |
|
if (l < mLevel) |
|
{ |
|
return; |
|
} |
|
auto p = prefix(loc, l, mLevel < level::info); |
|
printf("%s:\n%s%s\n", p.c_str(), content.c_str(), resetColor); |
|
} |
|
|
|
#define PRINT_LOG(logger, level, ...) \ |
|
(logger)->log(olog::source_loc{__FILE__, __LINE__}, (level), __VA_ARGS__) |
|
#define TRACE(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::trace, __VA_ARGS__) |
|
#define DEBUG(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::debug, __VA_ARGS__) |
|
#define INFO(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::info, __VA_ARGS__) |
|
#define WARN(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::warn, __VA_ARGS__) |
|
#define ERROR(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::error, __VA_ARGS__) |
|
#define FATAL(...) \ |
|
PRINT_LOG(olog::default_logger(), olog::level::fatal, __VA_ARGS__) |
|
|
|
#define PRINT_LOGF(logger, level, ...) \ |
|
(logger)->logf(olog::source_loc{__FILE__, __LINE__}, (level), __VA_ARGS__) |
|
#define TRACEF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::trace, __VA_ARGS__) |
|
#define DEBUGF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::debug, __VA_ARGS__) |
|
#define INFOF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::info, __VA_ARGS__) |
|
#define WARNF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::warn, __VA_ARGS__) |
|
#define ERRORF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::error, __VA_ARGS__) |
|
#define FATALF(...) \ |
|
PRINT_LOGF(olog::default_logger(), olog::level::fatal, __VA_ARGS__) |
|
} |
|
|
|
#endif |