Skip to content
This repository was archived by the owner on Feb 27, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 96 additions & 5 deletions include/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,36 @@ do { \

namespace Log
{
//
// The following helper functions (that begin with an underscore) are
// logically private and should not be called directly.
//

void _write_var(int level, const char *module, int line_number, const char *fmt, ...);
void _write(int level, const char *module, int line_number, const char *fmt, va_list args);

// Functions that convert a value into a version that is suitable for logging.
// For most types this is just the identity function (i.e. it passes the value
// through) and if this is not an acceptable type we get a compilation error.
//
// The exception is `std::string`, where we specialize the template to convert
// it to a `const char*` by calling c_str(). This is safe because the caller
// of `_loggable` always passes it an lvalue which exists for the duration of
// the log call.
//
// Note that we need template parameters for both the argument and the return
// type so that the specialization for std::string can return a type that's
// different from the argument.
template<class T, class R=T>
R _loggable(T arg) { return arg; }

template<class R=const char*>
const char* _loggable(const std::string& arg) { return arg.c_str(); }

//
// Constants and variables after this point are public.
//

const int ERROR_LEVEL = 0;
const int WARNING_LEVEL = 1;
const int STATUS_LEVEL = 2;
Expand All @@ -61,8 +91,23 @@ namespace Log
}
void setLoggingLevel(int level);
Logger* setLogger(Logger *log);
void write(int level, const char *module, int line_number, const char *fmt, ...);
void _write(int level, const char *module, int line_number, const char *fmt, va_list args);

// write function that is parameterized over all types that may be passed to
// it. This converts these arguments into their loggable form before calling
// the simple variadic `_write_var` function.
template<typename ...Types>
void write(int level, const char *module, int line_number, const char *fmt, Types... args)
{
// C++11 suport parameter packs which are lists of parameters of (possibly)
// heterogeneous types. The expression `_loggable(args)...` expands to
// calling `_loggable` on each argument.
//
// For example if args contains two arguments (arg0 and arg1) this expands
// to `_loggable(arg0), _loggable(arg1)`, preserving the type information
// for arg0 and arg1.
_write_var(level, module, line_number, fmt, _loggable(args)...);
}

void backtrace(const char* fmt, ...);
void backtrace_adv();
void commit();
Expand All @@ -71,13 +116,59 @@ namespace Log
namespace RamRecorder
{
extern bool record_everything;
void _record(int level,
const char* module,
int lineno,
const char* context,
const char* format,
va_list args);

void recordEverything();
void _record(int level, const char* module, int lineno, const char* context, const char* format, va_list args);
void record(int level, const char* module, int lineno, const char* format, ...);
void record_with_context(int level, const char* module, int lineno, const char* context, const char* format, ...);
void reset();
void write(const char* buffer, size_t length);
void dump(const std::string& output_dir);

/// Record methods that work on plain-old-data types (e.g. chars, ints,
/// pointers). The caller is responsible for ensuring that any arguments
/// passed into these functions are valid until the function returns. For
/// example, it is not safe to call `c_str` on a string rvalue and pass the
/// resulting `char*` into one of these functions.

void record_pod(int level,
const char* module,
int lineno,
const char* format,
...);
void record_with_context_pod(int level,
const char* module,
int lineno,
const char* context,
const char* format,
...);

/// Template parameterized record functions. These use the same approach as the
/// Log::write function.

template<typename ...Types>
void record(int level,
const char* module,
int lineno,
const char* format,
Types... args)
{
record_pod(level, module, lineno, format, Log::_loggable(args)...);
}

template<typename ...Types>
void record_with_context(int level,
const char* module,
int lineno,
const char* context,
const char* format,
Types... args)
{
record_with_context_pod(level, module, lineno, context, format, Log::_loggable(args)...);
}
}

#endif
15 changes: 12 additions & 3 deletions src/log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Logger* Log::setLogger(Logger *log)
return old;
}

void Log::write(int level, const char *module, int line_number, const char *fmt, ...)
void Log::_write_var(int level, const char *module, int line_number, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
Expand Down Expand Up @@ -286,15 +286,24 @@ void RamRecorder::_record(int level, const char* module, int lineno, const char*
}
}

void RamRecorder::record(int level, const char* module, int lineno, const char* format, ...)
void RamRecorder::record_pod(int level,
const char* module,
int lineno,
const char* format,
...)
{
va_list args;
va_start(args, format);
_record(level, module, lineno, nullptr, format, args);
va_end(args);
}

void RamRecorder::record_with_context(int level, const char* module, int lineno, const char* context, const char* format, ...)
void RamRecorder::record_with_context_pod(int level,
const char* module,
int lineno,
const char* context,
const char* format,
...)
{
va_list args;
va_start(args, format);
Expand Down