/**
* @file Trace.hh
*
* Author: Peter Helfer
* Date: 2011-05-23
*/
#ifndef TRACE_HH
#define TRACE_HH
#include <string>
#include <unordered_set>
using std::string;
using std::unordered_set;
#include "fmt/format.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
/**
* Trace class
*/
class Trace
{
public:
enum TraceLevel {
TRACE_Flow,
TRACE_Debug3,
TRACE_Debug2,
TRACE_Debug1,
TRACE_Debug,
TRACE_Info1,
TRACE_Info,
TRACE_Warn,
TRACE_Error,
TRACE_Fatal,
TRACE_Maxval
};
static void setTraceLevel(TraceLevel level)
{
traceLevel = level;
}
static bool setTraceLevel(const char *levelString)
{
for (uint i = 0; i < TRACE_Maxval; i++) {
if (strcasecmp(levelString, traceLevelString((TraceLevel) i)) == 0) {
setTraceLevel((TraceLevel) i);
return true;
}
}
fmt::print(stderr, "Unknown trace level '{}'\n", levelString);
return false;
}
static TraceLevel getTraceLevel()
{
return traceLevel;
}
static const char *getTraceLevelString()
{
return traceLevelString(traceLevel);
}
static void setTraceTag(const string &tag)
{
traceTags.insert(tag);
}
static bool isSet(const string &tag)
{
return traceTags.find(tag) != traceTags.end();
}
#ifdef TRACE_ON
#define TRACE(lvl, ...) \
do { \
if (lvl >= Trace::getTraceLevel()) { \
Trace::trace(lvl, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
} \
} while(0);
#define TTRACE(tag, lvl, ...) \
do { \
if (Trace::isSet(tag) && lvl >= Trace::getTraceLevel()) { \
Trace::trace(lvl, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
} \
} while(0);
#define TRACE_FLOW_IS_ON (Trace::TRACE_Flow >= Trace::getTraceLevel())
#define TRACE_DEBUG3_IS_ON (Trace::TRACE_Debug3 >= Trace::getTraceLevel())
#define TRACE_DEBUG2_IS_ON (Trace::TRACE_Debug2 >= Trace::getTraceLevel())
#define TRACE_DEBUG1_IS_ON (Trace::TRACE_Debug1 >= Trace::getTraceLevel())
#define TRACE_DEBUG_IS_ON (Trace::TRACE_Debug >= Trace::getTraceLevel())
#define TRACE_INFO1_IS_ON (Trace::TRACE_Info1 >= Trace::getTraceLevel())
#define TRACE_INFO_IS_ON (Trace::TRACE_Info >= Trace::getTraceLevel())
#define TRACE_WARN_IS_ON (Trace::TRACE_Warn >= Trace::getTraceLevel())
#define TRACE_ERROR_IS_ON (Trace::TRACE_Error >= Trace::getTraceLevel())
#else
#define TRACE(lvl, ...)
#define TTRACE(tag, lvl, ...)
#define TRACE_FLOW_IS_ON false
#define TRACE_DEBUG3_IS_ON false
#define TRACE_DEBUG2_IS_ON false
#define TRACE_DEBUG1_IS_ON false
#define TRACE_DEBUG_IS_ON false
#define TRACE_INFO1_IS_ON false
#define TRACE_INFO_IS_ON false
#define TRACE_WARN_IS_ON false
#define TRACE_ERROR_IS_ON false
#endif
#define TRACE_FLOW(...) TRACE(Trace::TRACE_Flow, __VA_ARGS__)
#define TRACE_DEBUG3(...) TRACE(Trace::TRACE_Debug3, __VA_ARGS__)
#define TRACE_DEBUG2(...) TRACE(Trace::TRACE_Debug2, __VA_ARGS__)
#define TRACE_DEBUG1(...) TRACE(Trace::TRACE_Debug1, __VA_ARGS__)
#define TRACE_DEBUG(...) TRACE(Trace::TRACE_Debug, __VA_ARGS__)
#define TRACE_INFO1(...) TRACE(Trace::TRACE_Info1, __VA_ARGS__)
#define TRACE_INFO(...) TRACE(Trace::TRACE_Info, __VA_ARGS__)
#define TRACE_WARN(...) TRACE(Trace::TRACE_Warn, __VA_ARGS__)
#define TRACE_ERROR(...) TRACE(Trace::TRACE_Error, __VA_ARGS__)
#define TTRACE_FLOW(tag, ...) TTRACE(tag, Trace::TRACE_Flow, __VA_ARGS__)
#define TTRACE_DEBUG3(tag, ...) TTRACE(tag, Trace::TRACE_Debug3, __VA_ARGS__)
#define TTRACE_DEBUG2(tag, ...) TTRACE(tag, Trace::TRACE_Debug2, __VA_ARGS__)
#define TTRACE_DEBUG1(tag, ...) TTRACE(tag, Trace::TRACE_Debug1, __VA_ARGS__)
#define TTRACE_DEBUG(tag, ...) TTRACE(tag, Trace::TRACE_Debug, __VA_ARGS__)
#define TTRACE_INFO1(tag, ...) TTRACE(tag, Trace::TRACE_Info1, __VA_ARGS__)
#define TTRACE_INFO(tag, ...) TTRACE(tag, Trace::TRACE_Info, __VA_ARGS__)
#define TTRACE_WARN(tag, ...) TTRACE(tag, Trace::TRACE_Warn, __VA_ARGS__)
#define TTRACE_ERROR(tag, ...) TTRACE(tag, Trace::TRACE_Error, __VA_ARGS__)
#define TRACE_FATAL(...) \
do { \
Trace::trace(Trace::TRACE_Fatal, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
abort(); \
} while (0)
// TRACE_FATAL will always abort the process, even when compiled without TRACE_ON
#define TTRACE_FLOW_IS_ON(tag) (Trace::isSet(tag) && TRACE_FLOW_IS_ON)
#define TTRACE_DEBUG3_IS_ON(tag) (Trace::isSet(tag) && TRACE_DEBUG3_IS_ON)
#define TTRACE_DEBUG2_IS_ON(tag) (Trace::isSet(tag) && TRACE_DEBUG2_IS_ON)
#define TTRACE_DEBUG1_IS_ON(tag) (Trace::isSet(tag) && TRACE_DEBUG1_IS_ON)
#define TTRACE_DEBUG_IS_ON(tag) (Trace::isSet(tag) && TRACE_DEBUG_IS_ON)
#define TTRACE_INFO1_IS_ON(tag) (Trace::isSet(tag) && TRACE_INFO1_IS_ON)
#define TTRACE_INFO_IS_ON(tag) (Trace::isSet(tag) && TRACE_INFO_IS_ON)
#define TTRACE_WARN_IS_ON(tag) (Trace::isSet(tag) && TRACE_WARN_IS_ON)
#define TTRACE_ERROR_IS_ON(tag) (Trace::isSet(tag) && TRACE_ERROR_IS_ON)
/**
* Abort if a condition is true. Unlike assert(3), ABORT_IF cannot be
* disabled by a compile option like -DNDEBUG.
* @param cond The condition
* @param fmt fmtlib format string for error message
* @param ... Optional parameters values for error message
*/
#define ABORT_IF(cond, ...) \
if (cond) { \
TRACE_DEBUG("Aborting because: {}", #cond); \
TRACE_FATAL( __VA_ARGS__); \
}
#define ABORT_UNLESS(cond, ...) \
ABORT_IF(!(cond), __VA_ARGS__)
#define TRACE_ENTER() do { TRACE_FLOW("-->"); Trace::incrIndent(); } while (0);
#define TRACE_EXIT() do { Trace::decrIndent(); TRACE_FLOW("<--"); } while (0);
#define TRACE_RETURN(x) do { TRACE_EXIT(); return x; } while (0)
template <typename... Args>
static void trace(TraceLevel lvl,
const char *file,
int line,
const char *func,
const char *fmt,
Args ... args)
{
FILE *dest = lvl >= TRACE_Warn ? stderr : stdout;
fmt::print(dest, "{}{} {}[{}] {}(): ",
indentStr(), traceLevelString(lvl), file, line, func);
fmt::vprint(dest, fmt, fmt::make_format_args(args...));
fmt::print(dest, "\n");
}
static void incrIndent()
{
if (indentLevel < MAXINDENT) indentLevel++;
}
static void decrIndent()
{
if (indentLevel > 0) indentLevel--;
}
static const char *traceLevelString(TraceLevel lvl)
{
switch (lvl) {
case TRACE_Flow: return "FLOW";
case TRACE_Debug3: return "DEBUG3";
case TRACE_Debug2: return "DEBUG2";
case TRACE_Debug1: return "DEBUG1";
case TRACE_Debug: return "DEBUG";
case TRACE_Info1: return "INFO1";
case TRACE_Info: return "INFO";
case TRACE_Warn: return "WARN";
case TRACE_Error: return "ERROR";
case TRACE_Fatal: return "FATAL";
default: return "UNKNOWN TRACE LEVEL";
}
}
private:
static TraceLevel traceLevel;
static unordered_set<string> traceTags;
static uint indentLevel;
enum { MAXINDENT = 128 };
static const char *indentStr()
{
if (traceLevel <= TRACE_Flow) {
static char buf[MAXINDENT + 1];
static bool firstTime = true;
if (firstTime) {
for (uint i = 0; i < MAXINDENT; i++) {
buf[i] = ' ';
}
buf[MAXINDENT] = 0;
firstTime = false;
}
return buf + MAXINDENT - indentLevel;
} else {
return "";
}
}
};
#endif // TRACE_HH