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:
parent
81e3f6fd03
commit
978d3d9b66
2 changed files with 124 additions and 72 deletions
48
README.md
48
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue