feat add the CLUTCHFUNC macro

- Allow to (de)clutch any function call, like asserts.
- fix make max_buffer static const.
- feat make locate and scope_t public.
This commit is contained in:
Johann Dreo 2020-11-01 19:22:17 +01:00
commit 978d3d9b66
2 changed files with 124 additions and 72 deletions

View file

@ -1,5 +1,5 @@
**Clutchlog is a logging system which targets versatile debugging.**
**It allows to (de)clutch messages for a given: log level, source code location or call stack depth.**
***Clutchlog is a logging system which targets versatile debugging.***
***It allows to (de)clutch messages for a given: log level, source code location or call stack depth.***
[TOC]
@ -8,15 +8,22 @@ Features
Clutchlog allows to select which log messages will be displayed, based on their locations:
- *classical log levels*: each message has a given detail level and it is displayed if you ask for a at least the same
- **Classical log levels**: each message has a given detail level and it is displayed if you ask for a at least the same
one.
- *call stack depth*: you can ask to display messages within functions which are called up to a given stack depth.
- *source code location*: you can ask to display messages called from given files, functions and line number, all based on
- **Call stack depth**: you can ask to display messages within functions which are called up to a given stack depth.
- **Source code location**: you can ask to display messages called from given files, functions and line number, all based on
regular expressions.
Additionally, Clutchlog will do its best to allow the compiler to optimize out calls,
for instance debug messages in "Release" builds.
Additional features:
- **Templated log format**, to easily design your own format.
- **Colored log**. By default only important ones are colored (critical and error in red, warning in magenta).
- **Macro to dump the content of a container in a file** with automatic naming (yes, it is useful for fast debugging).
- **Generic clutching wrapper**, to wrap any function call. Useful to (de)clutch *asserts* for example.
Example
=======
@ -291,6 +298,21 @@ log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), CLUTCHLOC, "dumped_
log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), "main.cpp", "main", 122, "dumped.dat", "\n\n");
```
(De)clutch any function call
----------------------------
The `CLUTHFUNC` macro allows to wrap any function within the current logger.
For instance, this can be useful if you want to (de)clutch calls to `assert`s.
To do that, just declare your own macro:
```cpp
#define ASSERT(LEVEL, ...) { CLUTCHFUNC(LEVEL, assert, __VA_ARGS__) }
```
Thus, any call like `ASSERT(error, x > 3);` will be declutchable
with the same configuration than a call to `CLUTCHLOG`.
Log level semantics
===================
@ -319,7 +341,21 @@ are only available for operating systems having the following headers:
Some colors/styles may not be supported by some exotic terminal emulators.
Clutchlog needs `C++-17` with the `filesystem` feature.
You may need to indicate `-std=c++17 -lstdc++fs` to your compiler.
You may need to indicate `-std=c++17 -lstdc++fs` to some compilers.
What Clutchlog do not provide at the moment (but may in a near future):
- Super fast log writing.
- Thread safety.
What Clutchlog will most certainly never provide:
- Round-robin log managers.
- Duplicated messages management.
- External output systems (only allow output stream, you can still do the proxy yourself).
- External error handlers (not my job, come on).
- Automatic argument parser (please, use a dedicated lib).
- Signal handling (WTF would you do that, anyway?).
Build and tests

View file

@ -65,10 +65,12 @@
#endif // CLUTCHDUMP_DEFAULT_SEP
#ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
//! Default mark for stack depth.
#define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
#endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
#ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
//! Default level over which calls to the logger are optimized out when NDEBUG is defined.
#define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
#endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
@ -114,12 +116,26 @@
}
#endif // NDEBUG
//! Call an assert at the given level.
#ifndef NDEBUG
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
auto& logger = clutchlog::logger(); \
clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
if(scope.matches) { \
FUNC(__VA_ARGS__); \
} \
}
#else // not Debug build.
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {/*nothing*/} while(false); }
#endif // NDEBUG
/** @} */
#else // not WITH_CLUTCHLOG
// Disabled macros can still be used in Release builds.
#define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
#define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
#define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {/*nothing*/} while(false); }
#endif // WITH_CLUTCHLOG
/**********************************************************************
@ -344,71 +360,7 @@ class clutchlog
std::regex _in_func;
std::regex _in_line;
//! Structure holding a location matching.
struct scope_t {
bool matches; // everything is compatible
level stage; // current log level
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
size_t depth; // current depth
#endif
bool there; // location is compatible
scope_t() :
matches(false),
stage(level::xdebug),
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
depth(0),
#endif
there(false)
{}
};
//! Gather information on the current location of the call.
scope_t locate(
const level& stage,
const std::string& file,
const std::string& func,
const size_t line
) const
{
scope_t scope; // False scope by default.
/***** Log level stage *****/
// Test stage first, because it's fastest.
scope.stage = stage;
if(not (scope.stage <= _stage)) {
// Bypass useless computations if no match
// because of the stage.
return scope;
}
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
/***** Stack depth *****/
// Backtrace in second, quite fast.
const size_t max_buffer = 4096;
size_t stack_depth;
void *buffer[max_buffer];
stack_depth = backtrace(buffer, max_buffer);
scope.depth = stack_depth;
if(not (scope.depth <= _depth + _strip_calls)) {
// Bypass if no match.
return scope;
}
#endif
/***** Location *****/
// Location last, slowest.
std::ostringstream sline; sline << line;
scope.there =
std::regex_search(file, _in_file)
and std::regex_search(func, _in_func)
and std::regex_search(sline.str(), _in_line);
// No need to retest stage and depth, which are true here.
scope.matches = scope.there;
return scope;
}
static const size_t max_buffer = 4096;
/** @}*/
public:
@ -499,6 +451,70 @@ class clutchlog
/** @name Low-level API
* @{ */
//! Structure holding a location matching.
struct scope_t {
bool matches; // everything is compatible
level stage; // current log level
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
size_t depth; // current depth
#endif
bool there; // location is compatible
scope_t() :
matches(false),
stage(level::xdebug),
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
depth(0),
#endif
there(false)
{}
}; // scope_t
//! Gather information on the current location of the call.
scope_t locate(
const level& stage,
const std::string& file,
const std::string& func,
const size_t line
) const
{
scope_t scope; // False scope by default.
/***** Log level stage *****/
// Test stage first, because it's fastest.
scope.stage = stage;
if(not (scope.stage <= _stage)) {
// Bypass useless computations if no match
// because of the stage.
return scope;
}
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
/***** Stack depth *****/
// Backtrace in second, quite fast.
size_t stack_depth;
void *buffer[max_buffer];
stack_depth = backtrace(buffer, max_buffer);
scope.depth = stack_depth;
if(not (scope.depth <= _depth + _strip_calls)) {
// Bypass if no match.
return scope;
}
#endif
/***** Location *****/
// Location last, slowest.
std::ostringstream sline; sline << line;
scope.there =
std::regex_search(file, _in_file)
and std::regex_search(func, _in_func)
and std::regex_search(sline.str(), _in_line);
// No need to retest stage and depth, which are true here.
scope.matches = scope.there;
return scope;
} // locate
/** Replace `mark` by `tag` in `form`.
*
* @code